From 8344699a0d8ce94584251e5573bdf14736cb13a8 Mon Sep 17 00:00:00 2001 From: Keagan McClelland Date: Wed, 22 May 2024 16:55:41 -0700 Subject: [PATCH 001/218] lntypes: Add Dual[A] primitive type This commit introduces a new type Dual[A] to make it easier to manage symmetric configurations or state for lightning channels. --- lntypes/channel_party.go | 67 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/lntypes/channel_party.go b/lntypes/channel_party.go index be800541bd..5848becee6 100644 --- a/lntypes/channel_party.go +++ b/lntypes/channel_party.go @@ -50,3 +50,70 @@ func (p ChannelParty) IsLocal() bool { func (p ChannelParty) IsRemote() bool { return p == Remote } + +// Dual represents a structure when we are tracking the same parameter for both +// the Local and Remote parties. +type Dual[A any] struct { + // Local is the value tracked for the Local ChannelParty. + Local A + + // Remote is the value tracked for the Remote ChannelParty. + Remote A +} + +// GetForParty gives Dual an access method that takes a ChannelParty as an +// argument. It is included for ergonomics in cases where the ChannelParty is +// in a variable and which party determines how we want to access the Dual. +func (d *Dual[A]) GetForParty(p ChannelParty) A { + switch p { + case Local: + return d.Local + case Remote: + return d.Remote + default: + panic(fmt.Sprintf( + "switch default triggered in ForParty: %v", p, + )) + } +} + +// SetForParty sets the value in the Dual for the given ChannelParty. This +// returns a copy of the original value. +func (d *Dual[A]) SetForParty(p ChannelParty, value A) { + switch p { + case Local: + d.Local = value + case Remote: + d.Remote = value + default: + panic(fmt.Sprintf( + "switch default triggered in ForParty: %v", p, + )) + } +} + +// ModifyForParty applies the function argument to the given ChannelParty field +// and returns a new copy of the Dual. +func (d *Dual[A]) ModifyForParty(p ChannelParty, f func(A) A) A { + switch p { + case Local: + d.Local = f(d.Local) + return d.Local + case Remote: + d.Remote = f(d.Remote) + return d.Remote + default: + panic(fmt.Sprintf( + "switch default triggered in ForParty: %v", p, + )) + } +} + +// MapDual applies the function argument to both the Local and Remote fields of +// the Dual[A] and returns a Dual[B] with that function applied. +func MapDual[A, B any](d Dual[A], f func(A) B) Dual[B] { + return Dual[B]{ + Local: f(d.Local), + Remote: f(d.Remote), + } +} From a888573fcca8dd00b3234bcc60d72d4a6642e7e6 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Fri, 26 Jan 2024 18:48:19 -0800 Subject: [PATCH 002/218] msgmux: add new abstract message router In this commit, we add a new abstract message router. Over time, the goal is that this message router replaces the logic we currently have in the readHandler (the giant switch for each message). With this new abstraction, can reduce the responsibilities of the readHandler to *just* reading messages off the wire and handing them off to the msg router. The readHandler no longer needs to know *where* the messages should go, or how they should be dispatched. This will be used in tandem with the new `protofsm` module in an upcoming PR implementing the new rbf-coop close. --- go.mod | 2 +- go.sum | 4 +- msgmux/log.go | 32 +++++ msgmux/msg_router.go | 274 ++++++++++++++++++++++++++++++++++++++ msgmux/msg_router_test.go | 157 ++++++++++++++++++++++ 5 files changed, 466 insertions(+), 3 deletions(-) create mode 100644 msgmux/log.go create mode 100644 msgmux/msg_router.go create mode 100644 msgmux/msg_router_test.go diff --git a/go.mod b/go.mod index ee656cae08..b1ebeb9e10 100644 --- a/go.mod +++ b/go.mod @@ -35,7 +35,7 @@ require ( github.com/lightningnetwork/lightning-onion v1.2.1-0.20240712235311-98bd56499dfb github.com/lightningnetwork/lnd/cert v1.2.2 github.com/lightningnetwork/lnd/clock v1.1.1 - github.com/lightningnetwork/lnd/fn v1.2.0 + github.com/lightningnetwork/lnd/fn v1.2.1 github.com/lightningnetwork/lnd/healthcheck v1.2.5 github.com/lightningnetwork/lnd/kvdb v1.4.10 github.com/lightningnetwork/lnd/queue v1.1.1 diff --git a/go.sum b/go.sum index cc4ce1bfd9..26f4ec0682 100644 --- a/go.sum +++ b/go.sum @@ -450,8 +450,8 @@ github.com/lightningnetwork/lnd/cert v1.2.2 h1:71YK6hogeJtxSxw2teq3eGeuy4rHGKcFf github.com/lightningnetwork/lnd/cert v1.2.2/go.mod h1:jQmFn/Ez4zhDgq2hnYSw8r35bqGVxViXhX6Cd7HXM6U= github.com/lightningnetwork/lnd/clock v1.1.1 h1:OfR3/zcJd2RhH0RU+zX/77c0ZiOnIMsDIBjgjWdZgA0= github.com/lightningnetwork/lnd/clock v1.1.1/go.mod h1:mGnAhPyjYZQJmebS7aevElXKTFDuO+uNFFfMXK1W8xQ= -github.com/lightningnetwork/lnd/fn v1.2.0 h1:YTb2m8NN5ZiJAskHeBZAmR1AiPY8SXziIYPAX1VI/ZM= -github.com/lightningnetwork/lnd/fn v1.2.0/go.mod h1:SyFohpVrARPKH3XVAJZlXdVe+IwMYc4OMAvrDY32kw0= +github.com/lightningnetwork/lnd/fn v1.2.1 h1:pPsVGrwi9QBwdLJzaEGK33wmiVKOxs/zc8H7+MamFf0= +github.com/lightningnetwork/lnd/fn v1.2.1/go.mod h1:SyFohpVrARPKH3XVAJZlXdVe+IwMYc4OMAvrDY32kw0= github.com/lightningnetwork/lnd/healthcheck v1.2.5 h1:aTJy5xeBpcWgRtW/PGBDe+LMQEmNm/HQewlQx2jt7OA= github.com/lightningnetwork/lnd/healthcheck v1.2.5/go.mod h1:G7Tst2tVvWo7cx6mSBEToQC5L1XOGxzZTPB29g9Rv2I= github.com/lightningnetwork/lnd/kvdb v1.4.10 h1:vK89IVv1oVH9ubQWU+EmoCQFeVRaC8kfmOrqHbY5zoY= diff --git a/msgmux/log.go b/msgmux/log.go new file mode 100644 index 0000000000..63da4791f9 --- /dev/null +++ b/msgmux/log.go @@ -0,0 +1,32 @@ +package msgmux + +import ( + "github.com/btcsuite/btclog" + "github.com/lightningnetwork/lnd/build" +) + +// Subsystem defines the logging code for this subsystem. +const Subsystem = "MSGX" + +// log is a logger that is initialized with no output filters. This +// means the package will not perform any logging by default until the caller +// requests it. +var log btclog.Logger + +// The default amount of logging is none. +func init() { + UseLogger(build.NewSubLogger(Subsystem, nil)) +} + +// DisableLog disables all library log output. Logging output is disabled +// by default until UseLogger is called. +func DisableLog() { + UseLogger(btclog.Disabled) +} + +// UseLogger uses a specified Logger to output package logging info. +// This should be used in preference to SetLogWriter if the caller is also +// using btclog. +func UseLogger(logger btclog.Logger) { + log = logger +} diff --git a/msgmux/msg_router.go b/msgmux/msg_router.go new file mode 100644 index 0000000000..cd69b14a61 --- /dev/null +++ b/msgmux/msg_router.go @@ -0,0 +1,274 @@ +package msgmux + +// For some reason golangci-lint has a false positive on the sort order of the +// imports for the new "maps" package... We need the nolint directive here to +// ignore that. +// +//nolint:gci +import ( + "fmt" + "maps" + "sync" + + "github.com/btcsuite/btcd/btcec/v2" + "github.com/lightningnetwork/lnd/fn" + "github.com/lightningnetwork/lnd/lnwire" +) + +var ( + // ErrDuplicateEndpoint is returned when an endpoint is registered with + // a name that already exists. + ErrDuplicateEndpoint = fmt.Errorf("endpoint already registered") + + // ErrUnableToRouteMsg is returned when a message is unable to be + // routed to any endpoints. + ErrUnableToRouteMsg = fmt.Errorf("unable to route message") +) + +// EndpointName is the name of a given endpoint. This MUST be unique across all +// registered endpoints. +type EndpointName = string + +// PeerMsg is a wire message that includes the public key of the peer that sent +// it. +type PeerMsg struct { + lnwire.Message + + // PeerPub is the public key of the peer that sent this message. + PeerPub btcec.PublicKey +} + +// Endpoint is an interface that represents a message endpoint, or the +// sub-system that will handle processing an incoming wire message. +type Endpoint interface { + // Name returns the name of this endpoint. This MUST be unique across + // all registered endpoints. + Name() EndpointName + + // CanHandle returns true if the target message can be routed to this + // endpoint. + CanHandle(msg PeerMsg) bool + + // SendMessage handles the target message, and returns true if the + // message was able being processed. + SendMessage(msg PeerMsg) bool +} + +// MsgRouter is an interface that represents a message router, which is generic +// sub-system capable of routing any incoming wire message to a set of +// registered endpoints. +type Router interface { + // RegisterEndpoint registers a new endpoint with the router. If a + // duplicate endpoint exists, an error is returned. + RegisterEndpoint(Endpoint) error + + // UnregisterEndpoint unregisters the target endpoint from the router. + UnregisterEndpoint(EndpointName) error + + // RouteMsg attempts to route the target message to a registered + // endpoint. If ANY endpoint could handle the message, then nil is + // returned. Otherwise, ErrUnableToRouteMsg is returned. + RouteMsg(PeerMsg) error + + // Start starts the peer message router. + Start() + + // Stop stops the peer message router. + Stop() +} + +// sendQuery sends a query to the main event loop, and returns the response. +func sendQuery[Q any, R any](sendChan chan fn.Req[Q, R], queryArg Q, + quit chan struct{}) fn.Result[R] { + + query, respChan := fn.NewReq[Q, R](queryArg) + + if !fn.SendOrQuit(sendChan, query, quit) { + return fn.Errf[R]("router shutting down") + } + + return fn.NewResult(fn.RecvResp(respChan, nil, quit)) +} + +// sendQueryErr is a helper function based on sendQuery that can be used when +// the query only needs an error response. +func sendQueryErr[Q any](sendChan chan fn.Req[Q, error], queryArg Q, + quitChan chan struct{}) error { + + return fn.ElimEither( + fn.Iden, fn.Iden, + sendQuery(sendChan, queryArg, quitChan).Either, + ) +} + +// EndpointsMap is a map of all registered endpoints. +type EndpointsMap map[EndpointName]Endpoint + +// MultiMsgRouter is a type of message router that is capable of routing new +// incoming messages, permitting a message to be routed to multiple registered +// endpoints. +type MultiMsgRouter struct { + startOnce sync.Once + stopOnce sync.Once + + // registerChan is the channel that all new endpoints will be sent to. + registerChan chan fn.Req[Endpoint, error] + + // unregisterChan is the channel that all endpoints that are to be + // removed are sent to. + unregisterChan chan fn.Req[EndpointName, error] + + // msgChan is the channel that all messages will be sent to for + // processing. + msgChan chan fn.Req[PeerMsg, error] + + // endpointsQueries is a channel that all queries to the endpoints map + // will be sent to. + endpointQueries chan fn.Req[Endpoint, EndpointsMap] + + wg sync.WaitGroup + quit chan struct{} +} + +// NewMultiMsgRouter creates a new instance of a peer message router. +func NewMultiMsgRouter() *MultiMsgRouter { + return &MultiMsgRouter{ + registerChan: make(chan fn.Req[Endpoint, error]), + unregisterChan: make(chan fn.Req[EndpointName, error]), + msgChan: make(chan fn.Req[PeerMsg, error]), + endpointQueries: make(chan fn.Req[Endpoint, EndpointsMap]), + quit: make(chan struct{}), + } +} + +// Start starts the peer message router. +func (p *MultiMsgRouter) Start() { + log.Infof("Starting Router") + + p.startOnce.Do(func() { + p.wg.Add(1) + go p.msgRouter() + }) +} + +// Stop stops the peer message router. +func (p *MultiMsgRouter) Stop() { + log.Infof("Stopping Router") + + p.stopOnce.Do(func() { + close(p.quit) + p.wg.Wait() + }) +} + +// RegisterEndpoint registers a new endpoint with the router. If a duplicate +// endpoint exists, an error is returned. +func (p *MultiMsgRouter) RegisterEndpoint(endpoint Endpoint) error { + return sendQueryErr(p.registerChan, endpoint, p.quit) +} + +// UnregisterEndpoint unregisters the target endpoint from the router. +func (p *MultiMsgRouter) UnregisterEndpoint(name EndpointName) error { + return sendQueryErr(p.unregisterChan, name, p.quit) +} + +// RouteMsg attempts to route the target message to a registered endpoint. If +// ANY endpoint could handle the message, then nil is returned. +func (p *MultiMsgRouter) RouteMsg(msg PeerMsg) error { + return sendQueryErr(p.msgChan, msg, p.quit) +} + +// Endpoints returns a list of all registered endpoints. +func (p *MultiMsgRouter) endpoints() fn.Result[EndpointsMap] { + return sendQuery(p.endpointQueries, nil, p.quit) +} + +// msgRouter is the main goroutine that handles all incoming messages. +func (p *MultiMsgRouter) msgRouter() { + defer p.wg.Done() + + // endpoints is a map of all registered endpoints. + endpoints := make(map[EndpointName]Endpoint) + + for { + select { + // A new endpoint was just sent in, so we'll add it to our set + // of registered endpoints. + case newEndpointMsg := <-p.registerChan: + endpoint := newEndpointMsg.Request + + log.Infof("MsgRouter: registering new "+ + "Endpoint(%s)", endpoint.Name()) + + // If this endpoint already exists, then we'll return + // an error as we require unique names. + if _, ok := endpoints[endpoint.Name()]; ok { + log.Errorf("MsgRouter: rejecting "+ + "duplicate endpoint: %v", + endpoint.Name()) + + newEndpointMsg.Resolve(ErrDuplicateEndpoint) + + continue + } + + endpoints[endpoint.Name()] = endpoint + + newEndpointMsg.Resolve(nil) + + // A request to unregister an endpoint was just sent in, so + // we'll attempt to remove it. + case endpointName := <-p.unregisterChan: + delete(endpoints, endpointName.Request) + + log.Infof("MsgRouter: unregistering "+ + "Endpoint(%s)", endpointName.Request) + + endpointName.Resolve(nil) + + // A new message was just sent in. We'll attempt to route it to + // all the endpoints that can handle it. + case msgQuery := <-p.msgChan: + msg := msgQuery.Request + + // Loop through all the endpoints and send the message + // to those that can handle it the message. + var couldSend bool + for _, endpoint := range endpoints { + if endpoint.CanHandle(msg) { + log.Tracef("MsgRouter: sending "+ + "msg %T to endpoint %s", msg, + endpoint.Name()) + + sent := endpoint.SendMessage(msg) + couldSend = couldSend || sent + } + } + + var err error + if !couldSend { + log.Tracef("MsgRouter: unable to route "+ + "msg %T", msg) + + err = ErrUnableToRouteMsg + } + + msgQuery.Resolve(err) + + // A query for the endpoint state just came in, we'll send back + // a copy of our current state. + case endpointQuery := <-p.endpointQueries: + endpointsCopy := make(EndpointsMap, len(endpoints)) + maps.Copy(endpointsCopy, endpoints) + + endpointQuery.Resolve(endpointsCopy) + + case <-p.quit: + return + } + } +} + +// A compile time check to ensure MultiMsgRouter implements the MsgRouter +// interface. +var _ Router = (*MultiMsgRouter)(nil) diff --git a/msgmux/msg_router_test.go b/msgmux/msg_router_test.go new file mode 100644 index 0000000000..af8cdedef6 --- /dev/null +++ b/msgmux/msg_router_test.go @@ -0,0 +1,157 @@ +package msgmux + +import ( + "testing" + + "github.com/lightningnetwork/lnd/lnwire" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" +) + +type mockEndpoint struct { + mock.Mock +} + +func (m *mockEndpoint) Name() string { + args := m.Called() + + return args.String(0) +} + +func (m *mockEndpoint) CanHandle(msg PeerMsg) bool { + args := m.Called(msg) + + return args.Bool(0) +} + +func (m *mockEndpoint) SendMessage(msg PeerMsg) bool { + args := m.Called(msg) + + return args.Bool(0) +} + +// TestMessageRouterOperation tests the basic operation of the message router: +// add new endpoints, route to them, remove, them, etc. +func TestMessageRouterOperation(t *testing.T) { + msgRouter := NewMultiMsgRouter() + msgRouter.Start() + defer msgRouter.Stop() + + openChanMsg := PeerMsg{ + Message: &lnwire.OpenChannel{}, + } + commitSigMsg := PeerMsg{ + Message: &lnwire.CommitSig{}, + } + + errorMsg := PeerMsg{ + Message: &lnwire.Error{}, + } + + // For this test, we'll have two endpoints, each with distinct names. + // One endpoint will only handle OpenChannel, while the other will + // handle the CommitSig message. + fundingEndpoint := &mockEndpoint{} + fundingEndpointName := "funding" + fundingEndpoint.On("Name").Return(fundingEndpointName) + fundingEndpoint.On("CanHandle", openChanMsg).Return(true) + fundingEndpoint.On("CanHandle", errorMsg).Return(false) + fundingEndpoint.On("CanHandle", commitSigMsg).Return(false) + fundingEndpoint.On("SendMessage", openChanMsg).Return(true) + + commitEndpoint := &mockEndpoint{} + commitEndpointName := "commit" + commitEndpoint.On("Name").Return(commitEndpointName) + commitEndpoint.On("CanHandle", commitSigMsg).Return(true) + commitEndpoint.On("CanHandle", openChanMsg).Return(false) + commitEndpoint.On("CanHandle", errorMsg).Return(false) + commitEndpoint.On("SendMessage", commitSigMsg).Return(true) + + t.Run("add endpoints", func(t *testing.T) { + // First, we'll add the funding endpoint to the router. + require.NoError(t, msgRouter.RegisterEndpoint(fundingEndpoint)) + + endpoints, err := msgRouter.endpoints().Unpack() + require.NoError(t, err) + + // There should be a single endpoint registered. + require.Len(t, endpoints, 1) + + // The name of the registered endpoint should be "funding". + require.Equal( + t, "funding", endpoints[fundingEndpointName].Name(), + ) + }) + + t.Run("duplicate endpoint reject", func(t *testing.T) { + // Next, we'll attempt to add the funding endpoint again. This + // should return an ErrDuplicateEndpoint error. + require.ErrorIs( + t, msgRouter.RegisterEndpoint(fundingEndpoint), + ErrDuplicateEndpoint, + ) + }) + + t.Run("route to endpoint", func(t *testing.T) { + // Next, we'll add our other endpoint, then attempt to route a + // message. + require.NoError(t, msgRouter.RegisterEndpoint(commitEndpoint)) + + // If we try to route a message none of the endpoints know of, + // we should get an error. + require.ErrorIs( + t, msgRouter.RouteMsg(errorMsg), ErrUnableToRouteMsg, + ) + + fundingEndpoint.AssertCalled(t, "CanHandle", errorMsg) + commitEndpoint.AssertCalled(t, "CanHandle", errorMsg) + + // Next, we'll route the open channel message. Only the + // fundingEndpoint should be used. + require.NoError(t, msgRouter.RouteMsg(openChanMsg)) + + fundingEndpoint.AssertCalled(t, "CanHandle", openChanMsg) + commitEndpoint.AssertCalled(t, "CanHandle", openChanMsg) + + fundingEndpoint.AssertCalled(t, "SendMessage", openChanMsg) + commitEndpoint.AssertNotCalled(t, "SendMessage", openChanMsg) + + // We'll do the same for the commit sig message. + require.NoError(t, msgRouter.RouteMsg(commitSigMsg)) + + fundingEndpoint.AssertCalled(t, "CanHandle", commitSigMsg) + commitEndpoint.AssertCalled(t, "CanHandle", commitSigMsg) + + commitEndpoint.AssertCalled(t, "SendMessage", commitSigMsg) + fundingEndpoint.AssertNotCalled(t, "SendMessage", commitSigMsg) + }) + + t.Run("remove endpoints", func(t *testing.T) { + // Finally, we'll remove both endpoints. + require.NoError( + t, msgRouter.UnregisterEndpoint(fundingEndpointName), + ) + require.NoError( + t, msgRouter.UnregisterEndpoint(commitEndpointName), + ) + + endpoints, err := msgRouter.endpoints().Unpack() + require.NoError(t, err) + + // There should be no endpoints registered. + require.Len(t, endpoints, 0) + + // Trying to route a message should fail. + require.ErrorIs( + t, msgRouter.RouteMsg(openChanMsg), + ErrUnableToRouteMsg, + ) + require.ErrorIs( + t, msgRouter.RouteMsg(commitSigMsg), + ErrUnableToRouteMsg, + ) + }) + + commitEndpoint.AssertExpectations(t) + fundingEndpoint.AssertExpectations(t) +} From e2571880178ebaa7f48a1144d12285485d74a9cc Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Tue, 30 Jan 2024 18:00:11 -0800 Subject: [PATCH 003/218] peer: update readHandler to dispatch to msgRouter if set Over time with this, we should be able to significantly reduce the size of the peer.Brontide struct as we only need all those deps as the peer needs to recognize and handle each incoming wire message itself. --- peer/brontide.go | 37 ++++++++++++++++++++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/peer/brontide.go b/peer/brontide.go index 920a1bcca8..d561948502 100644 --- a/peer/brontide.go +++ b/peer/brontide.go @@ -42,6 +42,7 @@ import ( "github.com/lightningnetwork/lnd/lnwallet/chainfee" "github.com/lightningnetwork/lnd/lnwallet/chancloser" "github.com/lightningnetwork/lnd/lnwire" + "github.com/lightningnetwork/lnd/msgmux" "github.com/lightningnetwork/lnd/netann" "github.com/lightningnetwork/lnd/pool" "github.com/lightningnetwork/lnd/queue" @@ -522,6 +523,10 @@ type Brontide struct { // potentially holding lots of un-consumed events. channelEventClient *subscribe.Client + // msgRouter is an instance of the msgmux.Router which is used to send + // off new wire messages for handing. + msgRouter fn.Option[msgmux.Router] + startReady chan struct{} quit chan struct{} wg sync.WaitGroup @@ -559,6 +564,9 @@ func NewBrontide(cfg Config) *Brontide { startReady: make(chan struct{}), quit: make(chan struct{}), log: build.NewPrefixLog(logPrefix, peerLog), + msgRouter: fn.Some[msgmux.Router]( + msgmux.NewMultiMsgRouter(), + ), } if cfg.Conn != nil && cfg.Conn.RemoteAddr() != nil { @@ -738,6 +746,12 @@ func (p *Brontide) Start() error { return err } + // Register the message router now as we may need to register some + // endpoints while loading the channels below. + p.msgRouter.WhenSome(func(router msgmux.Router) { + router.Start() + }) + msgs, err := p.loadActiveChannels(activeChans) if err != nil { return fmt.Errorf("unable to load channels: %w", err) @@ -913,7 +927,8 @@ func (p *Brontide) loadActiveChannels(chans []*channeldb.OpenChannel) ( p.cfg.Signer, dbChan, p.cfg.SigPool, ) if err != nil { - return nil, err + return nil, fmt.Errorf("unable to create channel "+ + "state machine: %w", err) } chanPoint := dbChan.FundingOutpoint @@ -1368,6 +1383,10 @@ func (p *Brontide) Disconnect(reason error) { p.cfg.Conn.Close() close(p.quit) + + p.msgRouter.WhenSome(func(router msgmux.Router) { + router.Stop() + }) } // String returns the string representation of this peer. @@ -1809,6 +1828,22 @@ out: } } + // If a message router is active, then we'll try to have it + // handle this message. If it can, then we're able to skip the + // rest of the message handling logic. + err = fn.MapOptionZ(p.msgRouter, func(r msgmux.Router) error { + return r.RouteMsg(msgmux.PeerMsg{ + PeerPub: *p.IdentityKey(), + Message: nextMsg, + }) + }) + + // No error occurred, and the message was handled by the + // router. + if err == nil { + continue + } + var ( targetChan lnwire.ChannelID isLinkUpdate bool From 35e48838c3c96fc5ded08adce348ac72002df016 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Thu, 4 Apr 2024 16:13:30 -0700 Subject: [PATCH 004/218] multi: make MsgRouter available in the ImplementationCfg With this commit, we allow the `MsgRouter` to be available in the `ImplementationCfg`. With this, programs outside of lnd itself are able to now hook into the message processing flow to direct handle custom messages, and even normal wire messages. --- config_builder.go | 14 ++++++++++++++ lnd.go | 1 + peer/brontide.go | 15 ++++++++++++--- server.go | 7 ++++++- 4 files changed, 33 insertions(+), 4 deletions(-) diff --git a/config_builder.go b/config_builder.go index bf6274cdf5..bef59b9a04 100644 --- a/config_builder.go +++ b/config_builder.go @@ -33,6 +33,7 @@ import ( "github.com/lightningnetwork/lnd/chainreg" "github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/clock" + "github.com/lightningnetwork/lnd/fn" "github.com/lightningnetwork/lnd/invoices" "github.com/lightningnetwork/lnd/keychain" "github.com/lightningnetwork/lnd/kvdb" @@ -42,6 +43,7 @@ import ( "github.com/lightningnetwork/lnd/lnwallet/btcwallet" "github.com/lightningnetwork/lnd/lnwallet/rpcwallet" "github.com/lightningnetwork/lnd/macaroons" + "github.com/lightningnetwork/lnd/msgmux" "github.com/lightningnetwork/lnd/rpcperms" "github.com/lightningnetwork/lnd/signal" "github.com/lightningnetwork/lnd/sqldb" @@ -118,6 +120,14 @@ type ChainControlBuilder interface { *btcwallet.Config) (*chainreg.ChainControl, func(), error) } +// AuxComponents is a set of auxiliary components that can be used by lnd for +// certain custom channel types. +type AuxComponents struct { + // MsgRouter is an optional message router that if set will be used in + // place of a new blank default message router. + MsgRouter fn.Option[msgmux.Router] +} + // ImplementationCfg is a struct that holds all configuration items for // components that can be implemented outside lnd itself. type ImplementationCfg struct { @@ -144,6 +154,10 @@ type ImplementationCfg struct { // ChainControlBuilder is a type that can provide a custom wallet // implementation. ChainControlBuilder + + // AuxComponents is a set of auxiliary components that can be used by + // lnd for certain custom channel types. + AuxComponents } // DefaultWalletImpl is the default implementation of our normal, btcwallet diff --git a/lnd.go b/lnd.go index 38f5c0d759..e483d5512f 100644 --- a/lnd.go +++ b/lnd.go @@ -600,6 +600,7 @@ func Main(cfg *Config, lisCfg ListenerCfg, implCfg *ImplementationCfg, cfg, cfg.Listeners, dbs, activeChainControl, &idKeyDesc, activeChainControl.Cfg.WalletUnlockParams.ChansToRestore, multiAcceptor, torController, tlsManager, leaderElector, + implCfg, ) if err != nil { return mkErr("unable to create server: %v", err) diff --git a/peer/brontide.go b/peer/brontide.go index d561948502..a6cb4da889 100644 --- a/peer/brontide.go +++ b/peer/brontide.go @@ -387,6 +387,11 @@ type Config struct { // This value will be passed to created links. MaxFeeExposure lnwire.MilliSatoshi + // MsgRouter is an optional instance of the main message router that + // the peer will use. If None, then a new default version will be used + // in place. + MsgRouter fn.Option[msgmux.Router] + // Quit is the server's quit channel. If this is closed, we halt operation. Quit chan struct{} } @@ -542,6 +547,12 @@ var _ lnpeer.Peer = (*Brontide)(nil) func NewBrontide(cfg Config) *Brontide { logPrefix := fmt.Sprintf("Peer(%x):", cfg.PubKeyBytes) + // We'll either use the msg router instance passed in, or create a new + // blank instance. + msgRouter := cfg.MsgRouter.Alt(fn.Some[msgmux.Router]( + msgmux.NewMultiMsgRouter(), + )) + p := &Brontide{ cfg: cfg, activeSignal: make(chan struct{}), @@ -564,9 +575,7 @@ func NewBrontide(cfg Config) *Brontide { startReady: make(chan struct{}), quit: make(chan struct{}), log: build.NewPrefixLog(logPrefix, peerLog), - msgRouter: fn.Some[msgmux.Router]( - msgmux.NewMultiMsgRouter(), - ), + msgRouter: msgRouter, } if cfg.Conn != nil && cfg.Conn.RemoteAddr() != nil { diff --git a/server.go b/server.go index 33f1098484..69f19d9e0b 100644 --- a/server.go +++ b/server.go @@ -160,6 +160,8 @@ type server struct { cfg *Config + implCfg *ImplementationCfg + // identityECDH is an ECDH capable wrapper for the private key used // to authenticate any incoming connections. identityECDH keychain.SingleKeyECDH @@ -486,7 +488,8 @@ func newServer(cfg *Config, listenAddrs []net.Addr, chansToRestore walletunlocker.ChannelsToRecover, chanPredicate chanacceptor.ChannelAcceptor, torController *tor.Controller, tlsManager *TLSManager, - leaderElector cluster.LeaderElector) (*server, error) { + leaderElector cluster.LeaderElector, + implCfg *ImplementationCfg) (*server, error) { var ( err error @@ -571,6 +574,7 @@ func newServer(cfg *Config, listenAddrs []net.Addr, s := &server{ cfg: cfg, + implCfg: implCfg, graphDB: dbs.GraphDB.ChannelGraph(), chanStateDB: dbs.ChanStateDB.ChannelStateDB(), addrSource: dbs.ChanStateDB, @@ -4039,6 +4043,7 @@ func (s *server) peerConnected(conn net.Conn, connReq *connmgr.ConnReq, DisallowRouteBlinding: s.cfg.ProtocolOptions.NoRouteBlinding(), MaxFeeExposure: thresholdMSats, Quit: s.quit, + MsgRouter: s.implCfg.MsgRouter, } copy(pCfg.PubKeyBytes[:], peerAddr.IdentityKey.SerializeCompressed()) From 8eae1eec4aee3e6f7f990536f27e45f45ff25bd1 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Tue, 9 Jul 2024 14:05:33 -0700 Subject: [PATCH 005/218] peer: don't stop global msg router In this commit, we fix a bug that would cause a global message router to be stopped anytime a peer disconnected. The global msg router only allows `Start` to be called once, so afterwards, no messages would properly be routed. --- peer/brontide.go | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/peer/brontide.go b/peer/brontide.go index a6cb4da889..25c7cea6f2 100644 --- a/peer/brontide.go +++ b/peer/brontide.go @@ -532,6 +532,11 @@ type Brontide struct { // off new wire messages for handing. msgRouter fn.Option[msgmux.Router] + // globalMsgRouter is a flag that indicates whether we have a global + // msg router. If so, then we don't worry about stopping the msg router + // when a peer disconnects. + globalMsgRouter bool + startReady chan struct{} quit chan struct{} wg sync.WaitGroup @@ -547,6 +552,11 @@ var _ lnpeer.Peer = (*Brontide)(nil) func NewBrontide(cfg Config) *Brontide { logPrefix := fmt.Sprintf("Peer(%x):", cfg.PubKeyBytes) + // We have a global message router if one was passed in via the config. + // In this case, we don't need to attempt to tear it down when the peer + // is stopped. + globalMsgRouter := cfg.MsgRouter.IsSome() + // We'll either use the msg router instance passed in, or create a new // blank instance. msgRouter := cfg.MsgRouter.Alt(fn.Some[msgmux.Router]( @@ -576,6 +586,7 @@ func NewBrontide(cfg Config) *Brontide { quit: make(chan struct{}), log: build.NewPrefixLog(logPrefix, peerLog), msgRouter: msgRouter, + globalMsgRouter: globalMsgRouter, } if cfg.Conn != nil && cfg.Conn.RemoteAddr() != nil { @@ -1393,9 +1404,13 @@ func (p *Brontide) Disconnect(reason error) { close(p.quit) - p.msgRouter.WhenSome(func(router msgmux.Router) { - router.Stop() - }) + // If our msg router isn't global (local to this instance), then we'll + // stop it. Otherwise, we'll leave it running. + if !p.globalMsgRouter { + p.msgRouter.WhenSome(func(router msgmux.Router) { + router.Stop() + }) + } } // String returns the string representation of this peer. From e735f77d436a6faa04170af9ad5a85223d2f60aa Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Wed, 14 Aug 2024 11:38:28 -0700 Subject: [PATCH 006/218] build: set min build version to Go 1.22.6 Go 1.23 was released this week, so with this PR we update the build system to officially support the last two releases. --- .github/workflows/main.yml | 2 +- .github/workflows/release.yaml | 3 +-- .golangci.yml | 2 +- Dockerfile | 2 +- Makefile | 2 +- dev.Dockerfile | 2 +- docker/btcd/Dockerfile | 2 +- docs/INSTALL.md | 6 +++--- go.mod | 2 +- lnrpc/Dockerfile | 2 +- lnrpc/gen_protos_docker.sh | 2 +- make/builder.Dockerfile | 2 +- 12 files changed, 14 insertions(+), 15 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 2dc0ad163b..fe4a64d66a 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -31,7 +31,7 @@ env: # /dev.Dockerfile # /make/builder.Dockerfile # /.github/workflows/release.yml - GO_VERSION: 1.22.5 + GO_VERSION: 1.22.6 jobs: ######################## diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 702734cf8f..d7e932e158 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -11,12 +11,11 @@ defaults: env: # If you change this value, please change it in the following files as well: - # /.travis.yml # /Dockerfile # /dev.Dockerfile # /make/builder.Dockerfile # /.github/workflows/main.yml - GO_VERSION: 1.22.5 + GO_VERSION: 1.22.6 jobs: main: diff --git a/.golangci.yml b/.golangci.yml index f3de207048..3b19be23e2 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -57,7 +57,7 @@ linters-settings: - G306 # Poor file permissions used when writing to a new file. staticcheck: - go: "1.22.5" + go: "1.22.6" checks: ["-SA1019"] lll: diff --git a/Dockerfile b/Dockerfile index 683eff81a9..3a6f642b1e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -3,7 +3,7 @@ # /make/builder.Dockerfile # /.github/workflows/main.yml # /.github/workflows/release.yml -FROM golang:1.22.5-alpine as builder +FROM golang:1.22.6-alpine as builder # Force Go to use the cgo based DNS resolver. This is required to ensure DNS # queries required to connect to linked containers succeed. diff --git a/Makefile b/Makefile index d7e726df1f..5568b0b20a 100644 --- a/Makefile +++ b/Makefile @@ -35,7 +35,7 @@ endif # GO_VERSION is the Go version used for the release build, docker files, and # GitHub Actions. This is the reference version for the project. All other Go # versions are checked against this version. -GO_VERSION = 1.22.5 +GO_VERSION = 1.22.6 GOBUILD := $(LOOPVARFIX) go build -v GOINSTALL := $(LOOPVARFIX) go install -v diff --git a/dev.Dockerfile b/dev.Dockerfile index c0d4572bb4..945e6a5b5d 100644 --- a/dev.Dockerfile +++ b/dev.Dockerfile @@ -3,7 +3,7 @@ # /make/builder.Dockerfile # /.github/workflows/main.yml # /.github/workflows/release.yml -FROM golang:1.22.5-alpine as builder +FROM golang:1.22.6-alpine as builder LABEL maintainer="Olaoluwa Osuntokun " diff --git a/docker/btcd/Dockerfile b/docker/btcd/Dockerfile index 0a54540c8e..0bb29c898d 100644 --- a/docker/btcd/Dockerfile +++ b/docker/btcd/Dockerfile @@ -1,4 +1,4 @@ -FROM golang:1.22.5-alpine as builder +FROM golang:1.22.6-alpine as builder LABEL maintainer="Olaoluwa Osuntokun " diff --git a/docs/INSTALL.md b/docs/INSTALL.md index 3090835f3d..3b7f7cf4b2 100644 --- a/docs/INSTALL.md +++ b/docs/INSTALL.md @@ -100,12 +100,12 @@ the following commands for your OS: Linux (x86-64) ``` - wget https://dl.google.com/go/go1.22.5.linux-amd64.tar.gz + wget https://dl.google.com/go/go1.22.6.linux-amd64.tar.gz sha256sum go1.22.5.linux-amd64.tar.gz | awk -F " " '{ print $1 }' ``` The final output of the command above should be - `904b924d435eaea086515bc63235b192ea441bd8c9b198c507e85009e6e4c7f0`. If it + `999805bed7d9039ec3da1a53bfbcafc13e367da52aa823cb60b68ba22d44c616`. If it isn't, then the target REPO HAS BEEN MODIFIED, and you shouldn't install this version of Go. If it matches, then proceed to install Go: ``` @@ -123,7 +123,7 @@ the following commands for your OS: ``` The final output of the command above should be - `8c4587cf3e63c9aefbcafa92818c4d9d51683af93ea687bf6c7508d6fa36f85e`. If it + `b566484fe89a54c525dd1a4cbfec903c1f6e8f0b7b3dbaf94c79bc9145391083`. If it isn't, then the target REPO HAS BEEN MODIFIED, and you shouldn't install this version of Go. If it matches, then proceed to install Go: ``` diff --git a/go.mod b/go.mod index b1ebeb9e10..a9c43b9bb5 100644 --- a/go.mod +++ b/go.mod @@ -209,6 +209,6 @@ replace github.com/lightningnetwork/lnd/sqldb => ./sqldb // If you change this please also update .github/pull_request_template.md, // docs/INSTALL.md and GO_IMAGE in lnrpc/gen_protos_docker.sh. -go 1.21.4 +go 1.22.6 retract v0.0.2 diff --git a/lnrpc/Dockerfile b/lnrpc/Dockerfile index 0f74d5e360..253e54f16a 100644 --- a/lnrpc/Dockerfile +++ b/lnrpc/Dockerfile @@ -1,4 +1,4 @@ -FROM golang:1.22.5-bookworm +FROM golang:1.22.6-bookworm RUN apt-get update && apt-get install -y \ git \ diff --git a/lnrpc/gen_protos_docker.sh b/lnrpc/gen_protos_docker.sh index edf97e06b9..1472f5c5fa 100755 --- a/lnrpc/gen_protos_docker.sh +++ b/lnrpc/gen_protos_docker.sh @@ -6,7 +6,7 @@ set -e DIR="$(cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd)" # golang docker image version used in this script. -GO_IMAGE=docker.io/library/golang:1.21.4-alpine +GO_IMAGE=docker.io/library/golang:1.22.6-alpine PROTOBUF_VERSION=$(docker run --rm -v $DIR/../:/lnd -w /lnd $GO_IMAGE \ go list -f '{{.Version}}' -m google.golang.org/protobuf) diff --git a/make/builder.Dockerfile b/make/builder.Dockerfile index db227983a1..86762006d4 100644 --- a/make/builder.Dockerfile +++ b/make/builder.Dockerfile @@ -3,7 +3,7 @@ # /dev.Dockerfile # /.github/workflows/main.yml # /.github/workflows/release.yml -FROM golang:1.22.5-bookworm +FROM golang:1.22.6-bookworm MAINTAINER Olaoluwa Osuntokun From 7502bf3ad98866ad932b6cf5f49dd2cb5de33176 Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Tue, 20 Aug 2024 12:46:03 +0200 Subject: [PATCH 007/218] .golangci: remove or rename old and deprecated linters --- .golangci.yml | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/.golangci.yml b/.golangci.yml index 3b19be23e2..d3b25ef40c 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -133,25 +133,15 @@ linters: - gochecknoinits # Deprecated linters. See https://golangci-lint.run/usage/linters/. - - interfacer - - golint - - maligned - - scopelint - - exhaustivestruct - bodyclose - contextcheck - nilerr - noctx - rowserrcheck - sqlclosecheck - - structcheck - tparallel - unparam - wastedassign - - ifshort - - varcheck - - deadcode - - nosnakecase # Disable gofumpt as it has weird behavior regarding formatting multiple @@ -191,7 +181,7 @@ linters: - wrapcheck # Allow dynamic errors. - - goerr113 + - err113 # We use ErrXXX instead. - errname From 03493d491b1e0060ae75785b9640d8bcfeba0c90 Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Tue, 20 Aug 2024 12:46:24 +0200 Subject: [PATCH 008/218] multi: update linter, fix new issues --- .golangci.yml | 20 +- channeldb/graph_test.go | 2 +- contractcourt/channel_arbitrator.go | 8 +- contractcourt/mock_htlcnotifier_test.go | 3 +- htlcswitch/link.go | 90 ++-- htlcswitch/link_test.go | 7 +- htlcswitch/mock.go | 15 +- invoices/invoice_expiry_watcher.go | 11 +- invoices/invoiceregistry.go | 8 +- itest/lnd_channel_funding_fund_max_test.go | 4 +- ...lnd_channel_funding_utxo_selection_test.go | 5 +- lntest/node/harness_node.go | 4 +- lnwallet/btcwallet/btcwallet.go | 1 - lnwallet/chanfunding/coin_select_test.go | 2 +- msgmux/msg_router.go | 5 - tools/Dockerfile | 2 +- tools/go.mod | 199 +++---- tools/go.sum | 502 +++++++++--------- watchtower/wtclient/queue.go | 2 +- zpay32/decode.go | 2 +- 20 files changed, 452 insertions(+), 440 deletions(-) diff --git a/.golangci.yml b/.golangci.yml index d3b25ef40c..dd68626157 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -197,12 +197,26 @@ linters: # The linter is too aggressive and doesn't add much value since reviewers # will also catch magic numbers that make sense to extract. - gomnd + - mnd - # Some of the tests cannot be parallelized. On the other hand, we don't - # gain much performance with this check so we disable it for now until - # unit tests become our CI bottleneck. + # Some of the tests cannot be parallelized. On the other hand, we don't + # gain much performance with this check so we disable it for now until + # unit tests become our CI bottleneck. - paralleltest + # New linters that we haven't had time to address yet. + - testifylint + - perfsprint + - inamedparam + - copyloopvar + - tagalign + - protogetter + - revive + - depguard + - gosmopolitan + - intrange + + issues: # Only show newly introduced problems. new-from-rev: 8c66353e4c02329abdacb5a8df29998035ec2e24 diff --git a/channeldb/graph_test.go b/channeldb/graph_test.go index f45bc307b2..89197a0a80 100644 --- a/channeldb/graph_test.go +++ b/channeldb/graph_test.go @@ -2382,7 +2382,7 @@ func TestStressTestChannelGraphAPI(t *testing.T) { methodsMu.Unlock() err := fn() - require.NoErrorf(t, err, fmt.Sprintf(name)) + require.NoErrorf(t, err, name) } }) } diff --git a/contractcourt/channel_arbitrator.go b/contractcourt/channel_arbitrator.go index cb5cee8720..4ddb52f645 100644 --- a/contractcourt/channel_arbitrator.go +++ b/contractcourt/channel_arbitrator.go @@ -1982,9 +1982,11 @@ func (c *ChannelArbitrator) isPreimageAvailable(hash lntypes.Hash) (bool, // have the incoming contest resolver decide that we don't want to // settle this invoice. invoice, err := c.cfg.Registry.LookupInvoice(context.Background(), hash) - switch err { - case nil: - case invoices.ErrInvoiceNotFound, invoices.ErrNoInvoicesCreated: + switch { + case err == nil: + case errors.Is(err, invoices.ErrInvoiceNotFound) || + errors.Is(err, invoices.ErrNoInvoicesCreated): + return false, nil default: return false, err diff --git a/contractcourt/mock_htlcnotifier_test.go b/contractcourt/mock_htlcnotifier_test.go index 4f7aadbab3..52bd18676a 100644 --- a/contractcourt/mock_htlcnotifier_test.go +++ b/contractcourt/mock_htlcnotifier_test.go @@ -10,5 +10,6 @@ type mockHTLCNotifier struct { } func (m *mockHTLCNotifier) NotifyFinalHtlcEvent(key models.CircuitKey, - info channeldb.FinalHtlcInfo) { //nolint:whitespace + info channeldb.FinalHtlcInfo) { + } diff --git a/htlcswitch/link.go b/htlcswitch/link.go index 337ce636cd..ff140352c0 100644 --- a/htlcswitch/link.go +++ b/htlcswitch/link.go @@ -1154,10 +1154,11 @@ func (l *channelLink) htlcManager() { // be updated when the close transaction is // ready to avoid that we go down before // storing the transaction in the db. - l.fail( + l.failf( + //nolint:lll LinkFailureError{ code: ErrSyncError, - FailureAction: LinkFailureForceClose, //nolint:lll + FailureAction: LinkFailureForceClose, }, "unable to synchronize channel "+ "states: %v", err, @@ -1195,7 +1196,7 @@ func (l *channelLink) htlcManager() { default: } - l.fail( + l.failf( LinkFailureError{ code: ErrRecoveryError, FailureAction: LinkFailureForceNone, @@ -1259,14 +1260,14 @@ func (l *channelLink) htlcManager() { // If the duplicate keystone error was encountered, we'll fail // without sending an Error message to the peer. case ErrDuplicateKeystone: - l.fail(LinkFailureError{code: ErrCircuitError}, + l.failf(LinkFailureError{code: ErrCircuitError}, "temporary circuit error: %v", err) return // A non-nil error was encountered, send an Error message to // the peer. default: - l.fail(LinkFailureError{code: ErrInternalError}, + l.failf(LinkFailureError{code: ErrInternalError}, "unable to resolve fwd pkgs: %v", err) return } @@ -1405,7 +1406,7 @@ func (l *channelLink) htlcManager() { } case <-l.cfg.PendingCommitTicker.Ticks(): - l.fail( + l.failf( LinkFailureError{ code: ErrRemoteUnresponsive, FailureAction: LinkFailureDisconnect, @@ -1438,19 +1439,19 @@ func (l *channelLink) htlcManager() { // If the duplicate keystone error was encountered, // fail back gracefully. case ErrDuplicateKeystone: - l.fail(LinkFailureError{code: ErrCircuitError}, - fmt.Sprintf("process hodl queue: "+ - "temporary circuit error: %v", - err, - ), + l.failf(LinkFailureError{ + code: ErrCircuitError, + }, "process hodl queue: "+ + "temporary circuit error: %v", + err, ) // Send an Error message to the peer. default: - l.fail(LinkFailureError{code: ErrInternalError}, - fmt.Sprintf("process hodl queue: "+ - "unable to update commitment:"+ - " %v", err), + l.failf(LinkFailureError{ + code: ErrInternalError, + }, "process hodl queue: unable to update "+ + "commitment: %v", err, ) } @@ -1960,7 +1961,7 @@ func (l *channelLink) handleUpstreamMsg(msg lnwire.Message) { // handle message ordering due to concurrency choices. // An issue has been filed to address this here: // https://github.com/lightningnetwork/lnd/issues/8393 - l.fail( + l.failf( LinkFailureError{ code: ErrInvalidUpdate, FailureAction: LinkFailureDisconnect, @@ -1979,7 +1980,7 @@ func (l *channelLink) handleUpstreamMsg(msg lnwire.Message) { // where we are a relaying node (as the blinding point will // be in the payload when we're the introduction node). if msg.BlindingPoint.IsSome() && l.cfg.DisallowRouteBlinding { - l.fail(LinkFailureError{code: ErrInvalidUpdate}, + l.failf(LinkFailureError{code: ErrInvalidUpdate}, "blinding point included when route blinding "+ "is disabled") @@ -1991,7 +1992,7 @@ func (l *channelLink) handleUpstreamMsg(msg lnwire.Message) { // without sending a revoke. This would mean that the switch // check would only occur later. if l.isOverexposedWithHtlc(msg, true) { - l.fail(LinkFailureError{code: ErrInternalError}, + l.failf(LinkFailureError{code: ErrInternalError}, "peer sent us an HTLC that exceeded our max "+ "fee exposure") @@ -2003,7 +2004,7 @@ func (l *channelLink) handleUpstreamMsg(msg lnwire.Message) { // "settle" list in the event that we know the preimage. index, err := l.channel.ReceiveHTLC(msg) if err != nil { - l.fail(LinkFailureError{code: ErrInvalidUpdate}, + l.failf(LinkFailureError{code: ErrInvalidUpdate}, "unable to handle upstream add HTLC: %v", err) return } @@ -2029,7 +2030,7 @@ func (l *channelLink) handleUpstreamMsg(msg lnwire.Message) { } if !lockedin { - l.fail( + l.failf( LinkFailureError{code: ErrInvalidUpdate}, "unable to handle upstream settle", ) @@ -2037,7 +2038,7 @@ func (l *channelLink) handleUpstreamMsg(msg lnwire.Message) { } if err := l.channel.ReceiveHTLCSettle(pre, idx); err != nil { - l.fail( + l.failf( LinkFailureError{ code: ErrInvalidUpdate, FailureAction: LinkFailureForceClose, @@ -2126,7 +2127,7 @@ func (l *channelLink) handleUpstreamMsg(msg lnwire.Message) { // message to the usual HTLC fail message. err := l.channel.ReceiveFailHTLC(msg.ID, b.Bytes()) if err != nil { - l.fail(LinkFailureError{code: ErrInvalidUpdate}, + l.failf(LinkFailureError{code: ErrInvalidUpdate}, "unable to handle upstream fail HTLC: %v", err) return } @@ -2164,7 +2165,7 @@ func (l *channelLink) handleUpstreamMsg(msg lnwire.Message) { idx := msg.ID err := l.channel.ReceiveFailHTLC(idx, msg.Reason[:]) if err != nil { - l.fail(LinkFailureError{code: ErrInvalidUpdate}, + l.failf(LinkFailureError{code: ErrInvalidUpdate}, "unable to handle upstream fail HTLC: %v", err) return } @@ -2183,7 +2184,7 @@ func (l *channelLink) handleUpstreamMsg(msg lnwire.Message) { l.uncommittedPreimages..., ) if err != nil { - l.fail( + l.failf( LinkFailureError{code: ErrInternalError}, "unable to add preimages=%v to cache: %v", l.uncommittedPreimages, err, @@ -2219,7 +2220,7 @@ func (l *channelLink) handleUpstreamMsg(msg lnwire.Message) { case *lnwallet.InvalidHtlcSigError: sendData = []byte(err.Error()) } - l.fail( + l.failf( LinkFailureError{ code: ErrInvalidCommitment, FailureAction: LinkFailureForceClose, @@ -2248,7 +2249,7 @@ func (l *channelLink) handleUpstreamMsg(msg lnwire.Message) { // NOTE: We do not trigger a force close because this // could resolve itself in case our db was just busy // not accepting new transactions. - l.fail( + l.failf( LinkFailureError{ code: ErrInternalError, Warning: true, @@ -2336,7 +2337,7 @@ func (l *channelLink) handleUpstreamMsg(msg lnwire.Message) { ReceiveRevocation(msg) if err != nil { // TODO(halseth): force close? - l.fail( + l.failf( LinkFailureError{ code: ErrInvalidRevocation, FailureAction: LinkFailureDisconnect, @@ -2376,9 +2377,9 @@ func (l *channelLink) handleUpstreamMsg(msg lnwire.Message) { &chanID, state.RemoteCommitment.CommitHeight-1, ) if err != nil { - l.fail(LinkFailureError{code: ErrInternalError}, - "unable to queue breach backup: %v", - err) + l.failf(LinkFailureError{ + code: ErrInternalError, + }, "unable to queue breach backup: %v", err) return } } @@ -2425,7 +2426,7 @@ func (l *channelLink) handleUpstreamMsg(msg lnwire.Message) { // indicates something is wrong with our channel state. l.log.Errorf("Unable to determine if fee threshold " + "exceeded") - l.fail(LinkFailureError{code: ErrInternalError}, + l.failf(LinkFailureError{code: ErrInternalError}, "error calculating fee exposure: %v", err) return @@ -2434,7 +2435,7 @@ func (l *channelLink) handleUpstreamMsg(msg lnwire.Message) { if isDust { // The proposed fee-rate makes us exceed the fee // threshold. - l.fail(LinkFailureError{code: ErrInternalError}, + l.failf(LinkFailureError{code: ErrInternalError}, "fee threshold exceeded: %v", err) return } @@ -2442,7 +2443,7 @@ func (l *channelLink) handleUpstreamMsg(msg lnwire.Message) { // We received fee update from peer. If we are the initiator we // will fail the channel, if not we will apply the update. if err := l.channel.ReceiveUpdateFee(fee); err != nil { - l.fail(LinkFailureError{code: ErrInvalidUpdate}, + l.failf(LinkFailureError{code: ErrInvalidUpdate}, "error receiving fee update: %v", err) return } @@ -2461,7 +2462,7 @@ func (l *channelLink) handleUpstreamMsg(msg lnwire.Message) { // Error received from remote, MUST fail channel, but should // only print the contents of the error message if all // characters are printable ASCII. - l.fail( + l.failf( LinkFailureError{ code: ErrRemoteError, @@ -2550,14 +2551,14 @@ func (l *channelLink) updateCommitTxOrFail() bool { // A duplicate keystone error should be resolved and is not fatal, so // we won't send an Error message to the peer. case ErrDuplicateKeystone: - l.fail(LinkFailureError{code: ErrCircuitError}, + l.failf(LinkFailureError{code: ErrCircuitError}, "temporary circuit error: %v", err) return false // Any other error is treated results in an Error message being sent to // the peer. default: - l.fail(LinkFailureError{code: ErrInternalError}, + l.failf(LinkFailureError{code: ErrInternalError}, "unable to update commitment: %v", err) return false } @@ -3440,7 +3441,7 @@ func (l *channelLink) processRemoteAdds(fwdPkg *channeldb.FwdPkg, fwdPkg.ID(), decodeReqs, ) if sphinxErr != nil { - l.fail(LinkFailureError{code: ErrInternalError}, + l.failf(LinkFailureError{code: ErrInternalError}, "unable to decode hop iterators: %v", sphinxErr) return } @@ -3599,9 +3600,9 @@ func (l *channelLink) processRemoteAdds(fwdPkg *channeldb.FwdPkg, pd, obfuscator, fwdInfo, heightNow, pld, ) if err != nil { - l.fail(LinkFailureError{code: ErrInternalError}, - err.Error(), - ) + l.failf(LinkFailureError{ + code: ErrInternalError, + }, err.Error()) //nolint return } @@ -3745,7 +3746,7 @@ func (l *channelLink) processRemoteAdds(fwdPkg *channeldb.FwdPkg, if fwdPkg.State == channeldb.FwdStateLockedIn { err := l.channel.SetFwdFilter(fwdPkg.Height, fwdPkg.FwdFilter) if err != nil { - l.fail(LinkFailureError{code: ErrInternalError}, + l.failf(LinkFailureError{code: ErrInternalError}, "unable to set fwd filter: %v", err) return } @@ -4077,13 +4078,14 @@ func (l *channelLink) sendMalformedHTLCError(htlcIndex uint64, }) } -// fail is a function which is used to encapsulate the action necessary for +// failf is a function which is used to encapsulate the action necessary for // properly failing the link. It takes a LinkFailureError, which will be passed // to the OnChannelFailure closure, in order for it to determine if we should // force close the channel, and if we should send an error message to the // remote peer. -func (l *channelLink) fail(linkErr LinkFailureError, - format string, a ...interface{}) { +func (l *channelLink) failf(linkErr LinkFailureError, format string, + a ...interface{}) { + reason := fmt.Errorf(format, a...) // Return if we have already notified about a failure. diff --git a/htlcswitch/link_test.go b/htlcswitch/link_test.go index f50df8cd5e..60010d6fff 100644 --- a/htlcswitch/link_test.go +++ b/htlcswitch/link_test.go @@ -4881,7 +4881,8 @@ func (h *persistentLinkHarness) restartLink( FetchLastChannelUpdate: mockGetChanUpdateMessage, PreimageCache: pCache, OnChannelFailure: func(lnwire.ChannelID, - lnwire.ShortChannelID, LinkFailureError) { // nolint:whitespace + lnwire.ShortChannelID, LinkFailureError) { + }, UpdateContractSignals: func(*contractcourt.ContractSignals) error { return nil @@ -5836,7 +5837,7 @@ func TestChannelLinkFail(t *testing.T) { c.cfg.Peer.(*mockPeer).disconnected = true }, func(*testing.T, *Switch, *channelLink, - *lnwallet.LightningChannel) { //nolint:whitespace,lll + *lnwallet.LightningChannel) { // Should fail at startup. }, @@ -5856,7 +5857,7 @@ func TestChannelLinkFail(t *testing.T) { c.channel.State().Packager = pkg }, func(*testing.T, *Switch, *channelLink, - *lnwallet.LightningChannel) { //nolint:whitespace,lll + *lnwallet.LightningChannel) { // Should fail at startup. }, diff --git a/htlcswitch/mock.go b/htlcswitch/mock.go index ed05a31655..c328bc533e 100644 --- a/htlcswitch/mock.go +++ b/htlcswitch/mock.go @@ -1152,22 +1152,27 @@ type mockHTLCNotifier struct { } func (h *mockHTLCNotifier) NotifyForwardingEvent(key HtlcKey, info HtlcInfo, - eventType HtlcEventType) { //nolint:whitespace + eventType HtlcEventType) { + } func (h *mockHTLCNotifier) NotifyLinkFailEvent(key HtlcKey, info HtlcInfo, eventType HtlcEventType, linkErr *LinkError, - incoming bool) { //nolint:whitespace + incoming bool) { + } func (h *mockHTLCNotifier) NotifyForwardingFailEvent(key HtlcKey, - eventType HtlcEventType) { //nolint:whitespace + eventType HtlcEventType) { + } func (h *mockHTLCNotifier) NotifySettleEvent(key HtlcKey, - preimage lntypes.Preimage, eventType HtlcEventType) { //nolint:whitespace,lll + preimage lntypes.Preimage, eventType HtlcEventType) { + } func (h *mockHTLCNotifier) NotifyFinalHtlcEvent(key models.CircuitKey, - info channeldb.FinalHtlcInfo) { //nolint:whitespace + info channeldb.FinalHtlcInfo) { + } diff --git a/invoices/invoice_expiry_watcher.go b/invoices/invoice_expiry_watcher.go index 7c5539636c..d7659dce3f 100644 --- a/invoices/invoice_expiry_watcher.go +++ b/invoices/invoice_expiry_watcher.go @@ -1,6 +1,7 @@ package invoices import ( + "errors" "fmt" "sync" "time" @@ -345,14 +346,14 @@ func (ew *InvoiceExpiryWatcher) cancelNextHeightExpiredInvoice() { // unexpected error. func (ew *InvoiceExpiryWatcher) expireInvoice(hash lntypes.Hash, force bool) { err := ew.cancelInvoice(hash, force) - switch err { - case nil: + switch { + case err == nil: - case ErrInvoiceAlreadyCanceled: + case errors.Is(err, ErrInvoiceAlreadyCanceled): - case ErrInvoiceAlreadySettled: + case errors.Is(err, ErrInvoiceAlreadySettled): - case ErrInvoiceNotFound: + case errors.Is(err, ErrInvoiceNotFound): // It's possible that the user has manually canceled the invoice // which will then be deleted by the garbage collector resulting // in an ErrInvoiceNotFound error. diff --git a/invoices/invoiceregistry.go b/invoices/invoiceregistry.go index 472c55048e..7b3caa34a4 100644 --- a/invoices/invoiceregistry.go +++ b/invoices/invoiceregistry.go @@ -1056,8 +1056,8 @@ func (i *InvoiceRegistry) notifyExitHopHtlcLocked( ), nil, nil } - switch err { - case ErrInvoiceNotFound: + switch { + case errors.Is(err, ErrInvoiceNotFound): // If the invoice was not found, return a failure resolution // with an invoice not found result. return NewFailResolution( @@ -1065,13 +1065,13 @@ func (i *InvoiceRegistry) notifyExitHopHtlcLocked( ResultInvoiceNotFound, ), nil, nil - case ErrInvRefEquivocation: + case errors.Is(err, ErrInvRefEquivocation): return NewFailResolution( ctx.circuitKey, ctx.currentHeight, ResultInvoiceNotFound, ), nil, nil - case nil: + case err == nil: default: ctx.log(err.Error()) diff --git a/itest/lnd_channel_funding_fund_max_test.go b/itest/lnd_channel_funding_fund_max_test.go index b836bdf621..43aec3c982 100644 --- a/itest/lnd_channel_funding_fund_max_test.go +++ b/itest/lnd_channel_funding_fund_max_test.go @@ -2,7 +2,7 @@ package itest import ( "context" - "fmt" + "errors" "testing" "github.com/btcsuite/btcd/btcutil" @@ -219,7 +219,7 @@ func runFundMaxTestCase(ht *lntest.HarnessTest, alice, bob *node.HarnessNode, // If we don't expect the channel opening to be // successful, simply check for an error. if testCase.chanOpenShouldFail { - expectedErr := fmt.Errorf(testCase.expectedErrStr) + expectedErr := errors.New(testCase.expectedErrStr) ht.OpenChannelAssertErr( alice, bob, chanParams, expectedErr, ) diff --git a/itest/lnd_channel_funding_utxo_selection_test.go b/itest/lnd_channel_funding_utxo_selection_test.go index 8504a17cb9..2b6d0cd301 100644 --- a/itest/lnd_channel_funding_utxo_selection_test.go +++ b/itest/lnd_channel_funding_utxo_selection_test.go @@ -2,6 +2,7 @@ package itest import ( "context" + "errors" "fmt" "testing" @@ -315,7 +316,7 @@ func runUtxoSelectionTestCase(ht *lntest.HarnessTest, alice, // If we don't expect the channel opening to be // successful, simply check for an error. if tc.chanOpenShouldFail { - expectedErr := fmt.Errorf(tc.expectedErrStr) + expectedErr := errors.New(tc.expectedErrStr) ht.OpenChannelAssertErr( alice, bob, chanParams, expectedErr, ) @@ -334,7 +335,7 @@ func runUtxoSelectionTestCase(ht *lntest.HarnessTest, alice, "locked by another subsystem: %s:%d", selectedOutpoints[0].TxidStr, selectedOutpoints[0].OutputIndex) - expectedErr := fmt.Errorf(expectedErrStr) + expectedErr := errors.New(expectedErrStr) ht.OpenChannelAssertErr( alice, bob, chanParams, expectedErr, ) diff --git a/lntest/node/harness_node.go b/lntest/node/harness_node.go index 3df5a8e64d..7415dfed29 100644 --- a/lntest/node/harness_node.go +++ b/lntest/node/harness_node.go @@ -6,7 +6,6 @@ import ( "crypto/rand" "encoding/hex" "encoding/json" - "errors" "fmt" "io" "os" @@ -661,8 +660,7 @@ func (hn *HarnessNode) WaitForProcessExit() error { break case <-time.After(wait.DefaultTimeout): - err = errors.New("timeout waiting for process to exit") - hn.printErrf(err.Error()) + hn.printErrf("timeout waiting for process to exit") } // Make sure log file is closed and renamed if necessary. diff --git a/lnwallet/btcwallet/btcwallet.go b/lnwallet/btcwallet/btcwallet.go index b3f9b4f9b1..e796c4322c 100644 --- a/lnwallet/btcwallet/btcwallet.go +++ b/lnwallet/btcwallet/btcwallet.go @@ -1688,7 +1688,6 @@ out: } } } - }(txNtfn) // Launch a goroutine to re-package and send diff --git a/lnwallet/chanfunding/coin_select_test.go b/lnwallet/chanfunding/coin_select_test.go index a6ff18baec..26a257b37f 100644 --- a/lnwallet/chanfunding/coin_select_test.go +++ b/lnwallet/chanfunding/coin_select_test.go @@ -874,7 +874,7 @@ func TestCoinSelectUpToAmount(t *testing.T) { defaultChanFundingChangeType, ) if len(test.expectErr) == 0 && err != nil { - t.Fatalf(err.Error()) + t.Fatal(err.Error()) } if changeAmt != test.expectedChange { t.Fatalf("expected %v change amt, got %v", diff --git a/msgmux/msg_router.go b/msgmux/msg_router.go index cd69b14a61..db9e783990 100644 --- a/msgmux/msg_router.go +++ b/msgmux/msg_router.go @@ -1,10 +1,5 @@ package msgmux -// For some reason golangci-lint has a false positive on the sort order of the -// imports for the new "maps" package... We need the nolint directive here to -// ignore that. -// -//nolint:gci import ( "fmt" "maps" diff --git a/tools/Dockerfile b/tools/Dockerfile index 2545b9c02d..47a081b683 100644 --- a/tools/Dockerfile +++ b/tools/Dockerfile @@ -1,4 +1,4 @@ -FROM golang:1.21 +FROM golang:1.22.6 RUN apt-get update && apt-get install -y git ENV GOCACHE=/tmp/build/.cache diff --git a/tools/go.mod b/tools/go.mod index 247f5ca122..02aaaea8d5 100644 --- a/tools/go.mod +++ b/tools/go.mod @@ -1,10 +1,10 @@ module github.com/lightningnetwork/lnd/tools -go 1.19 +go 1.22.6 require ( github.com/btcsuite/btcd v0.23.3 - github.com/golangci/golangci-lint v1.52.2 + github.com/golangci/golangci-lint v1.60.1 github.com/ory/go-acc v0.2.8 github.com/rinchsan/gosimports v0.1.5 ) @@ -12,25 +12,30 @@ require ( require ( 4d63.com/gocheckcompilerdirectives v1.2.1 // indirect 4d63.com/gochecknoglobals v0.2.1 // indirect - github.com/Abirdcfly/dupword v0.0.11 // indirect - github.com/Antonboom/errname v0.1.9 // indirect - github.com/Antonboom/nilnil v0.1.3 // indirect - github.com/BurntSushi/toml v1.2.1 // indirect + github.com/4meepo/tagalign v1.3.4 // indirect + github.com/Abirdcfly/dupword v0.0.14 // indirect + github.com/Antonboom/errname v0.1.13 // indirect + github.com/Antonboom/nilnil v0.1.9 // indirect + github.com/Antonboom/testifylint v1.4.3 // indirect + github.com/BurntSushi/toml v1.4.1-0.20240526193622-a339e1f7089c // indirect + github.com/Crocmagnon/fatcontext v0.4.0 // indirect github.com/Djarvur/go-err113 v0.0.0-20210108212216-aea10b59be24 // indirect - github.com/GaijinEntertainment/go-exhaustruct/v2 v2.3.0 // indirect - github.com/Masterminds/semver v1.5.0 // indirect - github.com/OpenPeeDeeP/depguard v1.1.1 // indirect + github.com/GaijinEntertainment/go-exhaustruct/v3 v3.3.0 // indirect + github.com/Masterminds/semver/v3 v3.2.1 // indirect + github.com/OpenPeeDeeP/depguard/v2 v2.2.0 // indirect github.com/aead/siphash v1.0.1 // indirect + github.com/alecthomas/go-check-sumtype v0.1.4 // indirect + github.com/alexkohler/nakedret/v2 v2.0.4 // indirect github.com/alexkohler/prealloc v1.0.0 // indirect github.com/alingse/asasalint v0.0.11 // indirect - github.com/ashanbrown/forbidigo v1.5.1 // indirect + github.com/ashanbrown/forbidigo v1.6.0 // indirect github.com/ashanbrown/makezero v1.1.1 // indirect github.com/beorn7/perks v1.0.1 // indirect - github.com/bkielbasa/cyclop v1.2.0 // indirect + github.com/bkielbasa/cyclop v1.2.1 // indirect github.com/blizzy78/varnamelen v0.8.0 // indirect - github.com/bombsimon/wsl/v3 v3.4.0 // indirect - github.com/breml/bidichk v0.2.4 // indirect - github.com/breml/errchkjson v0.3.1 // indirect + github.com/bombsimon/wsl/v4 v4.4.1 // indirect + github.com/breml/bidichk v0.2.7 // indirect + github.com/breml/errchkjson v0.3.6 // indirect github.com/btcsuite/btcd/btcec/v2 v2.1.3 // indirect github.com/btcsuite/btcd/btcutil v1.1.0 // indirect github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 // indirect @@ -38,168 +43,172 @@ require ( github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd // indirect github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792 // indirect github.com/btcsuite/winsvc v1.0.0 // indirect - github.com/butuzov/ireturn v0.1.1 // indirect + github.com/butuzov/ireturn v0.3.0 // indirect + github.com/butuzov/mirror v1.2.0 // indirect + github.com/catenacyber/perfsprint v0.7.1 // indirect + github.com/ccojocar/zxcvbn-go v1.0.2 // indirect github.com/cespare/xxhash v1.1.0 // indirect github.com/cespare/xxhash/v2 v2.1.2 // indirect github.com/charithe/durationcheck v0.0.10 // indirect - github.com/chavacava/garif v0.0.0-20230227094218-b8c73b2037b8 // indirect + github.com/chavacava/garif v0.1.0 // indirect + github.com/ckaznocha/intrange v0.1.2 // indirect github.com/curioswitch/go-reassign v0.2.0 // indirect - github.com/daixiang0/gci v0.10.1 // indirect + github.com/daixiang0/gci v0.13.4 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/decred/dcrd/crypto/blake256 v1.0.0 // indirect github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 // indirect github.com/decred/dcrd/lru v1.0.0 // indirect - github.com/denis-tingaikin/go-header v0.4.3 // indirect + github.com/denis-tingaikin/go-header v0.5.0 // indirect github.com/dgraph-io/ristretto v0.0.2 // indirect - github.com/esimonov/ifshort v1.0.4 // indirect - github.com/ettle/strcase v0.1.1 // indirect - github.com/fatih/color v1.15.0 // indirect + github.com/ettle/strcase v0.2.0 // indirect + github.com/fatih/color v1.17.0 // indirect github.com/fatih/structtag v1.2.0 // indirect - github.com/firefart/nonamedreturns v1.0.4 // indirect + github.com/firefart/nonamedreturns v1.0.5 // indirect github.com/fsnotify/fsnotify v1.5.4 // indirect github.com/fzipp/gocyclo v0.6.0 // indirect - github.com/go-critic/go-critic v0.7.0 // indirect + github.com/ghostiam/protogetter v0.3.6 // indirect + github.com/go-critic/go-critic v0.11.4 // indirect github.com/go-toolsmith/astcast v1.1.0 // indirect github.com/go-toolsmith/astcopy v1.1.0 // indirect - github.com/go-toolsmith/astequal v1.1.0 // indirect + github.com/go-toolsmith/astequal v1.2.0 // indirect github.com/go-toolsmith/astfmt v1.1.0 // indirect github.com/go-toolsmith/astp v1.1.0 // indirect github.com/go-toolsmith/strparse v1.1.0 // indirect github.com/go-toolsmith/typep v1.1.0 // indirect + github.com/go-viper/mapstructure/v2 v2.0.0 // indirect github.com/go-xmlfmt/xmlfmt v1.1.2 // indirect github.com/gobwas/glob v0.2.3 // indirect - github.com/gofrs/flock v0.8.1 // indirect - github.com/golang/protobuf v1.5.2 // indirect + github.com/gofrs/flock v0.12.1 // indirect + github.com/golang/protobuf v1.5.3 // indirect github.com/golang/snappy v0.0.4 // indirect - github.com/golangci/check v0.0.0-20180506172741-cfe4005ccda2 // indirect github.com/golangci/dupl v0.0.0-20180902072040-3e9179ac440a // indirect - github.com/golangci/go-misc v0.0.0-20220329215616-d24fe342adfe // indirect - github.com/golangci/gofmt v0.0.0-20220901101216-f2edd75033f2 // indirect - github.com/golangci/lint-1 v0.0.0-20191013205115-297bf364a8e0 // indirect - github.com/golangci/maligned v0.0.0-20180506175553-b1d89398deca // indirect - github.com/golangci/misspell v0.4.0 // indirect - github.com/golangci/revgrep v0.0.0-20220804021717-745bb2f7c2e6 // indirect - github.com/golangci/unconvert v0.0.0-20180507085042-28b1c447d1f4 // indirect - github.com/google/go-cmp v0.5.9 // indirect - github.com/google/uuid v1.3.0 // indirect - github.com/gordonklaus/ineffassign v0.0.0-20230107090616-13ace0543b28 // indirect + github.com/golangci/gofmt v0.0.0-20231018234816-f50ced29576e // indirect + github.com/golangci/misspell v0.6.0 // indirect + github.com/golangci/modinfo v0.3.4 // indirect + github.com/golangci/plugin-module-register v0.1.1 // indirect + github.com/golangci/revgrep v0.5.3 // indirect + github.com/golangci/unconvert v0.0.0-20240309020433-c5143eacb3ed // indirect + github.com/google/go-cmp v0.6.0 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/gordonklaus/ineffassign v0.1.0 // indirect github.com/gostaticanalysis/analysisutil v0.7.1 // indirect github.com/gostaticanalysis/comment v1.4.2 // indirect github.com/gostaticanalysis/forcetypeassert v0.1.0 // indirect github.com/gostaticanalysis/nilerr v0.1.1 // indirect - github.com/hashicorp/errwrap v1.0.0 // indirect - github.com/hashicorp/go-multierror v1.1.1 // indirect - github.com/hashicorp/go-version v1.6.0 // indirect + github.com/hashicorp/go-version v1.7.0 // indirect github.com/hashicorp/hcl v1.0.0 // indirect github.com/hexops/gotextdiff v1.0.3 // indirect - github.com/inconshreveable/mousetrap v1.0.1 // indirect + github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/jessevdk/go-flags v1.4.0 // indirect - github.com/jgautheron/goconst v1.5.1 // indirect + github.com/jgautheron/goconst v1.7.1 // indirect github.com/jingyugao/rowserrcheck v1.1.1 // indirect github.com/jirfag/go-printf-func-name v0.0.0-20200119135958-7558a9eaa5af // indirect + github.com/jjti/go-spancheck v0.6.2 // indirect github.com/jrick/logrotate v1.0.0 // indirect github.com/julz/importas v0.1.0 // indirect - github.com/junk1tm/musttag v0.5.0 // indirect - github.com/kisielk/errcheck v1.6.3 // indirect - github.com/kisielk/gotool v1.0.0 // indirect - github.com/kkHAIKE/contextcheck v1.1.4 // indirect + github.com/karamaru-alpha/copyloopvar v1.1.0 // indirect + github.com/kisielk/errcheck v1.7.0 // indirect + github.com/kkHAIKE/contextcheck v1.1.5 // indirect github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23 // indirect github.com/kulti/thelper v0.6.3 // indirect - github.com/kunwardeep/paralleltest v1.0.6 // indirect + github.com/kunwardeep/paralleltest v1.0.10 // indirect github.com/kyoh86/exportloopref v0.1.11 // indirect - github.com/ldez/gomoddirectives v0.2.3 // indirect - github.com/ldez/tagliatelle v0.4.0 // indirect - github.com/leonklingele/grouper v1.1.1 // indirect + github.com/lasiar/canonicalheader v1.1.1 // indirect + github.com/ldez/gomoddirectives v0.2.4 // indirect + github.com/ldez/tagliatelle v0.5.0 // indirect + github.com/leonklingele/grouper v1.1.2 // indirect github.com/lufeee/execinquery v1.2.1 // indirect + github.com/macabu/inamedparam v0.1.3 // indirect github.com/magiconair/properties v1.8.6 // indirect github.com/maratori/testableexamples v1.0.0 // indirect github.com/maratori/testpackage v1.1.1 // indirect github.com/matoous/godox v0.0.0-20230222163458-006bad1f9d26 // indirect github.com/mattn/go-colorable v0.1.13 // indirect - github.com/mattn/go-isatty v0.0.17 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-runewidth v0.0.9 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect - github.com/mbilski/exhaustivestruct v1.2.0 // indirect - github.com/mgechev/revive v1.3.1 // indirect + github.com/mgechev/revive v1.3.9 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect - github.com/moricho/tparallel v0.3.1 // indirect + github.com/moricho/tparallel v0.3.2 // indirect github.com/nakabonne/nestif v0.3.1 // indirect - github.com/nbutton23/zxcvbn-go v0.0.0-20210217022336-fa2cb2858354 // indirect - github.com/nishanths/exhaustive v0.9.5 // indirect + github.com/nishanths/exhaustive v0.12.0 // indirect github.com/nishanths/predeclared v0.2.2 // indirect - github.com/nunnatsa/ginkgolinter v0.9.0 // indirect + github.com/nunnatsa/ginkgolinter v0.16.2 // indirect github.com/olekukonko/tablewriter v0.0.5 // indirect github.com/ory/viper v1.7.5 // indirect github.com/pborman/uuid v1.2.0 // indirect github.com/pelletier/go-toml v1.9.5 // indirect - github.com/pelletier/go-toml/v2 v2.0.5 // indirect + github.com/pelletier/go-toml/v2 v2.2.2 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/polyfloyd/go-errorlint v1.4.0 // indirect + github.com/polyfloyd/go-errorlint v1.6.0 // indirect github.com/prometheus/client_golang v1.12.1 // indirect github.com/prometheus/client_model v0.2.0 // indirect github.com/prometheus/common v0.32.1 // indirect github.com/prometheus/procfs v0.7.3 // indirect - github.com/quasilyte/go-ruleguard v0.3.19 // indirect + github.com/quasilyte/go-ruleguard v0.4.2 // indirect + github.com/quasilyte/go-ruleguard/dsl v0.3.22 // indirect github.com/quasilyte/gogrep v0.5.0 // indirect github.com/quasilyte/regex/syntax v0.0.0-20210819130434-b3f0c404a727 // indirect github.com/quasilyte/stdinfo v0.0.0-20220114132959-f7386bf02567 // indirect - github.com/ryancurrah/gomodguard v1.3.0 // indirect - github.com/ryanrolds/sqlclosecheck v0.4.0 // indirect + github.com/ryancurrah/gomodguard v1.3.3 // indirect + github.com/ryanrolds/sqlclosecheck v0.5.1 // indirect github.com/sanposhiho/wastedassign/v2 v2.0.7 // indirect + github.com/santhosh-tekuri/jsonschema/v5 v5.3.1 // indirect github.com/sashamelentyev/interfacebloat v1.1.0 // indirect - github.com/sashamelentyev/usestdlibvars v1.23.0 // indirect - github.com/securego/gosec/v2 v2.15.0 // indirect + github.com/sashamelentyev/usestdlibvars v1.27.0 // indirect + github.com/securego/gosec/v2 v2.20.1-0.20240525090044-5f0084eb01a9 // indirect github.com/shazow/go-diff v0.0.0-20160112020656-b6b7b6733b8c // indirect - github.com/sirupsen/logrus v1.9.0 // indirect - github.com/sivchari/containedctx v1.0.2 // indirect - github.com/sivchari/nosnakecase v1.7.0 // indirect - github.com/sivchari/tenv v1.7.1 // indirect + github.com/sirupsen/logrus v1.9.3 // indirect + github.com/sivchari/containedctx v1.0.3 // indirect + github.com/sivchari/tenv v1.10.0 // indirect github.com/sonatard/noctx v0.0.2 // indirect github.com/sourcegraph/go-diff v0.7.0 // indirect - github.com/spf13/afero v1.8.2 // indirect + github.com/spf13/afero v1.11.0 // indirect github.com/spf13/cast v1.5.0 // indirect - github.com/spf13/cobra v1.6.1 // indirect + github.com/spf13/cobra v1.8.1 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/spf13/viper v1.12.0 // indirect github.com/ssgreg/nlreturn/v2 v2.2.1 // indirect github.com/stbenjam/no-sprintf-host-port v0.1.1 // indirect - github.com/stretchr/objx v0.5.0 // indirect - github.com/stretchr/testify v1.8.2 // indirect + github.com/stretchr/objx v0.5.2 // indirect + github.com/stretchr/testify v1.9.0 // indirect github.com/subosito/gotenv v1.4.1 // indirect github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 // indirect - github.com/t-yuki/gocover-cobertura v0.0.0-20180217150009-aaee18c8195c // indirect github.com/tdakkota/asciicheck v0.2.0 // indirect - github.com/tetafro/godot v1.4.11 // indirect - github.com/timakin/bodyclose v0.0.0-20221125081123-e39cf3fc478e // indirect + github.com/tetafro/godot v1.4.16 // indirect + github.com/timakin/bodyclose v0.0.0-20230421092635-574207250966 // indirect github.com/timonwong/loggercheck v0.9.4 // indirect - github.com/tomarrell/wrapcheck/v2 v2.8.1 // indirect + github.com/tomarrell/wrapcheck/v2 v2.8.3 // indirect github.com/tommy-muehle/go-mnd/v2 v2.5.1 // indirect - github.com/ultraware/funlen v0.0.3 // indirect - github.com/ultraware/whitespace v0.0.5 // indirect - github.com/uudashr/gocognit v1.0.6 // indirect + github.com/ultraware/funlen v0.1.0 // indirect + github.com/ultraware/whitespace v0.1.1 // indirect + github.com/uudashr/gocognit v1.1.3 // indirect + github.com/xen0n/gosmopolitan v1.2.2 // indirect github.com/yagipy/maintidx v1.0.0 // indirect - github.com/yeya24/promlinter v0.2.0 // indirect - gitlab.com/bosi/decorder v0.2.3 // indirect + github.com/yeya24/promlinter v0.3.0 // indirect + github.com/ykadowak/zerologlint v0.1.5 // indirect + gitlab.com/bosi/decorder v0.4.2 // indirect + go-simpler.org/musttag v0.12.2 // indirect + go-simpler.org/sloglint v0.7.2 // indirect go.uber.org/atomic v1.7.0 // indirect + go.uber.org/automaxprocs v1.5.3 // indirect go.uber.org/multierr v1.6.0 // indirect go.uber.org/zap v1.24.0 // indirect - golang.org/x/crypto v0.5.0 // indirect - golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e // indirect - golang.org/x/exp/typeparams v0.0.0-20230224173230-c95f2b4c22f2 // indirect - golang.org/x/mod v0.9.0 // indirect - golang.org/x/sync v0.1.0 // indirect - golang.org/x/sys v0.6.0 // indirect - golang.org/x/text v0.7.0 // indirect - golang.org/x/tools v0.7.0 // indirect - google.golang.org/protobuf v1.28.0 // indirect + golang.org/x/crypto v0.23.0 // indirect + golang.org/x/exp v0.0.0-20240103183307-be819d1f06fc // indirect + golang.org/x/exp/typeparams v0.0.0-20240314144324-c7f7c6466f7f // indirect + golang.org/x/mod v0.20.0 // indirect + golang.org/x/sync v0.8.0 // indirect + golang.org/x/sys v0.23.0 // indirect + golang.org/x/text v0.15.0 // indirect + golang.org/x/tools v0.24.0 // indirect + google.golang.org/protobuf v1.33.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - honnef.co/go/tools v0.4.3 // indirect - mvdan.cc/gofumpt v0.4.0 // indirect - mvdan.cc/interfacer v0.0.0-20180901003855-c20040233aed // indirect - mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b // indirect - mvdan.cc/unparam v0.0.0-20221223090309-7455f1af531d // indirect + honnef.co/go/tools v0.5.0 // indirect + mvdan.cc/gofumpt v0.6.0 // indirect + mvdan.cc/unparam v0.0.0-20240528143540-8a5130ca722f // indirect ) diff --git a/tools/go.sum b/tools/go.sum index ab681737bd..a71a0533fc 100644 --- a/tools/go.sum +++ b/tools/go.sum @@ -7,7 +7,6 @@ cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMT cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= -cloud.google.com/go v0.44.3/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= @@ -18,9 +17,6 @@ cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKV cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= -cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= -cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= -cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY= cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= @@ -38,59 +34,73 @@ cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0Zeo cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= -cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= -github.com/Abirdcfly/dupword v0.0.11 h1:z6v8rMETchZXUIuHxYNmlUAuKuB21PeaSymTed16wgU= -github.com/Abirdcfly/dupword v0.0.11/go.mod h1:wH8mVGuf3CP5fsBTkfWwwwKTjDnVVCxtU8d8rgeVYXA= -github.com/Antonboom/errname v0.1.9 h1:BZDX4r3l4TBZxZ2o2LNrlGxSHran4d1u4veZdoORTT4= -github.com/Antonboom/errname v0.1.9/go.mod h1:nLTcJzevREuAsgTbG85UsuiWpMpAqbKD1HNZ29OzE58= -github.com/Antonboom/nilnil v0.1.3 h1:6RTbx3d2mcEu3Zwq9TowQpQMVpP75zugwOtqY1RTtcE= -github.com/Antonboom/nilnil v0.1.3/go.mod h1:iOov/7gRcXkeEU+EMGpBu2ORih3iyVEiWjeste1SJm8= +github.com/4meepo/tagalign v1.3.4 h1:P51VcvBnf04YkHzjfclN6BbsopfJR5rxs1n+5zHt+w8= +github.com/4meepo/tagalign v1.3.4/go.mod h1:M+pnkHH2vG8+qhE5bVc/zeP7HS/j910Fwa9TUSyZVI0= +github.com/Abirdcfly/dupword v0.0.14 h1:3U4ulkc8EUo+CaT105/GJ1BQwtgyj6+VaBVbAX11Ba8= +github.com/Abirdcfly/dupword v0.0.14/go.mod h1:VKDAbxdY8YbKUByLGg8EETzYSuC4crm9WwI6Y3S0cLI= +github.com/Antonboom/errname v0.1.13 h1:JHICqsewj/fNckzrfVSe+T33svwQxmjC+1ntDsHOVvM= +github.com/Antonboom/errname v0.1.13/go.mod h1:uWyefRYRN54lBg6HseYCFhs6Qjcy41Y3Jl/dVhA87Ns= +github.com/Antonboom/nilnil v0.1.9 h1:eKFMejSxPSA9eLSensFmjW2XTgTwJMjZ8hUHtV4s/SQ= +github.com/Antonboom/nilnil v0.1.9/go.mod h1:iGe2rYwCq5/Me1khrysB4nwI7swQvjclR8/YRPl5ihQ= +github.com/Antonboom/testifylint v1.4.3 h1:ohMt6AHuHgttaQ1xb6SSnxCeK4/rnK7KKzbvs7DmEck= +github.com/Antonboom/testifylint v1.4.3/go.mod h1:+8Q9+AOLsz5ZiQiiYujJKs9mNz398+M6UgslP4qgJLA= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/toml v1.2.1 h1:9F2/+DoOYIOksmaJFPw1tGFy1eDnIJXg+UHjuD8lTak= -github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= +github.com/BurntSushi/toml v1.4.1-0.20240526193622-a339e1f7089c h1:pxW6RcqyfI9/kWtOwnv/G+AzdKuy2ZrqINhenH4HyNs= +github.com/BurntSushi/toml v1.4.1-0.20240526193622-a339e1f7089c/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/Crocmagnon/fatcontext v0.4.0 h1:4ykozu23YHA0JB6+thiuEv7iT6xq995qS1vcuWZq0tg= +github.com/Crocmagnon/fatcontext v0.4.0/go.mod h1:ZtWrXkgyfsYPzS6K3O88va6t2GEglG93vnII/F94WC0= github.com/Djarvur/go-err113 v0.0.0-20210108212216-aea10b59be24 h1:sHglBQTwgx+rWPdisA5ynNEsoARbiCBOyGcJM4/OzsM= github.com/Djarvur/go-err113 v0.0.0-20210108212216-aea10b59be24/go.mod h1:4UJr5HIiMZrwgkSPdsjy2uOQExX/WEILpIrO9UPGuXs= -github.com/GaijinEntertainment/go-exhaustruct/v2 v2.3.0 h1:+r1rSv4gvYn0wmRjC8X7IAzX8QezqtFV9m0MUHFJgts= -github.com/GaijinEntertainment/go-exhaustruct/v2 v2.3.0/go.mod h1:b3g59n2Y+T5xmcxJL+UEG2f8cQploZm1mR/v6BW0mU0= -github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww= -github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= +github.com/GaijinEntertainment/go-exhaustruct/v3 v3.3.0 h1:/fTUt5vmbkAcMBt4YQiuC23cV0kEsN1MVMNqeOW43cU= +github.com/GaijinEntertainment/go-exhaustruct/v3 v3.3.0/go.mod h1:ONJg5sxcbsdQQ4pOW8TGdTidT2TMAUy/2Xhr8mrYaao= +github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0rYXWg0= +github.com/Masterminds/semver/v3 v3.2.1/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ= github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= -github.com/OpenPeeDeeP/depguard v1.1.1 h1:TSUznLjvp/4IUP+OQ0t/4jF4QUyxIcVX8YnghZdunyA= -github.com/OpenPeeDeeP/depguard v1.1.1/go.mod h1:JtAMzWkmFEzDPyAd+W0NHl1lvpQKTvT9jnRVsohBKpc= +github.com/OpenPeeDeeP/depguard/v2 v2.2.0 h1:vDfG60vDtIuf0MEOhmLlLLSzqaRM8EMcgJPdp74zmpA= +github.com/OpenPeeDeeP/depguard/v2 v2.2.0/go.mod h1:CIzddKRvLBC4Au5aYP/i3nyaWQ+ClszLIuVocRiCYFQ= github.com/aead/siphash v1.0.1 h1:FwHfE/T45KPKYuuSAKyyvE+oPWcaQ+CUmFW0bPlM+kg= github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII= +github.com/alecthomas/assert/v2 v2.2.2 h1:Z/iVC0xZfWTaFNE6bA3z07T86hd45Xe2eLt6WVy2bbk= +github.com/alecthomas/assert/v2 v2.2.2/go.mod h1:pXcQ2Asjp247dahGEmsZ6ru0UVwnkhktn7S0bBDLxvQ= +github.com/alecthomas/go-check-sumtype v0.1.4 h1:WCvlB3l5Vq5dZQTFmodqL2g68uHiSwwlWcT5a2FGK0c= +github.com/alecthomas/go-check-sumtype v0.1.4/go.mod h1:WyYPfhfkdhyrdaligV6svFopZV8Lqdzn5pyVBaV6jhQ= +github.com/alecthomas/repr v0.2.0 h1:HAzS41CIzNW5syS8Mf9UwXhNH1J9aix/BvDRf1Ml2Yk= +github.com/alecthomas/repr v0.2.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= +github.com/alexkohler/nakedret/v2 v2.0.4 h1:yZuKmjqGi0pSmjGpOC016LtPJysIL0WEUiaXW5SUnNg= +github.com/alexkohler/nakedret/v2 v2.0.4/go.mod h1:bF5i0zF2Wo2o4X4USt9ntUWve6JbFv02Ff4vlkmS/VU= github.com/alexkohler/prealloc v1.0.0 h1:Hbq0/3fJPQhNkN0dR95AVrr6R7tou91y0uHG5pOcUuw= github.com/alexkohler/prealloc v1.0.0/go.mod h1:VetnK3dIgFBBKmg0YnD9F9x6Icjd+9cvfHR56wJVlKE= github.com/alingse/asasalint v0.0.11 h1:SFwnQXJ49Kx/1GghOFz1XGqHYKp21Kq1nHad/0WQRnw= github.com/alingse/asasalint v0.0.11/go.mod h1:nCaoMhw7a9kSJObvQyVzNTPBDbNpdocqrSP7t/cW5+I= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= -github.com/ashanbrown/forbidigo v1.5.1 h1:WXhzLjOlnuDYPYQo/eFlcFMi8X/kLfvWLYu6CSoebis= -github.com/ashanbrown/forbidigo v1.5.1/go.mod h1:Y8j9jy9ZYAEHXdu723cUlraTqbzjKF1MUyfOKL+AjcU= +github.com/ashanbrown/forbidigo v1.6.0 h1:D3aewfM37Yb3pxHujIPSpTf6oQk9sc9WZi8gerOIVIY= +github.com/ashanbrown/forbidigo v1.6.0/go.mod h1:Y8j9jy9ZYAEHXdu723cUlraTqbzjKF1MUyfOKL+AjcU= github.com/ashanbrown/makezero v1.1.1 h1:iCQ87C0V0vSyO+M9E/FZYbu65auqH0lnsOkf5FcB28s= github.com/ashanbrown/makezero v1.1.1/go.mod h1:i1bJLCRSCHOcOa9Y6MyF2FTfMZMFdHvxKHxgO5Z1axI= github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= +github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= -github.com/bkielbasa/cyclop v1.2.0 h1:7Jmnh0yL2DjKfw28p86YTd/B4lRGcNuu12sKE35sM7A= -github.com/bkielbasa/cyclop v1.2.0/go.mod h1:qOI0yy6A7dYC4Zgsa72Ppm9kONl0RoIlPbzot9mhmeI= +github.com/bkielbasa/cyclop v1.2.1 h1:AeF71HZDob1P2/pRm1so9cd1alZnrpyc4q2uP2l0gJY= +github.com/bkielbasa/cyclop v1.2.1/go.mod h1:K/dT/M0FPAiYjBgQGau7tz+3TMh4FWAEqlMhzFWCrgM= github.com/blizzy78/varnamelen v0.8.0 h1:oqSblyuQvFsW1hbBHh1zfwrKe3kcSj0rnXkKzsQ089M= github.com/blizzy78/varnamelen v0.8.0/go.mod h1:V9TzQZ4fLJ1DSrjVDfl89H7aMnTvKkApdHeyESmyR7k= -github.com/bombsimon/wsl/v3 v3.4.0 h1:RkSxjT3tmlptwfgEgTgU+KYKLI35p/tviNXNXiL2aNU= -github.com/bombsimon/wsl/v3 v3.4.0/go.mod h1:KkIB+TXkqy6MvK9BDZVbZxKNYsE1/oLRJbIFtf14qqo= -github.com/breml/bidichk v0.2.4 h1:i3yedFWWQ7YzjdZJHnPo9d/xURinSq3OM+gyM43K4/8= -github.com/breml/bidichk v0.2.4/go.mod h1:7Zk0kRFt1LIZxtQdl9W9JwGAcLTTkOs+tN7wuEYGJ3s= -github.com/breml/errchkjson v0.3.1 h1:hlIeXuspTyt8Y/UmP5qy1JocGNR00KQHgfaNtRAjoxQ= -github.com/breml/errchkjson v0.3.1/go.mod h1:XroxrzKjdiutFyW3nWhw34VGg7kiMsDQox73yWCGI2U= +github.com/bombsimon/wsl/v4 v4.4.1 h1:jfUaCkN+aUpobrMO24zwyAMwMAV5eSziCkOKEauOLdw= +github.com/bombsimon/wsl/v4 v4.4.1/go.mod h1:Xu/kDxGZTofQcDGCtQe9KCzhHphIe0fDuyWTxER9Feo= +github.com/breml/bidichk v0.2.7 h1:dAkKQPLl/Qrk7hnP6P+E0xOodrq8Us7+U0o4UBOAlQY= +github.com/breml/bidichk v0.2.7/go.mod h1:YodjipAGI9fGcYM7II6wFvGhdMYsC5pHDlGzqvEW3tQ= +github.com/breml/errchkjson v0.3.6 h1:VLhVkqSBH96AvXEyclMR37rZslRrY2kcyq+31HCsVrA= +github.com/breml/errchkjson v0.3.6/go.mod h1:jhSDoFheAF2RSDOlCfhHO9KqhZgAYLyvHe7bRCX8f/U= github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ= github.com/btcsuite/btcd v0.22.0-beta.0.20220111032746-97732e52810c/go.mod h1:tjmYdS6MLJ5/s0Fj4DbLgSbDHbEqLJrtnHecBFkdz5M= github.com/btcsuite/btcd v0.23.3 h1:4KH/JKy9WiCd+iUS9Mu0Zp7Dnj17TGdKrg9xc/FGj24= @@ -116,8 +126,14 @@ github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792 h1:R8vQdOQdZ9Y3 github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY= github.com/btcsuite/winsvc v1.0.0 h1:J9B4L7e3oqhXOcm+2IuNApwzQec85lE+QaikUcCs+dk= github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs= -github.com/butuzov/ireturn v0.1.1 h1:QvrO2QF2+/Cx1WA/vETCIYBKtRjc30vesdoPUNo1EbY= -github.com/butuzov/ireturn v0.1.1/go.mod h1:Wh6Zl3IMtTpaIKbmwzqi6olnM9ptYQxxVacMsOEFPoc= +github.com/butuzov/ireturn v0.3.0 h1:hTjMqWw3y5JC3kpnC5vXmFJAWI/m31jaCYQqzkS6PL0= +github.com/butuzov/ireturn v0.3.0/go.mod h1:A09nIiwiqzN/IoVo9ogpa0Hzi9fex1kd9PSD6edP5ZA= +github.com/butuzov/mirror v1.2.0 h1:9YVK1qIjNspaqWutSv8gsge2e/Xpq1eqEkslEUHy5cs= +github.com/butuzov/mirror v1.2.0/go.mod h1:DqZZDtzm42wIAIyHXeN8W/qb1EPlb9Qn/if9icBOpdQ= +github.com/catenacyber/perfsprint v0.7.1 h1:PGW5G/Kxn+YrN04cRAZKC+ZuvlVwolYMrIyyTJ/rMmc= +github.com/catenacyber/perfsprint v0.7.1/go.mod h1:/wclWYompEyjUD2FuIIDVKNkqz7IgBIWXIH3V0Zol50= +github.com/ccojocar/zxcvbn-go v1.0.2 h1:na/czXU8RrhXO4EZme6eQJLR4PzcGsahsBOAwU6I3Vg= +github.com/ccojocar/zxcvbn-go v1.0.2/go.mod h1:g1qkXtUSvHP8lhHp5GrSmTz6uWALGRMQdw6Qnz/hi60= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= @@ -126,26 +142,26 @@ github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cb github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/charithe/durationcheck v0.0.10 h1:wgw73BiocdBDQPik+zcEoBG/ob8uyBHf2iyoHGPf5w4= github.com/charithe/durationcheck v0.0.10/go.mod h1:bCWXb7gYRysD1CU3C+u4ceO49LoGOY1C1L6uouGNreQ= -github.com/chavacava/garif v0.0.0-20230227094218-b8c73b2037b8 h1:W9o46d2kbNL06lq7UNDPV0zYLzkrde/bjIqO02eoll0= -github.com/chavacava/garif v0.0.0-20230227094218-b8c73b2037b8/go.mod h1:gakxgyXaaPkxvLw1XQxNGK4I37ys9iBRzNUx/B7pUCo= +github.com/chavacava/garif v0.1.0 h1:2JHa3hbYf5D9dsgseMKAmc/MZ109otzgNFk5s87H9Pc= +github.com/chavacava/garif v0.1.0/go.mod h1:XMyYCkEL58DF0oyW4qDjjnPWONs2HBqYKI+UIPD+Gww= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/ckaznocha/intrange v0.1.2 h1:3Y4JAxcMntgb/wABQ6e8Q8leMd26JbX2790lIss9MTI= +github.com/ckaznocha/intrange v0.1.2/go.mod h1:RWffCw/vKBwHeOEwWdCikAtY0q4gGt8VhJZEEA5n+RE= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= -github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= -github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/curioswitch/go-reassign v0.2.0 h1:G9UZyOcpk/d7Gd6mqYgd8XYWFMw/znxwGDUstnC9DIo= github.com/curioswitch/go-reassign v0.2.0/go.mod h1:x6OpXuWvgfQaMGks2BZybTngWjT84hqJfKoO8Tt/Roc= -github.com/daixiang0/gci v0.10.1 h1:eheNA3ljF6SxnPD/vE4lCBusVHmV3Rs3dkKvFrJ7MR0= -github.com/daixiang0/gci v0.10.1/go.mod h1:xtHP9N7AHdNvtRNfcx9gwTDfw7FRJx4bZUsiEfiNNAI= +github.com/daixiang0/gci v0.13.4 h1:61UGkmpoAcxHM2hhNkZEf5SzwQtWJXTSws7jaPyqwlw= +github.com/daixiang0/gci v0.13.4/go.mod h1:12etP2OniiIdP4q+kjUGrC/rUagga7ODbqsom5Eo5Yk= github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= @@ -156,8 +172,8 @@ github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 h1:YLtO71vCjJRCBcrPMtQ9nqBsqpA1 github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeCxkaw7y45JueMRL4DIyJDKs= github.com/decred/dcrd/lru v1.0.0 h1:Kbsb1SFDsIlaupWPwsPp+dkxiBY1frcS07PCPgotKz8= github.com/decred/dcrd/lru v1.0.0/go.mod h1:mxKOwFd7lFjN2GZYsiz/ecgqR6kkYAl+0pz0tEMk218= -github.com/denis-tingaikin/go-header v0.4.3 h1:tEaZKAlqql6SKCY++utLmkPLd6K8IBM20Ha7UVm+mtU= -github.com/denis-tingaikin/go-header v0.4.3/go.mod h1:0wOCWuN71D5qIgE2nz9KrKmuYBAC2Mra5RassOIQ2/c= +github.com/denis-tingaikin/go-header v0.5.0 h1:SRdnP5ZKvcO9KKRP1KJrhFR3RrlGuD+42t4429eC9k8= +github.com/denis-tingaikin/go-header v0.5.0/go.mod h1:mMenU5bWrok6Wl2UsZjy+1okegmwQ3UgWl4V1D8gjlY= github.com/dgraph-io/ristretto v0.0.1/go.mod h1:T40EBc7CJke8TkpiYfGGKAeFjSaxuFXhuXRyumBd6RE= github.com/dgraph-io/ristretto v0.0.2 h1:a5WaUrDa0qm0YrAAS1tUykT5El3kt62KNZZeMxQn3po= github.com/dgraph-io/ristretto v0.0.2/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E= @@ -168,20 +184,17 @@ github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8 github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= -github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= -github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/esimonov/ifshort v1.0.4 h1:6SID4yGWfRae/M7hkVDVVyppy8q/v9OuxNdmjLQStBA= -github.com/esimonov/ifshort v1.0.4/go.mod h1:Pe8zjlRrJ80+q2CxHLfEOfTwxCZ4O+MuhcHcfgNWTk0= -github.com/ettle/strcase v0.1.1 h1:htFueZyVeE1XNnMEfbqp5r67qAN/4r6ya1ysq8Q+Zcw= -github.com/ettle/strcase v0.1.1/go.mod h1:hzDLsPC7/lwKyBOywSHEP89nt2pDgdy+No1NBA9o9VY= -github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs= -github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw= +github.com/ettle/strcase v0.2.0 h1:fGNiVF21fHXpX1niBgk0aROov1LagYsOwV/xqKDKR/Q= +github.com/ettle/strcase v0.2.0/go.mod h1:DajmHElDSaX76ITe3/VHVyMin4LWSJN5Z909Wp+ED1A= +github.com/fatih/color v1.17.0 h1:GlRw1BRJxkpqUCBKzKOw098ed57fEsKeNjpTe3cSjK4= +github.com/fatih/color v1.17.0/go.mod h1:YZ7TlrGPkiz6ku9fK3TLD/pl3CpsiFyu8N92HLgmosI= github.com/fatih/structtag v1.2.0 h1:/OdNE99OxoI/PqaW/SuSK9uxxT3f/tcSZgon/ssNSx4= github.com/fatih/structtag v1.2.0/go.mod h1:mBJUNpUnHmRKrKlQQlmCrh5PuhftFbNv8Ys4/aAZl94= -github.com/firefart/nonamedreturns v1.0.4 h1:abzI1p7mAEPYuR4A+VLKn4eNDOycjYo2phmY9sfv40Y= -github.com/firefart/nonamedreturns v1.0.4/go.mod h1:TDhe/tjI1BXo48CmYbUduTV7BdIga8MAO/xbKdcVsGI= -github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE= +github.com/firefart/nonamedreturns v1.0.5 h1:tM+Me2ZaXs8tfdDw3X6DOX++wMCOqzYUho6tUTYIdRA= +github.com/firefart/nonamedreturns v1.0.5/go.mod h1:gHJjDqhGM4WyPt639SOZs+G89Ko7QKH5R5BhnO6xJhw= +github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= +github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI= @@ -189,8 +202,10 @@ github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmV github.com/fzipp/gocyclo v0.6.0 h1:lsblElZG7d3ALtGMx9fmxeTKZaLLpU8mET09yN4BBLo= github.com/fzipp/gocyclo v0.6.0/go.mod h1:rXPyn8fnlpa0R2csP/31uerbiVBugk5whMdlyaLkLoA= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/go-critic/go-critic v0.7.0 h1:tqbKzB8pqi0NsRZ+1pyU4aweAF7A7QN0Pi4Q02+rYnQ= -github.com/go-critic/go-critic v0.7.0/go.mod h1:moYzd7GdVXE2C2hYTwd7h0CPcqlUeclsyBRwMa38v64= +github.com/ghostiam/protogetter v0.3.6 h1:R7qEWaSgFCsy20yYHNIJsU9ZOb8TziSRRxuAOTVKeOk= +github.com/ghostiam/protogetter v0.3.6/go.mod h1:7lpeDnEJ1ZjL/YtyoN99ljO4z0pd3H0d18/t2dPBxHw= +github.com/go-critic/go-critic v0.11.4 h1:O7kGOCx0NDIni4czrkRIXTnit0mkyKOCePh3My6OyEU= +github.com/go-critic/go-critic v0.11.4/go.mod h1:2QAdo4iuLik5S9YG0rT4wcZ8QxwHYkrr6/2MWAiv/vc= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= @@ -200,31 +215,38 @@ github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vb github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= -github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0= +github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= +github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= +github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= github.com/go-toolsmith/astcast v1.1.0 h1:+JN9xZV1A+Re+95pgnMgDboWNVnIMMQXwfBwLRPgSC8= github.com/go-toolsmith/astcast v1.1.0/go.mod h1:qdcuFWeGGS2xX5bLM/c3U9lewg7+Zu4mr+xPwZIB4ZU= github.com/go-toolsmith/astcopy v1.1.0 h1:YGwBN0WM+ekI/6SS6+52zLDEf8Yvp3n2seZITCUBt5s= github.com/go-toolsmith/astcopy v1.1.0/go.mod h1:hXM6gan18VA1T/daUEHCFcYiW8Ai1tIwIzHY6srfEAw= github.com/go-toolsmith/astequal v1.0.3/go.mod h1:9Ai4UglvtR+4up+bAD4+hCj7iTo4m/OXVTSLnCyTAx4= -github.com/go-toolsmith/astequal v1.1.0 h1:kHKm1AWqClYn15R0K1KKE4RG614D46n+nqUQ06E1dTw= github.com/go-toolsmith/astequal v1.1.0/go.mod h1:sedf7VIdCL22LD8qIvv7Nn9MuWJruQA/ysswh64lffQ= +github.com/go-toolsmith/astequal v1.2.0 h1:3Fs3CYZ1k9Vo4FzFhwwewC3CHISHDnVUPC4x0bI2+Cw= +github.com/go-toolsmith/astequal v1.2.0/go.mod h1:c8NZ3+kSFtFY/8lPso4v8LuJjdJiUFVnSuU3s0qrrDY= github.com/go-toolsmith/astfmt v1.1.0 h1:iJVPDPp6/7AaeLJEruMsBUlOYCmvg0MoCfJprsOmcco= github.com/go-toolsmith/astfmt v1.1.0/go.mod h1:OrcLlRwu0CuiIBp/8b5PYF9ktGVZUjlNMV634mhwuQ4= github.com/go-toolsmith/astp v1.1.0 h1:dXPuCl6u2llURjdPLLDxJeZInAeZ0/eZwFJmqZMnpQA= github.com/go-toolsmith/astp v1.1.0/go.mod h1:0T1xFGz9hicKs8Z5MfAqSUitoUYS30pDMsRVIDHs8CA= github.com/go-toolsmith/pkgload v1.2.2 h1:0CtmHq/02QhxcF7E9N5LIFcYFsMR5rdovfqTtRKkgIk= +github.com/go-toolsmith/pkgload v1.2.2/go.mod h1:R2hxLNRKuAsiXCo2i5J6ZQPhnPMOVtU+f0arbFPWCus= github.com/go-toolsmith/strparse v1.0.0/go.mod h1:YI2nUKP9YGZnL/L1/DLFBfixrcjslWct4wyljWhSRy8= github.com/go-toolsmith/strparse v1.1.0 h1:GAioeZUK9TGxnLS+qfdqNbA4z0SSm5zVNtCQiyP2Bvw= github.com/go-toolsmith/strparse v1.1.0/go.mod h1:7ksGy58fsaQkGQlY8WVoBFNyEPMGuJin1rfoPS4lBSQ= github.com/go-toolsmith/typep v1.1.0 h1:fIRYDyF+JywLfqzyhdiHzRop/GQDxxNhLGQ6gFUNHus= github.com/go-toolsmith/typep v1.1.0/go.mod h1:fVIw+7zjdsMxDA3ITWnH1yOiw1rnTQKCsF/sk2H/qig= +github.com/go-viper/mapstructure/v2 v2.0.0 h1:dhn8MZ1gZ0mzeodTG3jt5Vj/o87xZKuNAprG2mQfMfc= +github.com/go-viper/mapstructure/v2 v2.0.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= github.com/go-xmlfmt/xmlfmt v1.1.2 h1:Nea7b4icn8s57fTx1M5AI4qQT5HEM3rVUO8MuE6g80U= github.com/go-xmlfmt/xmlfmt v1.1.2/go.mod h1:aUCEOzzezBEjDBbFBoSiya/gduyIiWYRP6CnSFIV8AM= github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= -github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw= -github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= +github.com/gofrs/flock v0.12.1 h1:MTLVXXHf8ekldpJk3AKicLij9MdwOWkZ+a/jHHZby9E= +github.com/gofrs/flock v0.12.1/go.mod h1:9zxTsyu5xtJ9DK+1tFZyibEV7y3uwDxPPfbxeeHCoD0= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= @@ -254,30 +276,27 @@ github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QD github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/golangci/check v0.0.0-20180506172741-cfe4005ccda2 h1:23T5iq8rbUYlhpt5DB4XJkc6BU31uODLD1o1gKvZmD0= -github.com/golangci/check v0.0.0-20180506172741-cfe4005ccda2/go.mod h1:k9Qvh+8juN+UKMCS/3jFtGICgW8O96FVaZsaxdzDkR4= github.com/golangci/dupl v0.0.0-20180902072040-3e9179ac440a h1:w8hkcTqaFpzKqonE9uMCefW1WDie15eSP/4MssdenaM= github.com/golangci/dupl v0.0.0-20180902072040-3e9179ac440a/go.mod h1:ryS0uhF+x9jgbj/N71xsEqODy9BN81/GonCZiOzirOk= -github.com/golangci/go-misc v0.0.0-20220329215616-d24fe342adfe h1:6RGUuS7EGotKx6J5HIP8ZtyMdiDscjMLfRBSPuzVVeo= -github.com/golangci/go-misc v0.0.0-20220329215616-d24fe342adfe/go.mod h1:gjqyPShc/m8pEMpk0a3SeagVb0kaqvhscv+i9jI5ZhQ= -github.com/golangci/gofmt v0.0.0-20220901101216-f2edd75033f2 h1:amWTbTGqOZ71ruzrdA+Nx5WA3tV1N0goTspwmKCQvBY= -github.com/golangci/gofmt v0.0.0-20220901101216-f2edd75033f2/go.mod h1:9wOXstvyDRshQ9LggQuzBCGysxs3b6Uo/1MvYCR2NMs= -github.com/golangci/golangci-lint v1.52.2 h1:FrPElUUI5rrHXg1mQ7KxI1MXPAw5lBVskiz7U7a8a1A= -github.com/golangci/golangci-lint v1.52.2/go.mod h1:S5fhC5sHM5kE22/HcATKd1XLWQxX+y7mHj8B5H91Q/0= -github.com/golangci/lint-1 v0.0.0-20191013205115-297bf364a8e0 h1:MfyDlzVjl1hoaPzPD4Gpb/QgoRfSBR0jdhwGyAWwMSA= -github.com/golangci/lint-1 v0.0.0-20191013205115-297bf364a8e0/go.mod h1:66R6K6P6VWk9I95jvqGxkqJxVWGFy9XlDwLwVz1RCFg= -github.com/golangci/maligned v0.0.0-20180506175553-b1d89398deca h1:kNY3/svz5T29MYHubXix4aDDuE3RWHkPvopM/EDv/MA= -github.com/golangci/maligned v0.0.0-20180506175553-b1d89398deca/go.mod h1:tvlJhZqDe4LMs4ZHD0oMUlt9G2LWuDGoisJTBzLMV9o= -github.com/golangci/misspell v0.4.0 h1:KtVB/hTK4bbL/S6bs64rYyk8adjmh1BygbBiaAiX+a0= -github.com/golangci/misspell v0.4.0/go.mod h1:W6O/bwV6lGDxUCChm2ykw9NQdd5bYd1Xkjo88UcWyJc= -github.com/golangci/revgrep v0.0.0-20220804021717-745bb2f7c2e6 h1:DIPQnGy2Gv2FSA4B/hh8Q7xx3B7AIDk3DAMeHclH1vQ= -github.com/golangci/revgrep v0.0.0-20220804021717-745bb2f7c2e6/go.mod h1:0AKcRCkMoKvUvlf89F6O7H2LYdhr1zBh736mBItOdRs= -github.com/golangci/unconvert v0.0.0-20180507085042-28b1c447d1f4 h1:zwtduBRr5SSWhqsYNgcuWO2kFlpdOZbP0+yRjmvPGys= -github.com/golangci/unconvert v0.0.0-20180507085042-28b1c447d1f4/go.mod h1:Izgrg8RkN3rCIMLGE9CyYmU9pY2Jer6DgANEnZ/L/cQ= +github.com/golangci/gofmt v0.0.0-20231018234816-f50ced29576e h1:ULcKCDV1LOZPFxGZaA6TlQbiM3J2GCPnkx/bGF6sX/g= +github.com/golangci/gofmt v0.0.0-20231018234816-f50ced29576e/go.mod h1:Pm5KhLPA8gSnQwrQ6ukebRcapGb/BG9iUkdaiCcGHJM= +github.com/golangci/golangci-lint v1.60.1 h1:DRKNqNTQRLBJZ1il5u4fvgLQCjQc7QFs0DbhksJtVJE= +github.com/golangci/golangci-lint v1.60.1/go.mod h1:jDIPN1rYaIA+ijp9OZcUmUCoQOtZ76pOlFbi15FlLJY= +github.com/golangci/misspell v0.6.0 h1:JCle2HUTNWirNlDIAUO44hUsKhOFqGPoC4LZxlaSXDs= +github.com/golangci/misspell v0.6.0/go.mod h1:keMNyY6R9isGaSAu+4Q8NMBwMPkh15Gtc8UCVoDtAWo= +github.com/golangci/modinfo v0.3.4 h1:oU5huX3fbxqQXdfspamej74DFX0kyGLkw1ppvXoJ8GA= +github.com/golangci/modinfo v0.3.4/go.mod h1:wytF1M5xl9u0ij8YSvhkEVPP3M5Mc7XLl1pxH3B2aUM= +github.com/golangci/plugin-module-register v0.1.1 h1:TCmesur25LnyJkpsVrupv1Cdzo+2f7zX0H6Jkw1Ol6c= +github.com/golangci/plugin-module-register v0.1.1/go.mod h1:TTpqoB6KkwOJMV8u7+NyXMrkwwESJLOkfl9TxR1DGFc= +github.com/golangci/revgrep v0.5.3 h1:3tL7c1XBMtWHHqVpS5ChmiAAoe4PF/d5+ULzV9sLAzs= +github.com/golangci/revgrep v0.5.3/go.mod h1:U4R/s9dlXZsg8uJmaR1GrloUr14D7qDl8gi2iPXJH8k= +github.com/golangci/unconvert v0.0.0-20240309020433-c5143eacb3ed h1:IURFTjxeTfNFP0hTEi1YKjB/ub8zkpaOqFFMApi2EAs= +github.com/golangci/unconvert v0.0.0-20240309020433-c5143eacb3ed/go.mod h1:XLXN8bNw4CGRPaqgl3bv/lhz7bsGPh4/xSaMTbo2vkQ= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= @@ -292,12 +311,11 @@ github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= -github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= -github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= @@ -305,21 +323,18 @@ github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hf github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20240424215950-a892ee059fd6 h1:k7nVchz72niMH6YLQNvHSdIE7iqsQxK1P41mySCvssg= +github.com/google/pprof v0.0.0-20240424215950-a892ee059fd6/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= -github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= -github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= -github.com/gordonklaus/ineffassign v0.0.0-20230107090616-13ace0543b28 h1:9alfqbrhuD+9fLZ4iaAVwhlp5PEhmnBt7yvK2Oy5C1U= -github.com/gordonklaus/ineffassign v0.0.0-20230107090616-13ace0543b28/go.mod h1:Qcp2HIAYhR7mNUVSIxZww3Guk4it82ghYcEXIAk+QT0= +github.com/gordonklaus/ineffassign v0.1.0 h1:y2Gd/9I7MdY1oEIt+n+rowjBNDcLQq3RsH5hwJd0f9s= +github.com/gordonklaus/ineffassign v0.1.0/go.mod h1:Qcp2HIAYhR7mNUVSIxZww3Guk4it82ghYcEXIAk+QT0= github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gostaticanalysis/analysisutil v0.7.1 h1:ZMCjoue3DtDWQ5WyU16YbjbQEQ3VuzwxALrpYd+HeKk= github.com/gostaticanalysis/analysisutil v0.7.1/go.mod h1:v21E3hY37WKMGSnbsw2S/ojApNWb6C1//mXO48CXbVc= @@ -332,16 +347,13 @@ github.com/gostaticanalysis/nilerr v0.1.1 h1:ThE+hJP0fEp4zWLkWHWcRyI2Od0p7DlgYG3 github.com/gostaticanalysis/nilerr v0.1.1/go.mod h1:wZYb6YI5YAxxq0i1+VJbY0s2YONW0HU0GPE3+5PWN4A= github.com/gostaticanalysis/testutil v0.3.1-0.20210208050101-bfb5c8eec0e4/go.mod h1:D+FIZ+7OahH3ePw/izIEeH5I06eKs1IKI4Xr64/Am3M= github.com/gostaticanalysis/testutil v0.4.0 h1:nhdCmubdmDF6VEatUNjgUZBJKWRqugoISdUv3PPQgHY= +github.com/gostaticanalysis/testutil v0.4.0/go.mod h1:bLIoPefWXrRi/ssLFWX1dx7Repi5x3CuviD3dgAZaBU= github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= -github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA= -github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= -github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= -github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= github.com/hashicorp/go-version v1.2.1/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= -github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek= -github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/hashicorp/go-version v1.7.0 h1:5tqGy27NaOTB8yJKUZELlFAS/LTKJkrmONwQKeRZfjY= +github.com/hashicorp/go-version v1.7.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= @@ -350,19 +362,20 @@ github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUq github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= -github.com/inconshreveable/mousetrap v1.0.1 h1:U3uMjPSQEBMNp1lFxmllqCPM6P5u/Xq7Pgzkat/bFNc= -github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= +github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jessevdk/go-flags v1.4.0 h1:4IU2WS7AumrZ/40jfhf4QVDMsQwqA7VEHozFRrGARJA= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= -github.com/jgautheron/goconst v1.5.1 h1:HxVbL1MhydKs8R8n/HE5NPvzfaYmQJA3o879lE4+WcM= -github.com/jgautheron/goconst v1.5.1/go.mod h1:aAosetZ5zaeC/2EfMeRswtxUFBpe2Hr7HzkgX4fanO4= +github.com/jgautheron/goconst v1.7.1 h1:VpdAG7Ca7yvvJk5n8dMwQhfEZJh95kl/Hl9S1OI5Jkk= +github.com/jgautheron/goconst v1.7.1/go.mod h1:aAosetZ5zaeC/2EfMeRswtxUFBpe2Hr7HzkgX4fanO4= github.com/jingyugao/rowserrcheck v1.1.1 h1:zibz55j/MJtLsjP1OF4bSdgXxwL1b+Vn7Tjzq7gFzUs= github.com/jingyugao/rowserrcheck v1.1.1/go.mod h1:4yvlZSDb3IyDTUZJUmpZfm2Hwok+Dtp+nu2qOq+er9c= github.com/jirfag/go-printf-func-name v0.0.0-20200119135958-7558a9eaa5af h1:KA9BjwUk7KlCh6S9EAGWBt1oExIUv9WyNCiRz5amv48= github.com/jirfag/go-printf-func-name v0.0.0-20200119135958-7558a9eaa5af/go.mod h1:HEWGJkRDzjJY2sqdDwxccsGicWEf9BQOZsq2tV+xzM0= +github.com/jjti/go-spancheck v0.6.2 h1:iYtoxqPMzHUPp7St+5yA8+cONdyXD3ug6KK15n7Pklk= +github.com/jjti/go-spancheck v0.6.2/go.mod h1:+X7lvIrR5ZdUTkxFYqzJ0abr8Sb5LOo80uOhWNqIrYA= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/jrick/logrotate v1.0.0 h1:lQ1bL/n9mBNeIXoTUoYRlK4dHuNJVofX9oWqBtPnSzI= @@ -378,41 +391,45 @@ github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7V github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= github.com/julz/importas v0.1.0 h1:F78HnrsjY3cR7j0etXy5+TU1Zuy7Xt08X/1aJnH5xXY= github.com/julz/importas v0.1.0/go.mod h1:oSFU2R4XK/P7kNBrnL/FEQlDGN1/6WoxXEjSSXO0DV0= -github.com/junk1tm/musttag v0.5.0 h1:bV1DTdi38Hi4pG4OVWa7Kap0hi0o7EczuK6wQt9zPOM= -github.com/junk1tm/musttag v0.5.0/go.mod h1:PcR7BA+oREQYvHwgjIDmw3exJeds5JzRcvEJTfjrA0M= +github.com/karamaru-alpha/copyloopvar v1.1.0 h1:x7gNyKcC2vRBO1H2Mks5u1VxQtYvFiym7fCjIP8RPos= +github.com/karamaru-alpha/copyloopvar v1.1.0/go.mod h1:u7CIfztblY0jZLOQZgH3oYsJzpC2A7S6u/lfgSXHy0k= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= -github.com/kisielk/errcheck v1.6.3 h1:dEKh+GLHcWm2oN34nMvDzn1sqI0i0WxPvrgiJA5JuM8= -github.com/kisielk/errcheck v1.6.3/go.mod h1:nXw/i/MfnvRHqXa7XXmQMUB0oNFGuBrNI8d8NLy0LPw= -github.com/kisielk/gotool v1.0.0 h1:AV2c/EiW3KqPNT9ZKl07ehoAGi4C5/01Cfbblndcapg= +github.com/kisielk/errcheck v1.7.0 h1:+SbscKmWJ5mOK/bO1zS60F5I9WwZDWOfRsC4RwfwRV0= +github.com/kisielk/errcheck v1.7.0/go.mod h1:1kLL+jV4e+CFfueBmI1dSK2ADDyQnlrnrY/FqKluHJQ= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/kkHAIKE/contextcheck v1.1.4 h1:B6zAaLhOEEcjvUgIYEqystmnFk1Oemn8bvJhbt0GMb8= -github.com/kkHAIKE/contextcheck v1.1.4/go.mod h1:1+i/gWqokIa+dm31mqGLZhZJ7Uh44DJGZVmr6QRBNJg= +github.com/kkHAIKE/contextcheck v1.1.5 h1:CdnJh63tcDe53vG+RebdpdXJTc9atMgGqdx8LXxiilg= +github.com/kkHAIKE/contextcheck v1.1.5/go.mod h1:O930cpht4xb1YQpK+1+AgoM3mFsvxr7uyFptcnWTYUA= github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23 h1:FOOIBWrEkLgmlgGfMuZT83xIwfPDxEI2OHu6xUmJMFE= github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kulti/thelper v0.6.3 h1:ElhKf+AlItIu+xGnI990no4cE2+XaSu1ULymV2Yulxs= github.com/kulti/thelper v0.6.3/go.mod h1:DsqKShOvP40epevkFrvIwkCMNYxMeTNjdWL4dqWHZ6I= -github.com/kunwardeep/paralleltest v1.0.6 h1:FCKYMF1OF2+RveWlABsdnmsvJrei5aoyZoaGS+Ugg8g= -github.com/kunwardeep/paralleltest v1.0.6/go.mod h1:Y0Y0XISdZM5IKm3TREQMZ6iteqn1YuwCsJO/0kL9Zes= +github.com/kunwardeep/paralleltest v1.0.10 h1:wrodoaKYzS2mdNVnc4/w31YaXFtsc21PCTdvWJ/lDDs= +github.com/kunwardeep/paralleltest v1.0.10/go.mod h1:2C7s65hONVqY7Q5Efj5aLzRCNLjw2h4eMc9EcypGjcY= github.com/kyoh86/exportloopref v0.1.11 h1:1Z0bcmTypkL3Q4k+IDHMWTcnCliEZcaPiIe0/ymEyhQ= github.com/kyoh86/exportloopref v0.1.11/go.mod h1:qkV4UF1zGl6EkF1ox8L5t9SwyeBAZ3qLMd6up458uqA= -github.com/ldez/gomoddirectives v0.2.3 h1:y7MBaisZVDYmKvt9/l1mjNCiSA1BVn34U0ObUcJwlhA= -github.com/ldez/gomoddirectives v0.2.3/go.mod h1:cpgBogWITnCfRq2qGoDkKMEVSaarhdBr6g8G04uz6d0= -github.com/ldez/tagliatelle v0.4.0 h1:sylp7d9kh6AdXN2DpVGHBRb5guTVAgOxqNGhbqc4b1c= -github.com/ldez/tagliatelle v0.4.0/go.mod h1:mNtTfrHy2haaBAw+VT7IBV6VXBThS7TCreYWbBcJ87I= -github.com/leonklingele/grouper v1.1.1 h1:suWXRU57D4/Enn6pXR0QVqqWWrnJ9Osrz+5rjt8ivzU= -github.com/leonklingele/grouper v1.1.1/go.mod h1:uk3I3uDfi9B6PeUjsCKi6ndcf63Uy7snXgR4yDYQVDY= +github.com/lasiar/canonicalheader v1.1.1 h1:wC+dY9ZfiqiPwAexUApFush/csSPXeIi4QqyxXmng8I= +github.com/lasiar/canonicalheader v1.1.1/go.mod h1:cXkb3Dlk6XXy+8MVQnF23CYKWlyA7kfQhSw2CcZtZb0= +github.com/ldez/gomoddirectives v0.2.4 h1:j3YjBIjEBbqZ0NKtBNzr8rtMHTOrLPeiwTkfUJZ3alg= +github.com/ldez/gomoddirectives v0.2.4/go.mod h1:oWu9i62VcQDYp9EQ0ONTfqLNh+mDLWWDO+SO0qSQw5g= +github.com/ldez/tagliatelle v0.5.0 h1:epgfuYt9v0CG3fms0pEgIMNPuFf/LpPIfjk4kyqSioo= +github.com/ldez/tagliatelle v0.5.0/go.mod h1:rj1HmWiL1MiKQuOONhd09iySTEkUuE/8+5jtPYz9xa4= +github.com/leonklingele/grouper v1.1.2 h1:o1ARBDLOmmasUaNDesWqWCIFH3u7hoFlM84YrjT3mIY= +github.com/leonklingele/grouper v1.1.2/go.mod h1:6D0M/HVkhs2yRKRFZUoGjeDy7EZTfFBE9gl4kjmIGkA= github.com/lufeee/execinquery v1.2.1 h1:hf0Ems4SHcUGBxpGN7Jz78z1ppVkP/837ZlETPCEtOM= github.com/lufeee/execinquery v1.2.1/go.mod h1:EC7DrEKView09ocscGHC+apXMIaorh4xqSxS/dy8SbM= +github.com/macabu/inamedparam v0.1.3 h1:2tk/phHkMlEL/1GNe/Yf6kkR/hkcUdAEY3L0hjYV1Mk= +github.com/macabu/inamedparam v0.1.3/go.mod h1:93FLICAIk/quk7eaPPQvbzihUdn/QkGDwIZEoLtpH6I= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo= @@ -428,16 +445,14 @@ github.com/matryer/is v1.4.0/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwM github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= -github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng= -github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/mbilski/exhaustivestruct v1.2.0 h1:wCBmUnSYufAHO6J4AVWY6ff+oxWxsVFrwgOdMUQePUo= -github.com/mbilski/exhaustivestruct v1.2.0/go.mod h1:OeTBVxQWoEmB2J2JCHmXWPJ0aksxSUOUy+nvtVEfzXc= -github.com/mgechev/revive v1.3.1 h1:OlQkcH40IB2cGuprTPcjB0iIUddgVZgGmDX3IAMR8D4= -github.com/mgechev/revive v1.3.1/go.mod h1:YlD6TTWl2B8A103R9KWJSPVI9DrEf+oqr15q21Ld+5I= +github.com/mgechev/revive v1.3.9 h1:18Y3R4a2USSBF+QZKFQwVkBROUda7uoBlkEuBD+YD1A= +github.com/mgechev/revive v1.3.9/go.mod h1:+uxEIr5UH0TjXWHTno3xh4u7eg6jDpXKzQccA9UGhHU= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= @@ -449,21 +464,18 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= -github.com/moricho/tparallel v0.3.1 h1:fQKD4U1wRMAYNngDonW5XupoB/ZGJHdpzrWqgyg9krA= -github.com/moricho/tparallel v0.3.1/go.mod h1:leENX2cUv7Sv2qDgdi0D0fCftN8fRC67Bcn8pqzeYNI= +github.com/moricho/tparallel v0.3.2 h1:odr8aZVFA3NZrNybggMkYO3rgPRcqjeQUlBBFVxKHTI= +github.com/moricho/tparallel v0.3.2/go.mod h1:OQ+K3b4Ln3l2TZveGCywybl68glfLEwFGqvnjok8b+U= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/nakabonne/nestif v0.3.1 h1:wm28nZjhQY5HyYPx+weN3Q65k6ilSBxDb8v5S81B81U= github.com/nakabonne/nestif v0.3.1/go.mod h1:9EtoZochLn5iUprVDmDjqGKPofoUEBL8U4Ngq6aY7OE= -github.com/nbutton23/zxcvbn-go v0.0.0-20210217022336-fa2cb2858354 h1:4kuARK6Y6FxaNu/BnU2OAaLF86eTVhP2hjTB6iMvItA= -github.com/nbutton23/zxcvbn-go v0.0.0-20210217022336-fa2cb2858354/go.mod h1:KSVJerMDfblTH7p5MZaTt+8zaT2iEk3AkVb9PQdZuE8= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= -github.com/nishanths/exhaustive v0.9.5 h1:TzssWan6orBiLYVqewCG8faud9qlFntJE30ACpzmGME= -github.com/nishanths/exhaustive v0.9.5/go.mod h1:IbwrGdVMizvDcIxPYGVdQn5BqWJaOwpCvg4RGb8r/TA= +github.com/nishanths/exhaustive v0.12.0 h1:vIY9sALmw6T/yxiASewa4TQcFsVYZQQRUQJhKRf3Swg= +github.com/nishanths/exhaustive v0.12.0/go.mod h1:mEZ95wPIZW+x8kC4TgC+9YCUgiST7ecevsVDTgc2obs= github.com/nishanths/predeclared v0.2.2 h1:V2EPdZPliZymNAn79T8RkNApBjMmVKh5XRpLm/w98Vk= github.com/nishanths/predeclared v0.2.2/go.mod h1:RROzoN6TnGQupbC+lqggsOlcgysk3LMK/HI84Mp280c= -github.com/nunnatsa/ginkgolinter v0.9.0 h1:Sm0zX5QfjJzkeCjEp+t6d3Ha0jwvoDjleP9XCsrEzOA= -github.com/nunnatsa/ginkgolinter v0.9.0/go.mod h1:FHaMLURXP7qImeH6bvxWJUpyH+2tuqe5j4rW1gxJRmI= +github.com/nunnatsa/ginkgolinter v0.16.2 h1:8iLqHIZvN4fTLDC0Ke9tbSZVcyVHoBs0HIbnVSxfHJk= +github.com/nunnatsa/ginkgolinter v0.16.2/go.mod h1:4tWRinDN1FeJgU+iJANW/kz7xKN5nYRAOfJDQUS9dOQ= github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= @@ -474,18 +486,21 @@ github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+W github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= github.com/onsi/ginkgo v1.14.0 h1:2mOpI4JVVPBN+WQRa0WKH2eXR+Ey+uK4n7Zj0aYpIQA= github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= -github.com/onsi/ginkgo/v2 v2.8.0 h1:pAM+oBNPrpXRs+E/8spkeGx9QgekbRVyr74EUvRVOUI= +github.com/onsi/ginkgo/v2 v2.17.3 h1:oJcvKpIb7/8uLpDDtnQuf18xVnwKp8DTD7DQ6gTd/MU= +github.com/onsi/ginkgo/v2 v2.17.3/go.mod h1:nP2DPOQoNsQmsVyv5rDA8JkXQoCs6goXIvr/PRJ1eCc= github.com/onsi/gomega v1.4.1/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= -github.com/onsi/gomega v1.26.0 h1:03cDLK28U6hWvCAns6NeydX3zIm4SF3ci69ulidS32Q= +github.com/onsi/gomega v1.33.1 h1:dsYjIxxSR755MDmKVsaFQTE22ChNBcuuTWgkUDSubOk= +github.com/onsi/gomega v1.33.1/go.mod h1:U4R44UsT+9eLIaYRB2a5qajjtQYn0hauxvRm16AVYg0= github.com/ory/go-acc v0.2.8 h1:rOHHAPQjf0u7eHFGWpiXK+gIu/e0GRSJNr9pDukdNC4= github.com/ory/go-acc v0.2.8/go.mod h1:iCRZUdGb/7nqvSn8xWZkhfVrtXRZ9Wru2E5rabCjFPI= github.com/ory/viper v1.7.5 h1:+xVdq7SU3e1vNaCsk/ixsfxE4zylk1TJUiJrY647jUE= github.com/ory/viper v1.7.5/go.mod h1:ypOuyJmEUb3oENywQZRgeAMwqgOyDqwboO1tj3DjTaM= -github.com/otiai10/copy v1.2.0 h1:HvG945u96iNadPoG2/Ja2+AUJeW5YuFQMixq9yirC+k= github.com/otiai10/copy v1.2.0/go.mod h1:rrF5dJ5F0t/EWSYODDu4j9/vEeYHMkc8jt0zJChqQWw= +github.com/otiai10/copy v1.14.0 h1:dCI/t1iTdYGtkvCuBG2BgR6KZa83PTclw4U5n2wAllU= +github.com/otiai10/copy v1.14.0/go.mod h1:ECfuL02W+/FkTWZWgQqXPWZgW9oeKCSQ5qVfSc4qc4w= github.com/otiai10/curr v0.0.0-20150429015615-9b4961190c95/go.mod h1:9qAhocn7zKJG+0mI8eUu6xqkFDYS2kb2saOteoSB3cE= github.com/otiai10/curr v1.0.0/go.mod h1:LskTG5wDwr8Rs+nNQ+1LlxRjAtTZZjtJW4rMXl6j4vs= github.com/otiai10/mint v1.3.0/go.mod h1:F5AjcsTsWUqX+Na9fpHb52P8pcRX2CI6A3ctIT91xUo= @@ -496,17 +511,18 @@ github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/9 github.com/pelletier/go-toml v1.8.0/go.mod h1:D6yutnOGMveHEPV7VQOuvI/gXY61bv+9bAOTRnLElKs= github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= -github.com/pelletier/go-toml/v2 v2.0.5 h1:ipoSadvV8oGUjnUbMub59IDPPwfxF694nG/jwbMiyQg= -github.com/pelletier/go-toml/v2 v2.0.5/go.mod h1:OMHamSCAODeSsVrwwvcJOaoN0LIUIaFVNZzmWyNfXas= +github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM= +github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/polyfloyd/go-errorlint v1.4.0 h1:b+sQ5HibPIAjEZwtuwU8Wz/u0dMZ7YL+bk+9yWyHVJk= -github.com/polyfloyd/go-errorlint v1.4.0/go.mod h1:qJCkPeBn+0EXkdKTrUCcuFStM2xrDKfxI3MGLXPexUs= +github.com/polyfloyd/go-errorlint v1.6.0 h1:tftWV9DE7txiFzPpztTAwyoRLKNj9gpVm2cg8/OwcYY= +github.com/polyfloyd/go-errorlint v1.6.0/go.mod h1:HR7u8wuP1kb1NeN1zqTd1ZMlqUKPPHF+Id4vIPvDqVw= +github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g= +github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= @@ -534,8 +550,10 @@ github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1 github.com/prometheus/procfs v0.7.3 h1:4jVXhlkAyzOScmCkXBTOLRLTz8EeU+eyjrwB/EPq0VU= github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= -github.com/quasilyte/go-ruleguard v0.3.19 h1:tfMnabXle/HzOb5Xe9CUZYWXKfkS1KwRmZyPmD9nVcc= -github.com/quasilyte/go-ruleguard v0.3.19/go.mod h1:lHSn69Scl48I7Gt9cX3VrbsZYvYiBYszZOZW4A+oTEw= +github.com/quasilyte/go-ruleguard v0.4.2 h1:htXcXDK6/rO12kiTHKfHuqR4kr3Y4M0J0rOL6CH/BYs= +github.com/quasilyte/go-ruleguard v0.4.2/go.mod h1:GJLgqsLeo4qgavUoL8JeGFNS7qcisx3awV/w9eWTmNI= +github.com/quasilyte/go-ruleguard/dsl v0.3.22 h1:wd8zkOhSNr+I+8Qeciml08ivDt1pSXe60+5DqOpCjPE= +github.com/quasilyte/go-ruleguard/dsl v0.3.22/go.mod h1:KeCP03KrjuSO0H1kTuZQCWlQPulDV6YMIXmpQss17rU= github.com/quasilyte/gogrep v0.5.0 h1:eTKODPXbI8ffJMN+W2aE0+oL0z/nh8/5eNdiO34SOAo= github.com/quasilyte/gogrep v0.5.0/go.mod h1:Cm9lpz9NZjEoL1tgZ2OgeUKPIxL1meE7eo60Z6Sk+Ng= github.com/quasilyte/regex/syntax v0.0.0-20210819130434-b3f0c404a727 h1:TCg2WBOl980XxGFEZSS6KlBGIV0diGdySzxATTWoqaU= @@ -546,21 +564,24 @@ github.com/rinchsan/gosimports v0.1.5 h1:Z/l9lS79z0xgKC6fLJYmDdY44D0LFwo3MzaMtWv github.com/rinchsan/gosimports v0.1.5/go.mod h1:102/jU2cwf9fpa/YM9D9o4gSen2Vg8Jl80Sxctgd9N0= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= +github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= +github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/ryancurrah/gomodguard v1.3.0 h1:q15RT/pd6UggBXVBuLps8BXRvl5GPBcwVA7BJHMLuTw= -github.com/ryancurrah/gomodguard v1.3.0/go.mod h1:ggBxb3luypPEzqVtq33ee7YSN35V28XeGnid8dnni50= -github.com/ryanrolds/sqlclosecheck v0.4.0 h1:i8SX60Rppc1wRuyQjMciLqIzV3xnoHB7/tXbr6RGYNI= -github.com/ryanrolds/sqlclosecheck v0.4.0/go.mod h1:TBRRjzL31JONc9i4XMinicuo+s+E8yKZ5FN8X3G6CKQ= +github.com/ryancurrah/gomodguard v1.3.3 h1:eiSQdJVNr9KTNxY2Niij8UReSwR8Xrte3exBrAZfqpg= +github.com/ryancurrah/gomodguard v1.3.3/go.mod h1:rsKQjj4l3LXe8N344Ow7agAy5p9yjsWOtRzUMYmA0QY= +github.com/ryanrolds/sqlclosecheck v0.5.1 h1:dibWW826u0P8jNLsLN+En7+RqWWTYrjCB9fJfSfdyCU= +github.com/ryanrolds/sqlclosecheck v0.5.1/go.mod h1:2g3dUjoS6AL4huFdv6wn55WpLIDjY7ZgUR4J8HOO/XQ= github.com/sanposhiho/wastedassign/v2 v2.0.7 h1:J+6nrY4VW+gC9xFzUc+XjPD3g3wF3je/NsJFwFK7Uxc= github.com/sanposhiho/wastedassign/v2 v2.0.7/go.mod h1:KyZ0MWTwxxBmfwn33zh3k1dmsbF2ud9pAAGfoLfjhtI= +github.com/santhosh-tekuri/jsonschema/v5 v5.3.1 h1:lZUw3E0/J3roVtGQ+SCrUrg3ON6NgVqpn3+iol9aGu4= +github.com/santhosh-tekuri/jsonschema/v5 v5.3.1/go.mod h1:uToXkOrWAZ6/Oc07xWQrPOhJotwFIyu2bBVN41fcDUY= github.com/sashamelentyev/interfacebloat v1.1.0 h1:xdRdJp0irL086OyW1H/RTZTr1h/tMEOsumirXcOJqAw= github.com/sashamelentyev/interfacebloat v1.1.0/go.mod h1:+Y9yU5YdTkrNvoX0xHc84dxiN1iBi9+G8zZIhPVoNjQ= -github.com/sashamelentyev/usestdlibvars v1.23.0 h1:01h+/2Kd+NblNItNeux0veSL5cBF1jbEOPrEhDzGYq0= -github.com/sashamelentyev/usestdlibvars v1.23.0/go.mod h1:YPwr/Y1LATzHI93CqoPUN/2BzGQ/6N/cl/KwgR0B/aU= -github.com/securego/gosec/v2 v2.15.0 h1:v4Ym7FF58/jlykYmmhZ7mTm7FQvN/setNm++0fgIAtw= -github.com/securego/gosec/v2 v2.15.0/go.mod h1:VOjTrZOkUtSDt2QLSJmQBMWnvwiQPEjg0l+5juIqGk8= +github.com/sashamelentyev/usestdlibvars v1.27.0 h1:t/3jZpSXtRPRf2xr0m63i32ZrusyurIGT9E5wAvXQnI= +github.com/sashamelentyev/usestdlibvars v1.27.0/go.mod h1:9nl0jgOfHKWNFS43Ojw0i7aRoS4j6EBye3YBhmAIRF8= +github.com/securego/gosec/v2 v2.20.1-0.20240525090044-5f0084eb01a9 h1:rnO6Zp1YMQwv8AyxzuwsVohljJgp4L0ZqiCgtACsPsc= +github.com/securego/gosec/v2 v2.20.1-0.20240525090044-5f0084eb01a9/go.mod h1:dg7lPlu/xK/Ut9SedURCoZbVCR4yC7fM65DtH9/CDHs= github.com/shazow/go-diff v0.0.0-20160112020656-b6b7b6733b8c h1:W65qqJCIOVP4jpqPQ0YvHYKwcMEMVWIzWC5iNQQfBTU= github.com/shazow/go-diff v0.0.0-20160112020656-b6b7b6733b8c/go.mod h1:/PevMnwAxekIXwN8qQyfc5gl2NlkB3CQlkizAbOkeBs= github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk= @@ -569,14 +590,12 @@ github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeV github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= -github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= -github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= -github.com/sivchari/containedctx v1.0.2 h1:0hLQKpgC53OVF1VT7CeoFHk9YKstur1XOgfYIc1yrHI= -github.com/sivchari/containedctx v1.0.2/go.mod h1:PwZOeqm4/DLoJOqMSIJs3aKqXRX4YO+uXww087KZ7Bw= -github.com/sivchari/nosnakecase v1.7.0 h1:7QkpWIRMe8x25gckkFd2A5Pi6Ymo0qgr4JrhGt95do8= -github.com/sivchari/nosnakecase v1.7.0/go.mod h1:CwDzrzPea40/GB6uynrNLiorAlgFRvRbFSgJx2Gs+QY= -github.com/sivchari/tenv v1.7.1 h1:PSpuD4bu6fSmtWMxSGWcvqUUgIn7k3yOJhOIzVWn8Ak= -github.com/sivchari/tenv v1.7.1/go.mod h1:64yStXKSOxDfX47NlhVwND4dHwfZDdbp2Lyl018Icvg= +github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= +github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/sivchari/containedctx v1.0.3 h1:x+etemjbsh2fB5ewm5FeLNi5bUjK0V8n0RB+Wwfd0XE= +github.com/sivchari/containedctx v1.0.3/go.mod h1:c1RDvCbnJLtH4lLcYD/GqwiBSSf4F5Qk0xld2rBqzJ4= +github.com/sivchari/tenv v1.10.0 h1:g/hzMA+dBCKqGXgW8AV/1xIWhAvDrx0zFKNR48NFMg0= +github.com/sivchari/tenv v1.10.0/go.mod h1:tdY24masnVoZFxYrHv/nD6Tc8FbkEtAQEEziXpyMgqY= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= @@ -588,15 +607,15 @@ github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72 h1:qLC7fQah7D6K1 github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= -github.com/spf13/afero v1.8.2 h1:xehSyVa0YnHWsJ49JFljMpg1HX19V6NDZ1fkm1Xznbo= -github.com/spf13/afero v1.8.2/go.mod h1:CtAatgMJh6bJEIs48Ay/FOnkljP3WeGUG0MC1RfAqwo= +github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8= +github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w= github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU= github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= -github.com/spf13/cobra v1.6.1 h1:o94oiPyS4KD1mPy2fmcYYHHfCxLqYjJOhGsCHFZtEzA= -github.com/spf13/cobra v1.6.1/go.mod h1:IOw/AERYS7UzyrGinqmz6HLUo219MORXGxhbaJUqzrY= +github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM= +github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= @@ -613,9 +632,9 @@ github.com/stbenjam/no-sprintf-host-port v0.1.1/go.mod h1:TLhvtIvONRzdmkFiio4O8L github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= -github.com/stretchr/testify v1.1.4/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= @@ -623,45 +642,48 @@ github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5 github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= -github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/subosito/gotenv v1.4.1 h1:jyEFiXpy21Wm81FBN71l9VoMMV8H8jG+qIK3GCpY6Qs= github.com/subosito/gotenv v1.4.1/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc= -github.com/t-yuki/gocover-cobertura v0.0.0-20180217150009-aaee18c8195c h1:+aPplBwWcHBo6q9xrfWdMrT9o4kltkmmvpemgIjep/8= -github.com/t-yuki/gocover-cobertura v0.0.0-20180217150009-aaee18c8195c/go.mod h1:SbErYREK7xXdsRiigaQiQkI9McGRzYMvlKYaP3Nimdk= github.com/tdakkota/asciicheck v0.2.0 h1:o8jvnUANo0qXtnslk2d3nMKTFNlOnJjRrNcj0j9qkHM= github.com/tdakkota/asciicheck v0.2.0/go.mod h1:Qb7Y9EgjCLJGup51gDHFzbI08/gbGhL/UVhYIPWG2rg= github.com/tenntenn/modver v1.0.1 h1:2klLppGhDgzJrScMpkj9Ujy3rXPUspSjAcev9tSEBgA= github.com/tenntenn/modver v1.0.1/go.mod h1:bePIyQPb7UeioSRkw3Q0XeMhYZSMx9B8ePqg6SAMGH0= github.com/tenntenn/text/transform v0.0.0-20200319021203-7eef512accb3 h1:f+jULpRQGxTSkNYKJ51yaw6ChIqO+Je8UqsTKN/cDag= github.com/tenntenn/text/transform v0.0.0-20200319021203-7eef512accb3/go.mod h1:ON8b8w4BN/kE1EOhwT0o+d62W65a6aPw1nouo9LMgyY= -github.com/tetafro/godot v1.4.11 h1:BVoBIqAf/2QdbFmSwAWnaIqDivZdOV0ZRwEm6jivLKw= -github.com/tetafro/godot v1.4.11/go.mod h1:LR3CJpxDVGlYOWn3ZZg1PgNZdTUvzsZWu8xaEohUpn8= -github.com/timakin/bodyclose v0.0.0-20221125081123-e39cf3fc478e h1:MV6KaVu/hzByHP0UvJ4HcMGE/8a6A4Rggc/0wx2AvJo= -github.com/timakin/bodyclose v0.0.0-20221125081123-e39cf3fc478e/go.mod h1:27bSVNWSBOHm+qRp1T9qzaIpsWEP6TbUnei/43HK+PQ= +github.com/tetafro/godot v1.4.16 h1:4ChfhveiNLk4NveAZ9Pu2AN8QZ2nkUGFuadM9lrr5D0= +github.com/tetafro/godot v1.4.16/go.mod h1:2oVxTBSftRTh4+MVfUaUXR6bn2GDXCaMcOG4Dk3rfio= +github.com/timakin/bodyclose v0.0.0-20230421092635-574207250966 h1:quvGphlmUVU+nhpFa4gg4yJyTRJ13reZMDHrKwYw53M= +github.com/timakin/bodyclose v0.0.0-20230421092635-574207250966/go.mod h1:27bSVNWSBOHm+qRp1T9qzaIpsWEP6TbUnei/43HK+PQ= github.com/timonwong/loggercheck v0.9.4 h1:HKKhqrjcVj8sxL7K77beXh0adEm6DLjV/QOGeMXEVi4= github.com/timonwong/loggercheck v0.9.4/go.mod h1:caz4zlPcgvpEkXgVnAJGowHAMW2NwHaNlpS8xDbVhTg= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= -github.com/tomarrell/wrapcheck/v2 v2.8.1 h1:HxSqDSN0sAt0yJYsrcYVoEeyM4aI9yAm3KQpIXDJRhQ= -github.com/tomarrell/wrapcheck/v2 v2.8.1/go.mod h1:/n2Q3NZ4XFT50ho6Hbxg+RV1uyo2Uow/Vdm9NQcl5SE= +github.com/tomarrell/wrapcheck/v2 v2.8.3 h1:5ov+Cbhlgi7s/a42BprYoxsr73CbdMUTzE3bRDFASUs= +github.com/tomarrell/wrapcheck/v2 v2.8.3/go.mod h1:g9vNIyhb5/9TQgumxQyOEqDHsmGYcGsVMOx/xGkqdMo= github.com/tommy-muehle/go-mnd/v2 v2.5.1 h1:NowYhSdyE/1zwK9QCLeRb6USWdoif80Ie+v+yU8u1Zw= github.com/tommy-muehle/go-mnd/v2 v2.5.1/go.mod h1:WsUAkMJMYww6l/ufffCD3m+P7LEvr8TnZn9lwVDlgzw= github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= -github.com/ultraware/funlen v0.0.3 h1:5ylVWm8wsNwH5aWo9438pwvsK0QiqVuUrt9bn7S/iLA= -github.com/ultraware/funlen v0.0.3/go.mod h1:Dp4UiAus7Wdb9KUZsYWZEWiRzGuM2kXM1lPbfaF6xhA= -github.com/ultraware/whitespace v0.0.5 h1:hh+/cpIcopyMYbZNVov9iSxvJU3OYQg78Sfaqzi/CzI= -github.com/ultraware/whitespace v0.0.5/go.mod h1:aVMh/gQve5Maj9hQ/hg+F75lr/X5A89uZnzAmWSineA= -github.com/uudashr/gocognit v1.0.6 h1:2Cgi6MweCsdB6kpcVQp7EW4U23iBFQWfTXiWlyp842Y= -github.com/uudashr/gocognit v1.0.6/go.mod h1:nAIUuVBnYU7pcninia3BHOvQkpQCeO76Uscky5BOwcY= +github.com/ultraware/funlen v0.1.0 h1:BuqclbkY6pO+cvxoq7OsktIXZpgBSkYTQtmwhAK81vI= +github.com/ultraware/funlen v0.1.0/go.mod h1:XJqmOQja6DpxarLj6Jj1U7JuoS8PvL4nEqDaQhy22p4= +github.com/ultraware/whitespace v0.1.1 h1:bTPOGejYFulW3PkcrqkeQwOd6NKOOXvmGD9bo/Gk8VQ= +github.com/ultraware/whitespace v0.1.1/go.mod h1:XcP1RLD81eV4BW8UhQlpaR+SDc2givTvyI8a586WjW8= +github.com/uudashr/gocognit v1.1.3 h1:l+a111VcDbKfynh+airAy/DJQKaXh2m9vkoysMPSZyM= +github.com/uudashr/gocognit v1.1.3/go.mod h1:aKH8/e8xbTRBwjbCkwZ8qt4l2EpKXl31KMHgSS+lZ2U= +github.com/xen0n/gosmopolitan v1.2.2 h1:/p2KTnMzwRexIW8GlKawsTWOxn7UHA+jCMF/V8HHtvU= +github.com/xen0n/gosmopolitan v1.2.2/go.mod h1:7XX7Mj61uLYrj0qmeN0zi7XDon9JRAEhYQqAPLVNTeg= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= github.com/yagipy/maintidx v1.0.0 h1:h5NvIsCz+nRDapQ0exNv4aJ0yXSI0420omVANTv3GJM= github.com/yagipy/maintidx v1.0.0/go.mod h1:0qNf/I/CCZXSMhsRsrEPDZ+DkekpKLXAJfsTACwgXLk= -github.com/yeya24/promlinter v0.2.0 h1:xFKDQ82orCU5jQujdaD8stOHiv8UN68BSdn2a8u8Y3o= -github.com/yeya24/promlinter v0.2.0/go.mod h1:u54lkmBOZrpEbQQ6gox2zWKKLKu2SGe+2KOiextY+IA= +github.com/yeya24/promlinter v0.3.0 h1:JVDbMp08lVCP7Y6NP3qHroGAO6z2yGKQtS5JsjqtoFs= +github.com/yeya24/promlinter v0.3.0/go.mod h1:cDfJQQYv9uYciW60QT0eeHlFodotkYZlL+YcPQN+mW4= +github.com/ykadowak/zerologlint v0.1.5 h1:Gy/fMz1dFQN9JZTPjv1hxEk+sRWm05row04Yoolgdiw= +github.com/ykadowak/zerologlint v0.1.5/go.mod h1:KaUskqF3e/v59oPmdq1U1DnKcuHokl2/K1U4pmIELKg= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -669,19 +691,27 @@ github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9dec github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -gitlab.com/bosi/decorder v0.2.3 h1:gX4/RgK16ijY8V+BRQHAySfQAb354T7/xQpDB2n10P0= -gitlab.com/bosi/decorder v0.2.3/go.mod h1:9K1RB5+VPNQYtXtTDAzd2OEftsZb1oV0IrJrzChSdGE= +gitlab.com/bosi/decorder v0.4.2 h1:qbQaV3zgwnBZ4zPMhGLW4KZe7A7NwxEhJx39R3shffo= +gitlab.com/bosi/decorder v0.4.2/go.mod h1:muuhHoaJkA9QLcYHq4Mj8FJUwDZ+EirSHRiaTcTf6T8= +go-simpler.org/assert v0.9.0 h1:PfpmcSvL7yAnWyChSjOz6Sp6m9j5lyK8Ok9pEL31YkQ= +go-simpler.org/assert v0.9.0/go.mod h1:74Eqh5eI6vCK6Y5l3PI8ZYFXG4Sa+tkr70OIPJAUr28= +go-simpler.org/musttag v0.12.2 h1:J7lRc2ysXOq7eM8rwaTYnNrHd5JwjppzB6mScysB2Cs= +go-simpler.org/musttag v0.12.2/go.mod h1:uN1DVIasMTQKk6XSik7yrJoEysGtR2GRqvWnI9S7TYM= +go-simpler.org/sloglint v0.7.2 h1:Wc9Em/Zeuu7JYpl+oKoYOsQSy2X560aVueCW/m6IijY= +go-simpler.org/sloglint v0.7.2/go.mod h1:US+9C80ppl7VsThQclkM7BkCHQAzuz8kHLsW3ppuluo= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/automaxprocs v1.5.3 h1:kWazyxZUrS3Gs4qUpbwo5kEIMGe/DAvi5Z4tl2NW4j8= +go.uber.org/automaxprocs v1.5.3/go.mod h1:eRbA25aqJrxAbsLO0xy5jVwPt7FQnRgjW+efnwa1WM0= go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI= +go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= @@ -696,12 +726,10 @@ golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= -golang.org/x/crypto v0.5.0 h1:U/0M97KRkSFvyD/3FSmdP5W5swImpNgle/EHFhOsQPE= -golang.org/x/crypto v0.5.0/go.mod h1:NK/OQwhpMQP3MwtdjgLlYHnH9ebylxKWv3e0fK+mkQU= +golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI= +golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -712,12 +740,12 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e h1:+WEEuIdZHnUeJJmEUjyYC2gfUMj69yZXw17EnHg/otA= -golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e/go.mod h1:Kr81I6Kryrl9sr8s2FK3vxD90NdsKWRuOIl2O4CvYbA= +golang.org/x/exp v0.0.0-20240103183307-be819d1f06fc h1:ao2WRsKSzW6KuUY9IWPwWahcHCgR0s52IfwutMfEbdM= +golang.org/x/exp v0.0.0-20240103183307-be819d1f06fc/go.mod h1:iRJReGqOEeBhDZGkGbynYwcHlctCvnjTYIamk7uXpHI= golang.org/x/exp/typeparams v0.0.0-20220428152302-39d4317da171/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk= golang.org/x/exp/typeparams v0.0.0-20230203172020-98cc5a0785f9/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk= -golang.org/x/exp/typeparams v0.0.0-20230224173230-c95f2b4c22f2 h1:J74nGeMgeFnYQJN59eFwh06jX/V8g0lB7LWpjSLxtgU= -golang.org/x/exp/typeparams v0.0.0-20230224173230-c95f2b4c22f2/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk= +golang.org/x/exp/typeparams v0.0.0-20240314144324-c7f7c6466f7f h1:phY1HzDcf18Aq9A8KkmRtY9WvOFIxN8wgfvy6Zm1DV8= +golang.org/x/exp/typeparams v0.0.0-20240314144324-c7f7c6466f7f/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -730,7 +758,6 @@ golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHl golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= @@ -739,7 +766,6 @@ golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzB golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= @@ -748,8 +774,8 @@ golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91 golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI= golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.9.0 h1:KENHtAZL2y3NLMYZeHY9DW8HW8V+kQyJsY/V9JlKvCs= -golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.20.0 h1:utOm6MM3R3dnawAiJgn0y+xvuYRsm1RKM/4giyfDgV0= +golang.org/x/mod v0.20.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -785,9 +811,6 @@ golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81R golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= @@ -795,19 +818,15 @@ golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qx golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= -golang.org/x/net v0.3.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ= +golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE= +golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -821,8 +840,9 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= +golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -862,17 +882,12 @@ golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -883,22 +898,20 @@ golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220702020025-31831981b65f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.23.0 h1:YfKFowiIMvtgl1UERQoTPPToxltDeZfbj4H7dVUCwmM= +golang.org/x/sys v0.23.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= -golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA= golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -906,19 +919,17 @@ golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk= +golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= @@ -967,14 +978,7 @@ golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200820010801-b793a1359eac/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= -golang.org/x/tools v0.0.0-20201001104356-43ebab892c4c/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU= golang.org/x/tools v0.0.0-20201023174141-c8cfbd0f21e6/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.1-0.20210205202024-ef80cdb6ec6d/go.mod h1:9bzcO0MWcOuT0tm1iBGzDVPshzfwoVvREIui8C+MHqU= golang.org/x/tools v0.1.1-0.20210302220138-2ac05c832e1a/go.mod h1:9bzcO0MWcOuT0tm1iBGzDVPshzfwoVvREIui8C+MHqU= @@ -983,15 +987,13 @@ golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.8/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU= golang.org/x/tools v0.1.9/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU= golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E= -golang.org/x/tools v0.1.11/go.mod h1:SgwaegtQh8clINPpECJMqnxLv9I09HLqnW3RMqW0CA4= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA= golang.org/x/tools v0.3.0/go.mod h1:/rWhSS2+zyEVwoJf8YAX6L2f0ntZ7Kn/mGgAWcipA5k= -golang.org/x/tools v0.4.0/go.mod h1:UE5sM2OK9E/d67R0ANs2xJizIymRP5gJU295PvKXxjQ= golang.org/x/tools v0.5.0/go.mod h1:N+Kgy78s5I24c24dU8OfWNEotWjutIs8SnJvn5IDq+k= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/tools v0.7.0 h1:W4OVu8VVOaIO0yzWMNdepAulS7YfoS3Zabrm8DOXXU4= -golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s= +golang.org/x/tools v0.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24= +golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -1012,16 +1014,12 @@ google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0M google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= -google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= -google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= -google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= @@ -1051,13 +1049,6 @@ google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7Fc google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= @@ -1071,10 +1062,6 @@ google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKa google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= -google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= -google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -1087,13 +1074,14 @@ google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGj google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw= -google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= +google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= @@ -1122,16 +1110,12 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -honnef.co/go/tools v0.4.3 h1:o/n5/K5gXqk8Gozvs2cnL0F2S1/g1vcGCAx2vETjITw= -honnef.co/go/tools v0.4.3/go.mod h1:36ZgoUOrqOk1GxwHhyryEkq8FQWkUO2xGuSMhUCcdvA= -mvdan.cc/gofumpt v0.4.0 h1:JVf4NN1mIpHogBj7ABpgOyZc65/UUOkKQFkoURsz4MM= -mvdan.cc/gofumpt v0.4.0/go.mod h1:PljLOHDeZqgS8opHRKLzp2It2VBuSdteAgqUfzMTxlQ= -mvdan.cc/interfacer v0.0.0-20180901003855-c20040233aed h1:WX1yoOaKQfddO/mLzdV4wptyWgoH/6hwLs7QHTixo0I= -mvdan.cc/interfacer v0.0.0-20180901003855-c20040233aed/go.mod h1:Xkxe497xwlCKkIaQYRfC7CSLworTXY9RMqwhhCm+8Nc= -mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b h1:DxJ5nJdkhDlLok9K6qO+5290kphDJbHOQO1DFFFTeBo= -mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b/go.mod h1:2odslEg/xrtNQqCYg2/jCoyKnw3vv5biOc3JnIcYfL4= -mvdan.cc/unparam v0.0.0-20221223090309-7455f1af531d h1:3rvTIIM22r9pvXk+q3swxUQAQOxksVMGK7sml4nG57w= -mvdan.cc/unparam v0.0.0-20221223090309-7455f1af531d/go.mod h1:IeHQjmn6TOD+e4Z3RFiZMMsLVL+A96Nvptar8Fj71is= +honnef.co/go/tools v0.5.0 h1:29uoiIormS3Z6R+t56STz/oI4v+mB51TSmEOdJPgRnE= +honnef.co/go/tools v0.5.0/go.mod h1:e9irvo83WDG9/irijV44wr3tbhcFeRnfpVlRqVwpzMs= +mvdan.cc/gofumpt v0.6.0 h1:G3QvahNDmpD+Aek/bNOLrFR2XC6ZAdo62dZu65gmwGo= +mvdan.cc/gofumpt v0.6.0/go.mod h1:4L0wf+kgIPZtcCWXynNS2e6bhmj73umwnuXSZarixzA= +mvdan.cc/unparam v0.0.0-20240528143540-8a5130ca722f h1:lMpcwN6GxNbWtbpI1+xzFLSW8XzX0u72NttUGVFjO3U= +mvdan.cc/unparam v0.0.0-20240528143540-8a5130ca722f/go.mod h1:RSLa7mKKCNeTTMHBw5Hsy2rfJmd6O2ivt9Dw9ZqCQpQ= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= diff --git a/watchtower/wtclient/queue.go b/watchtower/wtclient/queue.go index 8581ea888f..ee03d288ad 100644 --- a/watchtower/wtclient/queue.go +++ b/watchtower/wtclient/queue.go @@ -323,7 +323,7 @@ func (q *DiskOverflowQueue[T]) drainInputList() { // What we do with this new item depends on what the mode of the // queue currently is. - for q.pushToActiveQueue(task) { //nolint:revive + for q.pushToActiveQueue(task) { // We retry until the task is handled or the quit // channel is closed. } diff --git a/zpay32/decode.go b/zpay32/decode.go index 05220c862b..fcfc7edfef 100644 --- a/zpay32/decode.go +++ b/zpay32/decode.go @@ -201,7 +201,7 @@ func Decode(invoice string, net *chaincfg.Params, opts ...DecodeOption) ( errStr += fmt.Sprintf(" %d,", bit) } - return nil, fmt.Errorf(strings.TrimRight(errStr, ",")) + return nil, errors.New(strings.TrimRight(errStr, ",")) } } From c849f91590b72b51ebd3c9c6b58de9ec5c4fbdbc Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Tue, 20 Aug 2024 19:01:21 +0200 Subject: [PATCH 009/218] .golangci: fix config, update new-from-rev --- .golangci.yml | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/.golangci.yml b/.golangci.yml index dd68626157..7b613e60af 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -1,18 +1,8 @@ run: - # timeout for analysis - deadline: 10m + go: "1.22.6" - # Skip autogenerated files for mobile and gRPC as well as copied code for - # internal use. - skip-files: - - "mobile\\/.*generated\\.go" - - "\\.pb\\.go$" - - "\\.pb\\.gw\\.go$" - - "internal\\/musig2v040" - - skip-dirs: - - channeldb/migration_01_to_11 - - channeldb/migration/lnwire21 + # Abort after 10 minutes. + timeout: 10m build-tags: - autopilotrpc @@ -57,7 +47,6 @@ linters-settings: - G306 # Poor file permissions used when writing to a new file. staticcheck: - go: "1.22.6" checks: ["-SA1019"] lll: @@ -219,7 +208,19 @@ linters: issues: # Only show newly introduced problems. - new-from-rev: 8c66353e4c02329abdacb5a8df29998035ec2e24 + new-from-rev: 77c7f776d5cbf9e147edc81d65ae5ba177a684e5 + + # Skip autogenerated files for mobile and gRPC as well as copied code for + # internal use. + skip-files: + - "mobile\\/.*generated\\.go" + - "\\.pb\\.go$" + - "\\.pb\\.gw\\.go$" + - "internal\\/musig2v040" + + skip-dirs: + - channeldb/migration_01_to_11 + - channeldb/migration/lnwire21 exclude-rules: # Exclude gosec from running for tests so that tests with weak randomness From 213618adccd4c77244167b998b68d67a18d95eb3 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Tue, 12 Mar 2024 10:17:19 -0400 Subject: [PATCH 010/218] lnwallet/chanfunding: rename assembler.go to interface.go In this commit, we rename the files as assembler.go houses the primary interfaces/abstractions of the package. In the rest of the codebase, this file is near uniformly called interface.go, so we rename the file to make the repo more digestible at a scan. --- lnwallet/chanfunding/{assembler.go => interface.go} | 7 +++++++ 1 file changed, 7 insertions(+) rename lnwallet/chanfunding/{assembler.go => interface.go} (96%) diff --git a/lnwallet/chanfunding/assembler.go b/lnwallet/chanfunding/interface.go similarity index 96% rename from lnwallet/chanfunding/assembler.go rename to lnwallet/chanfunding/interface.go index a694d2a2c4..ed7197973e 100644 --- a/lnwallet/chanfunding/assembler.go +++ b/lnwallet/chanfunding/interface.go @@ -4,9 +4,11 @@ import ( "time" "github.com/btcsuite/btcd/btcutil" + "github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/btcsuite/btcd/wire" "github.com/btcsuite/btcwallet/wallet" "github.com/btcsuite/btcwallet/wtxmgr" + "github.com/lightningnetwork/lnd/fn" "github.com/lightningnetwork/lnd/lnwallet/chainfee" ) @@ -119,6 +121,11 @@ type Request struct { // output. By definition, this'll also use segwit v1 (taproot) for the // funding output. Musig2 bool + + // TapscriptRoot is the root of the tapscript tree that will be used to + // create the funding output. This field will only be utilized if the + // Musig2 flag above is set to true. + TapscriptRoot fn.Option[chainhash.Hash] } // Intent is returned by an Assembler and represents the base functionality the From 1893104392a7cf9e2f682517e6a31d8f55934c7d Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Mon, 29 Apr 2024 11:24:50 +0200 Subject: [PATCH 011/218] mod: bump tlv to v1.2.6 --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index a9c43b9bb5..d1e78e485e 100644 --- a/go.mod +++ b/go.mod @@ -41,7 +41,7 @@ require ( github.com/lightningnetwork/lnd/queue v1.1.1 github.com/lightningnetwork/lnd/sqldb v1.0.3 github.com/lightningnetwork/lnd/ticker v1.1.1 - github.com/lightningnetwork/lnd/tlv v1.2.3 + github.com/lightningnetwork/lnd/tlv v1.2.6 github.com/lightningnetwork/lnd/tor v1.1.2 github.com/ltcsuite/ltcd v0.0.0-20190101042124-f37f8bf35796 github.com/miekg/dns v1.1.43 diff --git a/go.sum b/go.sum index 26f4ec0682..4332b1032d 100644 --- a/go.sum +++ b/go.sum @@ -460,8 +460,8 @@ github.com/lightningnetwork/lnd/queue v1.1.1 h1:99ovBlpM9B0FRCGYJo6RSFDlt8/vOkQQ github.com/lightningnetwork/lnd/queue v1.1.1/go.mod h1:7A6nC1Qrm32FHuhx/mi1cieAiBZo5O6l8IBIoQxvkz4= github.com/lightningnetwork/lnd/ticker v1.1.1 h1:J/b6N2hibFtC7JLV77ULQp++QLtCwT6ijJlbdiZFbSM= github.com/lightningnetwork/lnd/ticker v1.1.1/go.mod h1:waPTRAAcwtu7Ji3+3k+u/xH5GHovTsCoSVpho0KDvdA= -github.com/lightningnetwork/lnd/tlv v1.2.3 h1:If5ibokA/UoCBGuCKaY6Vn2SJU0l9uAbehCnhTZjEP8= -github.com/lightningnetwork/lnd/tlv v1.2.3/go.mod h1:zDkmqxOczP6LaLTvSFDQ1SJUfHcQRCMKFj93dn3eMB8= +github.com/lightningnetwork/lnd/tlv v1.2.6 h1:icvQG2yDr6k3ZuZzfRdG3EJp6pHurcuh3R6dg0gv/Mw= +github.com/lightningnetwork/lnd/tlv v1.2.6/go.mod h1:/CmY4VbItpOldksocmGT4lxiJqRP9oLxwSZOda2kzNQ= github.com/lightningnetwork/lnd/tor v1.1.2 h1:3zv9z/EivNFaMF89v3ciBjCS7kvCj4ZFG7XvD2Qq0/k= github.com/lightningnetwork/lnd/tor v1.1.2/go.mod h1:j7T9uJ2NLMaHwE7GiBGnpYLn4f7NRoTM6qj+ul6/ycA= github.com/ltcsuite/ltcd v0.0.0-20190101042124-f37f8bf35796 h1:sjOGyegMIhvgfq5oaue6Td+hxZuf3tDC8lAPrFldqFw= From 58ed8e751d2c855918c1a0c15f70aab75ef2f6c6 Mon Sep 17 00:00:00 2001 From: ffranr Date: Mon, 29 Apr 2024 11:28:22 +0100 Subject: [PATCH 012/218] lnwire: add type `CustomRecords` This commit introduces the `CustomRecords` type in the `lnwire` package, designed to hold arbitrary byte slices. Each entry in this map can associate with TLV type values that are greater than or equal to 65536. --- lnwire/custom_records.go | 176 ++++++++++++++++++++++++++++++ lnwire/custom_records_test.go | 198 ++++++++++++++++++++++++++++++++++ lnwire/encoding.go | 16 +++ lnwire/extra_bytes_test.go | 8 -- 4 files changed, 390 insertions(+), 8 deletions(-) create mode 100644 lnwire/custom_records.go create mode 100644 lnwire/custom_records_test.go diff --git a/lnwire/custom_records.go b/lnwire/custom_records.go new file mode 100644 index 0000000000..1e19888426 --- /dev/null +++ b/lnwire/custom_records.go @@ -0,0 +1,176 @@ +package lnwire + +import ( + "bytes" + "fmt" + "sort" + + "github.com/lightningnetwork/lnd/tlv" +) + +const ( + // MinCustomRecordsTlvType is the minimum custom records TLV type as + // defined in BOLT 01. + MinCustomRecordsTlvType = 65536 +) + +// CustomRecords stores a set of custom key/value pairs. Map keys are TLV types +// which must be greater than or equal to MinCustomRecordsTlvType. +type CustomRecords map[uint64][]byte + +// NewCustomRecords creates a new CustomRecords instance from a +// tlv.TypeMap. +func NewCustomRecords(tlvMap tlv.TypeMap) (CustomRecords, error) { + // Make comparisons in unit tests easy by returning nil if the map is + // empty. + if len(tlvMap) == 0 { + return nil, nil + } + + customRecords := make(CustomRecords, len(tlvMap)) + for k, v := range tlvMap { + customRecords[uint64(k)] = v + } + + // Validate the custom records. + err := customRecords.Validate() + if err != nil { + return nil, fmt.Errorf("custom records from tlv map "+ + "validation error: %w", err) + } + + return customRecords, nil +} + +// ParseCustomRecords creates a new CustomRecords instance from a tlv.Blob. +func ParseCustomRecords(b tlv.Blob) (CustomRecords, error) { + stream, err := tlv.NewStream() + if err != nil { + return nil, fmt.Errorf("error creating stream: %w", err) + } + + typeMap, err := stream.DecodeWithParsedTypes(bytes.NewReader(b)) + if err != nil { + return nil, fmt.Errorf("error decoding HTLC record: %w", err) + } + + return NewCustomRecords(typeMap) +} + +// Validate checks that all custom records are in the custom type range. +func (c CustomRecords) Validate() error { + if c == nil { + return nil + } + + for key := range c { + if key < MinCustomRecordsTlvType { + return fmt.Errorf("custom records entry with TLV "+ + "type below min: %d", MinCustomRecordsTlvType) + } + } + + return nil +} + +// Copy returns a copy of the custom records. +func (c CustomRecords) Copy() CustomRecords { + if c == nil { + return nil + } + + customRecords := make(CustomRecords, len(c)) + for k, v := range c { + customRecords[k] = v + } + + return customRecords +} + +// ExtendRecordProducers extends the given records slice with the custom +// records. The resultant records slice will be sorted if the given records +// slice contains TLV types greater than or equal to MinCustomRecordsTlvType. +func (c CustomRecords) ExtendRecordProducers( + producers []tlv.RecordProducer) ([]tlv.RecordProducer, error) { + + // If the custom records are nil or empty, there is nothing to do. + if len(c) == 0 { + return producers, nil + } + + // Validate the custom records. + err := c.Validate() + if err != nil { + return nil, err + } + + // Ensure that the existing records slice TLV types are not also present + // in the custom records. If they are, the resultant extended records + // slice would erroneously contain duplicate TLV types. + for _, rp := range producers { + record := rp.Record() + recordTlvType := uint64(record.Type()) + + _, foundDuplicateTlvType := c[recordTlvType] + if foundDuplicateTlvType { + return nil, fmt.Errorf("custom records contains a TLV "+ + "type that is already present in the "+ + "existing records: %d", recordTlvType) + } + } + + // Convert the custom records map to a TLV record producer slice and + // append them to the exiting records slice. + crRecords := tlv.MapToRecords(c) + for _, record := range crRecords { + r := recordProducer{record} + producers = append(producers, &r) + } + + // If the records slice which was given as an argument included TLV + // values greater than or equal to the minimum custom records TLV type + // we will sort the extended records slice to ensure that it is ordered + // correctly. + sort.Slice(producers, func(i, j int) bool { + recordI := producers[i].Record() + recordJ := producers[j].Record() + return recordI.Type() < recordJ.Type() + }) + + return producers, nil +} + +// RecordProducers returns a slice of record producers for the custom records. +func (c CustomRecords) RecordProducers() []tlv.RecordProducer { + // If the custom records are nil or empty, return an empty slice. + if len(c) == 0 { + return nil + } + + // Convert the custom records map to a TLV record producer slice. + records := tlv.MapToRecords(c) + + // Convert the records to record producers. + producers := make([]tlv.RecordProducer, len(records)) + for i, record := range records { + producers[i] = &recordProducer{record} + } + + return producers +} + +// Serialize serializes the custom records into a byte slice. +func (c CustomRecords) Serialize() ([]byte, error) { + records := tlv.MapToRecords(c) + stream, err := tlv.NewStream(records...) + if err != nil { + return nil, fmt.Errorf("error creating stream: %w", err) + } + + var b bytes.Buffer + if err := stream.Encode(&b); err != nil { + return nil, fmt.Errorf("error encoding custom records: %w", err) + } + + return b.Bytes(), nil +} diff --git a/lnwire/custom_records_test.go b/lnwire/custom_records_test.go new file mode 100644 index 0000000000..0338d0159c --- /dev/null +++ b/lnwire/custom_records_test.go @@ -0,0 +1,198 @@ +package lnwire + +import ( + "bytes" + "testing" + + "github.com/lightningnetwork/lnd/fn" + "github.com/lightningnetwork/lnd/tlv" + "github.com/stretchr/testify/require" +) + +// TestCustomRecords tests the custom records serialization and deserialization, +// as well as copying and producing records. +func TestCustomRecords(t *testing.T) { + testCases := []struct { + name string + customTypes tlv.TypeMap + expectedRecords CustomRecords + expectedErr string + }{ + { + name: "empty custom records", + customTypes: tlv.TypeMap{}, + expectedRecords: nil, + }, + { + name: "custom record with invalid type", + customTypes: tlv.TypeMap{ + 123: []byte{1, 2, 3}, + }, + expectedErr: "TLV type below min: 65536", + }, + { + name: "valid custom record", + customTypes: tlv.TypeMap{ + 65536: []byte{1, 2, 3}, + }, + expectedRecords: map[uint64][]byte{ + 65536: {1, 2, 3}, + }, + }, + { + name: "valid custom records, wrong order", + customTypes: tlv.TypeMap{ + 65537: []byte{3, 4, 5}, + 65536: []byte{1, 2, 3}, + }, + expectedRecords: map[uint64][]byte{ + 65536: {1, 2, 3}, + 65537: {3, 4, 5}, + }, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + records, err := NewCustomRecords(tc.customTypes) + + if tc.expectedErr != "" { + require.ErrorContains(t, err, tc.expectedErr) + return + } + + require.NoError(t, err) + require.Equal(t, tc.expectedRecords, records) + + // Serialize, then parse the records again. + blob, err := records.Serialize() + require.NoError(t, err) + + parsedRecords, err := ParseCustomRecords(blob) + require.NoError(t, err) + + require.Equal(t, tc.expectedRecords, parsedRecords) + + // Copy() should also return the same records. + require.Equal( + t, tc.expectedRecords, parsedRecords.Copy(), + ) + + // RecordProducers() should also allow us to serialize + // the records again. + serializedProducers := serializeRecordProducers( + t, parsedRecords.RecordProducers(), + ) + + require.Equal(t, blob, serializedProducers) + }) + } +} + +// TestCustomRecordsExtendRecordProducers tests that we can extend a slice of +// record producers with custom records. +func TestCustomRecordsExtendRecordProducers(t *testing.T) { + testCases := []struct { + name string + existingTypes map[uint64][]byte + customRecords CustomRecords + expectedResult tlv.TypeMap + expectedErr string + }{ + { + name: "normal merge", + existingTypes: map[uint64][]byte{ + 123: {3, 4, 5}, + 345: {1, 2, 3}, + }, + customRecords: CustomRecords{ + 65536: {1, 2, 3}, + }, + expectedResult: tlv.TypeMap{ + 123: {3, 4, 5}, + 345: {1, 2, 3}, + 65536: {1, 2, 3}, + }, + }, + { + name: "duplicates", + existingTypes: map[uint64][]byte{ + 123: {3, 4, 5}, + 345: {1, 2, 3}, + 65536: {1, 2, 3}, + }, + customRecords: CustomRecords{ + 65536: {1, 2, 3}, + }, + expectedErr: "contains a TLV type that is already " + + "present in the existing records: 65536", + }, + { + name: "non custom type in custom records", + existingTypes: map[uint64][]byte{ + 123: {3, 4, 5}, + 345: {1, 2, 3}, + 65536: {1, 2, 3}, + }, + customRecords: CustomRecords{ + 123: {1, 2, 3}, + }, + expectedErr: "TLV type below min: 65536", + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + nonCustomRecords := tlv.MapToRecords(tc.existingTypes) + nonCustomProducers := fn.Map( + func(r tlv.Record) tlv.RecordProducer { + return &recordProducer{r} + }, nonCustomRecords, + ) + + combined, err := tc.customRecords.ExtendRecordProducers( + nonCustomProducers, + ) + + if tc.expectedErr != "" { + require.ErrorContains(t, err, tc.expectedErr) + return + } + + require.NoError(t, err) + + serializedProducers := serializeRecordProducers( + t, combined, + ) + + stream, err := tlv.NewStream() + require.NoError(t, err) + + parsedMap, err := stream.DecodeWithParsedTypes( + bytes.NewReader(serializedProducers), + ) + require.NoError(t, err) + + require.Equal(t, tc.expectedResult, parsedMap) + }) + } +} + +// serializeRecordProducers is a helper function that serializes a slice of +// record producers into a byte slice. +func serializeRecordProducers(t *testing.T, + producers []tlv.RecordProducer) []byte { + + tlvRecords := fn.Map(func(p tlv.RecordProducer) tlv.Record { + return p.Record() + }, producers) + + stream, err := tlv.NewStream(tlvRecords...) + require.NoError(t, err) + + var b bytes.Buffer + err = stream.Encode(&b) + require.NoError(t, err) + + return b.Bytes() +} diff --git a/lnwire/encoding.go b/lnwire/encoding.go index e04b2b01d4..72000f81d1 100644 --- a/lnwire/encoding.go +++ b/lnwire/encoding.go @@ -1,5 +1,7 @@ package lnwire +import "github.com/lightningnetwork/lnd/tlv" + // QueryEncoding is an enum-like type that represents exactly how a set data is // encoded on the wire. type QueryEncoding uint8 @@ -15,3 +17,17 @@ const ( // NOTE: this should no longer be used or accepted. EncodingSortedZlib QueryEncoding = 1 ) + +// recordProducer is a simple helper struct that implements the +// tlv.RecordProducer interface. +type recordProducer struct { + record tlv.Record +} + +// Record returns the underlying record. +func (r *recordProducer) Record() tlv.Record { + return r.record +} + +// Ensure that recordProducer implements the tlv.RecordProducer interface. +var _ tlv.RecordProducer = (*recordProducer)(nil) diff --git a/lnwire/extra_bytes_test.go b/lnwire/extra_bytes_test.go index fd9f28841d..97a908bcec 100644 --- a/lnwire/extra_bytes_test.go +++ b/lnwire/extra_bytes_test.go @@ -86,14 +86,6 @@ func TestExtraOpaqueDataEncodeDecode(t *testing.T) { } } -type recordProducer struct { - record tlv.Record -} - -func (r *recordProducer) Record() tlv.Record { - return r.record -} - // TestExtraOpaqueDataPackUnpackRecords tests that we're able to pack a set of // tlv.Records into a stream, and unpack them on the other side to obtain the // same set of records. From cc2b7b6fda5b9fc4c57b56ef2adf1bb0b22a0c51 Mon Sep 17 00:00:00 2001 From: ffranr Date: Mon, 8 Apr 2024 14:40:50 +0100 Subject: [PATCH 013/218] multi: improve comment grammar --- htlcswitch/interfaces.go | 2 +- lnrpc/routerrpc/forward_interceptor.go | 4 ++-- lnrpc/routerrpc/router_server.go | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/htlcswitch/interfaces.go b/htlcswitch/interfaces.go index 1311373a17..246c76902a 100644 --- a/htlcswitch/interfaces.go +++ b/htlcswitch/interfaces.go @@ -331,7 +331,7 @@ type InterceptableHtlcForwarder interface { type ForwardInterceptor func(InterceptedPacket) error // InterceptedPacket contains the relevant information for the interceptor about -// an htlc. +// an HTLC. type InterceptedPacket struct { // IncomingCircuit contains the incoming channel and htlc id of the // packet. diff --git a/lnrpc/routerrpc/forward_interceptor.go b/lnrpc/routerrpc/forward_interceptor.go index 8af1a21f6c..55c77d6d9b 100644 --- a/lnrpc/routerrpc/forward_interceptor.go +++ b/lnrpc/routerrpc/forward_interceptor.go @@ -22,7 +22,7 @@ var ( ErrMissingPreimage = errors.New("missing preimage") ) -// forwardInterceptor is a helper struct that handles the lifecycle of an rpc +// forwardInterceptor is a helper struct that handles the lifecycle of an RPC // interceptor streaming session. // It is created when the stream opens and disconnects when the stream closes. type forwardInterceptor struct { @@ -43,7 +43,7 @@ func newForwardInterceptor(htlcSwitch htlcswitch.InterceptableHtlcForwarder, } // run sends the intercepted packets to the client and receives the -// corersponding responses. On one hand it registered itself as an interceptor +// corresponding responses. On one hand it registered itself as an interceptor // that receives the switch packets and on the other hand launches a go routine // to read from the client stream. // To coordinate all this and make sure it is safe for concurrent access all diff --git a/lnrpc/routerrpc/router_server.go b/lnrpc/routerrpc/router_server.go index a88bb4d0cf..044c03ac70 100644 --- a/lnrpc/routerrpc/router_server.go +++ b/lnrpc/routerrpc/router_server.go @@ -1525,7 +1525,7 @@ func (s *Server) HtlcInterceptor(stream Router_HtlcInterceptorServer) error { } defer atomic.CompareAndSwapInt32(&s.forwardInterceptorActive, 1, 0) - // run the forward interceptor. + // Run the forward interceptor. return newForwardInterceptor( s.cfg.RouterBackend.InterceptableForwarder, stream, ).run() From b59ecc9270d93701f80f5d0b370b28fed799fc58 Mon Sep 17 00:00:00 2001 From: ffranr Date: Tue, 9 Apr 2024 14:13:09 +0100 Subject: [PATCH 014/218] htlcswitch: add missing method doc --- htlcswitch/interceptable_switch.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/htlcswitch/interceptable_switch.go b/htlcswitch/interceptable_switch.go index 0ac19a36cd..5517eb82dd 100644 --- a/htlcswitch/interceptable_switch.go +++ b/htlcswitch/interceptable_switch.go @@ -387,6 +387,8 @@ func (s *InterceptableSwitch) setInterceptor(interceptor ForwardInterceptor) { }) } +// resolve processes a HTLC given the resolution type specified by the +// intercepting client. func (s *InterceptableSwitch) resolve(res *FwdResolution) error { intercepted, err := s.heldHtlcSet.pop(res.Key) if err != nil { From cd5fa5976ac99e60450332a0ff376406962dcd74 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Thu, 4 Apr 2024 16:41:16 -0700 Subject: [PATCH 015/218] funding: add new type alias for PendingChanID = [32]byte This'll be useful for new interface definitions that use the contents of the package. --- funding/manager.go | 36 +++++++++++++++++++++--------------- 1 file changed, 21 insertions(+), 15 deletions(-) diff --git a/funding/manager.go b/funding/manager.go index 8ad16005c7..a19901e29b 100644 --- a/funding/manager.go +++ b/funding/manager.go @@ -287,7 +287,7 @@ type InitFundingMsg struct { // PendingChanID is not all zeroes (the default value), then this will // be the pending channel ID used for the funding flow within the wire // protocol. - PendingChanID [32]byte + PendingChanID PendingChanID // ChannelType allows the caller to use an explicit channel type for the // funding negotiation. This type will only be observed if BOTH sides @@ -317,7 +317,7 @@ type fundingMsg struct { // pendingChannels is a map instantiated per-peer which tracks all active // pending single funded channels indexed by their pending channel identifier, // which is a set of 32-bytes generated via a CSPRNG. -type pendingChannels map[[32]byte]*reservationWithCtx +type pendingChannels map[PendingChanID]*reservationWithCtx // serializedPubKey is used within the FundingManager's activeReservations list // to identify the nodes with which the FundingManager is actively working to @@ -591,7 +591,7 @@ type Manager struct { // required as mid funding flow, we switch to referencing the channel // by its full channel ID once the commitment transactions have been // signed by both parties. - signedReservations map[lnwire.ChannelID][32]byte + signedReservations map[lnwire.ChannelID]PendingChanID // resMtx guards both of the maps above to ensure that all access is // goroutine safe. @@ -798,9 +798,13 @@ func (f *Manager) rebroadcastFundingTx(c *channeldb.OpenChannel) { } } +// PendingChanID is a type that represents a pending channel ID. This might be +// selected by the caller, but if not, will be automatically selected. +type PendingChanID = [32]byte + // nextPendingChanID returns the next free pending channel ID to be used to // identify a particular future channel funding workflow. -func (f *Manager) nextPendingChanID() [32]byte { +func (f *Manager) nextPendingChanID() PendingChanID { // Obtain a fresh nonce. We do this by encoding the current nonce // counter, then incrementing it by one. f.nonceMtx.Lock() @@ -812,7 +816,7 @@ func (f *Manager) nextPendingChanID() [32]byte { // We'll generate the next pending channelID by "encrypting" 32-bytes // of zeroes which'll extract 32 random bytes from our stream cipher. var ( - nextChanID [32]byte + nextChanID PendingChanID zeroes [32]byte ) salsa20.XORKeyStream(nextChanID[:], zeroes[:], nonce[:], &f.chanIDKey) @@ -1045,7 +1049,8 @@ func (f *Manager) reservationCoordinator() { // // NOTE: This MUST be run as a goroutine. func (f *Manager) advanceFundingState(channel *channeldb.OpenChannel, - pendingChanID [32]byte, updateChan chan<- *lnrpc.OpenStatusUpdate) { + pendingChanID PendingChanID, + updateChan chan<- *lnrpc.OpenStatusUpdate) { defer f.wg.Done() @@ -1115,7 +1120,7 @@ func (f *Manager) advanceFundingState(channel *channeldb.OpenChannel, // updateChan can be set non-nil to get OpenStatusUpdates. func (f *Manager) stateStep(channel *channeldb.OpenChannel, lnChannel *lnwallet.LightningChannel, - shortChanID *lnwire.ShortChannelID, pendingChanID [32]byte, + shortChanID *lnwire.ShortChannelID, pendingChanID PendingChanID, channelState channelOpeningState, updateChan chan<- *lnrpc.OpenStatusUpdate) error { @@ -1239,7 +1244,7 @@ func (f *Manager) stateStep(channel *channeldb.OpenChannel, // advancePendingChannelState waits for a pending channel's funding tx to // confirm, and marks it open in the database when that happens. func (f *Manager) advancePendingChannelState( - channel *channeldb.OpenChannel, pendingChanID [32]byte) error { + channel *channeldb.OpenChannel, pendingChanID PendingChanID) error { if channel.IsZeroConf() { // Persist the alias to the alias database. @@ -2770,7 +2775,7 @@ type confirmedChannel struct { // channel as closed. The error is only returned for the responder of the // channel flow. func (f *Manager) fundingTimeout(c *channeldb.OpenChannel, - pendingID [32]byte) error { + pendingID PendingChanID) error { // We'll get a timeout if the number of blocks mined since the channel // was initiated reaches MaxWaitNumBlocksFundingConf and we are not the @@ -3995,7 +4000,7 @@ func (f *Manager) handleChannelReady(peer lnpeer.Peer, //nolint:funlen // channel is now active, thus we change its state to `addedToGraph` to // let the channel start handling routing. func (f *Manager) handleChannelReadyReceived(channel *channeldb.OpenChannel, - scid *lnwire.ShortChannelID, pendingChanID [32]byte, + scid *lnwire.ShortChannelID, pendingChanID PendingChanID, updateChan chan<- *lnrpc.OpenStatusUpdate) error { chanID := lnwire.NewChanIDFromOutPoint(channel.FundingOutpoint) @@ -4519,7 +4524,7 @@ func (f *Manager) handleInitFundingMsg(msg *InitFundingMsg) { // If the caller specified their own channel ID, then we'll use that. // Otherwise we'll generate a fresh one as normal. This will be used // to track this reservation throughout its lifetime. - var chanID [32]byte + var chanID PendingChanID if msg.PendingChanID == zeroID { chanID = f.nextPendingChanID() } else { @@ -4942,7 +4947,8 @@ func (f *Manager) pruneZombieReservations() { // cancelReservationCtx does all needed work in order to securely cancel the // reservation. func (f *Manager) cancelReservationCtx(peerKey *btcec.PublicKey, - pendingChanID [32]byte, byRemote bool) (*reservationWithCtx, error) { + pendingChanID PendingChanID, + byRemote bool) (*reservationWithCtx, error) { log.Infof("Cancelling funding reservation for node_key=%x, "+ "chan_id=%x", peerKey.SerializeCompressed(), pendingChanID[:]) @@ -4990,7 +4996,7 @@ func (f *Manager) cancelReservationCtx(peerKey *btcec.PublicKey, // deleteReservationCtx deletes the reservation uniquely identified by the // target public key of the peer, and the specified pending channel ID. func (f *Manager) deleteReservationCtx(peerKey *btcec.PublicKey, - pendingChanID [32]byte) { + pendingChanID PendingChanID) { peerIDKey := newSerializedKey(peerKey) f.resMtx.Lock() @@ -5013,7 +5019,7 @@ func (f *Manager) deleteReservationCtx(peerKey *btcec.PublicKey, // getReservationCtx returns the reservation context for a particular pending // channel ID for a target peer. func (f *Manager) getReservationCtx(peerKey *btcec.PublicKey, - pendingChanID [32]byte) (*reservationWithCtx, error) { + pendingChanID PendingChanID) (*reservationWithCtx, error) { peerIDKey := newSerializedKey(peerKey) f.resMtx.RLock() @@ -5033,7 +5039,7 @@ func (f *Manager) getReservationCtx(peerKey *btcec.PublicKey, // of being funded. After the funding transaction has been confirmed, the // channel will receive a new, permanent channel ID, and will no longer be // considered pending. -func (f *Manager) IsPendingChannel(pendingChanID [32]byte, +func (f *Manager) IsPendingChannel(pendingChanID PendingChanID, peer lnpeer.Peer) bool { peerIDKey := newSerializedKey(peer.IdentityKey()) From 41ca01a1b72a1ea5185314578169fe48c6bbc09e Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Thu, 4 Apr 2024 16:43:53 -0700 Subject: [PATCH 016/218] funding: use atomic.Uint64 for chanIDNonce This lets us get rid of the mutex usage there. We also shift the algo slightly to increment by 1, then use that as the next value, which plays nicer with the atomics. --- funding/manager.go | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/funding/manager.go b/funding/manager.go index a19901e29b..61b03200e1 100644 --- a/funding/manager.go +++ b/funding/manager.go @@ -6,6 +6,7 @@ import ( "fmt" "io" "sync" + "sync/atomic" "time" "github.com/btcsuite/btcd/blockchain" @@ -568,8 +569,10 @@ type Manager struct { // chanIDNonce is a nonce that's incremented for each new funding // reservation created. - nonceMtx sync.RWMutex - chanIDNonce uint64 + chanIDNonce atomic.Uint64 + + // nonceMtx is a mutex that guards the pendingMusigNonces. + nonceMtx sync.RWMutex // pendingMusigNonces is used to store the musig2 nonce we generate to // send funding locked until we receive a funding locked message from @@ -805,13 +808,11 @@ type PendingChanID = [32]byte // nextPendingChanID returns the next free pending channel ID to be used to // identify a particular future channel funding workflow. func (f *Manager) nextPendingChanID() PendingChanID { - // Obtain a fresh nonce. We do this by encoding the current nonce - // counter, then incrementing it by one. - f.nonceMtx.Lock() - var nonce [8]byte - binary.LittleEndian.PutUint64(nonce[:], f.chanIDNonce) - f.chanIDNonce++ - f.nonceMtx.Unlock() + // Obtain a fresh nonce. We do this by encoding the incremented nonce. + nextNonce := f.chanIDNonce.Add(1) + + var nonceBytes [8]byte + binary.LittleEndian.PutUint64(nonceBytes[:], nextNonce) // We'll generate the next pending channelID by "encrypting" 32-bytes // of zeroes which'll extract 32 random bytes from our stream cipher. @@ -819,7 +820,9 @@ func (f *Manager) nextPendingChanID() PendingChanID { nextChanID PendingChanID zeroes [32]byte ) - salsa20.XORKeyStream(nextChanID[:], zeroes[:], nonce[:], &f.chanIDKey) + salsa20.XORKeyStream( + nextChanID[:], zeroes[:], nonceBytes[:], &f.chanIDKey, + ) return nextChanID } From 90470065baffc5575e329b76627049b0557fddc6 Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Fri, 23 Aug 2024 10:57:59 +0200 Subject: [PATCH 017/218] cmd/lncli: move commands and export We want to export some of our CLI code to re-use in other projects. But in Golang you cannot import code from a `main` package. So we need to move the actual code into its own package and only have the `func main()` in the `main` package. --- .golangci.yml | 4 +- cmd/{lncli => commands}/arg_parse.go | 2 +- cmd/{lncli => commands}/arg_parse_test.go | 2 +- .../autopilotrpc_active.go | 2 +- .../autopilotrpc_default.go | 2 +- cmd/{lncli => commands}/chainrpc_active.go | 2 +- cmd/{lncli => commands}/chainrpc_default.go | 2 +- cmd/{lncli => commands}/cmd_custom.go | 2 +- cmd/{lncli => commands}/cmd_debug.go | 2 +- .../cmd_import_mission_control.go | 2 +- cmd/{lncli => commands}/cmd_invoice.go | 4 +- cmd/{lncli => commands}/cmd_macaroon.go | 2 +- .../cmd_mission_control.go | 4 +- cmd/{lncli => commands}/cmd_open_channel.go | 2 +- cmd/{lncli => commands}/cmd_payments.go | 86 ++- cmd/{lncli => commands}/cmd_profile.go | 2 +- cmd/{lncli => commands}/cmd_state.go | 2 +- .../cmd_update_chan_status.go | 2 +- cmd/{lncli => commands}/cmd_version.go | 2 +- cmd/{lncli => commands}/cmd_walletunlocker.go | 2 +- cmd/{lncli => commands}/commands.go | 76 ++- cmd/{lncli => commands}/commands_test.go | 81 ++- cmd/{lncli => commands}/devrpc_active.go | 2 +- cmd/{lncli => commands}/devrpc_default.go | 2 +- cmd/{lncli => commands}/invoicesrpc_active.go | 2 +- .../invoicesrpc_default.go | 2 +- cmd/{lncli => commands}/macaroon_jar.go | 2 +- cmd/{lncli => commands}/macaroon_jar_test.go | 2 +- cmd/commands/main.go | 601 ++++++++++++++++++ cmd/{lncli => commands}/neutrino_active.go | 2 +- cmd/{lncli => commands}/neutrino_default.go | 2 +- cmd/{lncli => commands}/peersrpc_active.go | 2 +- cmd/{lncli => commands}/peersrpc_default.go | 2 +- cmd/{lncli => commands}/profile.go | 2 +- cmd/{lncli => commands}/routerrpc.go | 2 +- cmd/{lncli => commands}/types.go | 2 +- cmd/{lncli => commands}/walletrpc_active.go | 2 +- cmd/{lncli => commands}/walletrpc_default.go | 2 +- cmd/{lncli => commands}/walletrpc_types.go | 2 +- cmd/{lncli => commands}/watchtower_active.go | 2 +- cmd/{lncli => commands}/watchtower_default.go | 2 +- cmd/{lncli => commands}/wtclient.go | 2 +- cmd/lncli/main.go | 589 +---------------- 43 files changed, 841 insertions(+), 674 deletions(-) rename cmd/{lncli => commands}/arg_parse.go (98%) rename cmd/{lncli => commands}/arg_parse_test.go (99%) rename cmd/{lncli => commands}/autopilotrpc_active.go (99%) rename cmd/{lncli => commands}/autopilotrpc_default.go (92%) rename cmd/{lncli => commands}/chainrpc_active.go (99%) rename cmd/{lncli => commands}/chainrpc_default.go (91%) rename cmd/{lncli => commands}/cmd_custom.go (98%) rename cmd/{lncli => commands}/cmd_debug.go (99%) rename cmd/{lncli => commands}/cmd_import_mission_control.go (99%) rename cmd/{lncli => commands}/cmd_invoice.go (99%) rename cmd/{lncli => commands}/cmd_macaroon.go (99%) rename cmd/{lncli => commands}/cmd_mission_control.go (99%) rename cmd/{lncli => commands}/cmd_open_channel.go (99%) rename cmd/{lncli => commands}/cmd_payments.go (95%) rename cmd/{lncli => commands}/cmd_profile.go (99%) rename cmd/{lncli => commands}/cmd_state.go (98%) rename cmd/{lncli => commands}/cmd_update_chan_status.go (99%) rename cmd/{lncli => commands}/cmd_version.go (98%) rename cmd/{lncli => commands}/cmd_walletunlocker.go (99%) rename cmd/{lncli => commands}/commands.go (97%) rename cmd/{lncli => commands}/commands_test.go (58%) rename cmd/{lncli => commands}/devrpc_active.go (98%) rename cmd/{lncli => commands}/devrpc_default.go (90%) rename cmd/{lncli => commands}/invoicesrpc_active.go (99%) rename cmd/{lncli => commands}/invoicesrpc_default.go (92%) rename cmd/{lncli => commands}/macaroon_jar.go (99%) rename cmd/{lncli => commands}/macaroon_jar_test.go (99%) create mode 100644 cmd/commands/main.go rename cmd/{lncli => commands}/neutrino_active.go (99%) rename cmd/{lncli => commands}/neutrino_default.go (92%) rename cmd/{lncli => commands}/peersrpc_active.go (99%) rename cmd/{lncli => commands}/peersrpc_default.go (91%) rename cmd/{lncli => commands}/profile.go (99%) rename cmd/{lncli => commands}/routerrpc.go (95%) rename cmd/{lncli => commands}/types.go (99%) rename cmd/{lncli => commands}/walletrpc_active.go (99%) rename cmd/{lncli => commands}/walletrpc_default.go (91%) rename cmd/{lncli => commands}/walletrpc_types.go (99%) rename cmd/{lncli => commands}/watchtower_active.go (98%) rename cmd/{lncli => commands}/watchtower_default.go (92%) rename cmd/{lncli => commands}/wtclient.go (99%) diff --git a/.golangci.yml b/.golangci.yml index 7b613e60af..8114945c6f 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -261,8 +261,8 @@ issues: - forbidigo - godot - # Allow fmt.Printf() in lncli. - - path: cmd/lncli/* + # Allow fmt.Printf() in commands. + - path: cmd/commands/* linters: - forbidigo diff --git a/cmd/lncli/arg_parse.go b/cmd/commands/arg_parse.go similarity index 98% rename from cmd/lncli/arg_parse.go rename to cmd/commands/arg_parse.go index 49d165d556..045f35509a 100644 --- a/cmd/lncli/arg_parse.go +++ b/cmd/commands/arg_parse.go @@ -1,4 +1,4 @@ -package main +package commands import ( "regexp" diff --git a/cmd/lncli/arg_parse_test.go b/cmd/commands/arg_parse_test.go similarity index 99% rename from cmd/lncli/arg_parse_test.go rename to cmd/commands/arg_parse_test.go index 571292d2c6..ead411fe61 100644 --- a/cmd/lncli/arg_parse_test.go +++ b/cmd/commands/arg_parse_test.go @@ -1,4 +1,4 @@ -package main +package commands import ( "testing" diff --git a/cmd/lncli/autopilotrpc_active.go b/cmd/commands/autopilotrpc_active.go similarity index 99% rename from cmd/lncli/autopilotrpc_active.go rename to cmd/commands/autopilotrpc_active.go index 961e859947..212ef45797 100644 --- a/cmd/lncli/autopilotrpc_active.go +++ b/cmd/commands/autopilotrpc_active.go @@ -1,7 +1,7 @@ //go:build autopilotrpc // +build autopilotrpc -package main +package commands import ( "github.com/lightningnetwork/lnd/lnrpc/autopilotrpc" diff --git a/cmd/lncli/autopilotrpc_default.go b/cmd/commands/autopilotrpc_default.go similarity index 92% rename from cmd/lncli/autopilotrpc_default.go rename to cmd/commands/autopilotrpc_default.go index 7fb8852170..393b6f124f 100644 --- a/cmd/lncli/autopilotrpc_default.go +++ b/cmd/commands/autopilotrpc_default.go @@ -1,7 +1,7 @@ //go:build !autopilotrpc // +build !autopilotrpc -package main +package commands import "github.com/urfave/cli" diff --git a/cmd/lncli/chainrpc_active.go b/cmd/commands/chainrpc_active.go similarity index 99% rename from cmd/lncli/chainrpc_active.go rename to cmd/commands/chainrpc_active.go index 48946e0d5d..0f1f8b6121 100644 --- a/cmd/lncli/chainrpc_active.go +++ b/cmd/commands/chainrpc_active.go @@ -1,7 +1,7 @@ //go:build chainrpc // +build chainrpc -package main +package commands import ( "bytes" diff --git a/cmd/lncli/chainrpc_default.go b/cmd/commands/chainrpc_default.go similarity index 91% rename from cmd/lncli/chainrpc_default.go rename to cmd/commands/chainrpc_default.go index fa1ea99e2c..28440a839e 100644 --- a/cmd/lncli/chainrpc_default.go +++ b/cmd/commands/chainrpc_default.go @@ -1,7 +1,7 @@ //go:build !chainrpc // +build !chainrpc -package main +package commands import "github.com/urfave/cli" diff --git a/cmd/lncli/cmd_custom.go b/cmd/commands/cmd_custom.go similarity index 98% rename from cmd/lncli/cmd_custom.go rename to cmd/commands/cmd_custom.go index 7ff5d8a71e..728d70bd39 100644 --- a/cmd/lncli/cmd_custom.go +++ b/cmd/commands/cmd_custom.go @@ -1,4 +1,4 @@ -package main +package commands import ( "encoding/hex" diff --git a/cmd/lncli/cmd_debug.go b/cmd/commands/cmd_debug.go similarity index 99% rename from cmd/lncli/cmd_debug.go rename to cmd/commands/cmd_debug.go index 758bff576d..37024f5ecf 100644 --- a/cmd/lncli/cmd_debug.go +++ b/cmd/commands/cmd_debug.go @@ -1,4 +1,4 @@ -package main +package commands import ( "bytes" diff --git a/cmd/lncli/cmd_import_mission_control.go b/cmd/commands/cmd_import_mission_control.go similarity index 99% rename from cmd/lncli/cmd_import_mission_control.go rename to cmd/commands/cmd_import_mission_control.go index 420396322f..ac15b2f1f4 100644 --- a/cmd/lncli/cmd_import_mission_control.go +++ b/cmd/commands/cmd_import_mission_control.go @@ -1,4 +1,4 @@ -package main +package commands import ( "context" diff --git a/cmd/lncli/cmd_invoice.go b/cmd/commands/cmd_invoice.go similarity index 99% rename from cmd/lncli/cmd_invoice.go rename to cmd/commands/cmd_invoice.go index a7ed2b8b2e..cd806b7003 100644 --- a/cmd/lncli/cmd_invoice.go +++ b/cmd/commands/cmd_invoice.go @@ -1,4 +1,4 @@ -package main +package commands import ( "encoding/hex" @@ -9,7 +9,7 @@ import ( "github.com/urfave/cli" ) -var addInvoiceCommand = cli.Command{ +var AddInvoiceCommand = cli.Command{ Name: "addinvoice", Category: "Invoices", Usage: "Add a new invoice.", diff --git a/cmd/lncli/cmd_macaroon.go b/cmd/commands/cmd_macaroon.go similarity index 99% rename from cmd/lncli/cmd_macaroon.go rename to cmd/commands/cmd_macaroon.go index deea4e7ad3..149c7db453 100644 --- a/cmd/lncli/cmd_macaroon.go +++ b/cmd/commands/cmd_macaroon.go @@ -1,4 +1,4 @@ -package main +package commands import ( "bytes" diff --git a/cmd/lncli/cmd_mission_control.go b/cmd/commands/cmd_mission_control.go similarity index 99% rename from cmd/lncli/cmd_mission_control.go rename to cmd/commands/cmd_mission_control.go index 323acdff6d..fe4acb25cf 100644 --- a/cmd/lncli/cmd_mission_control.go +++ b/cmd/commands/cmd_mission_control.go @@ -1,4 +1,4 @@ -package main +package commands import ( "fmt" @@ -265,6 +265,7 @@ func setCfg(ctx *cli.Context) error { Config: mcCfg.Config, }, ) + return err } @@ -366,5 +367,6 @@ func resetMissionControl(ctx *cli.Context) error { req := &routerrpc.ResetMissionControlRequest{} _, err := client.ResetMissionControl(ctxc, req) + return err } diff --git a/cmd/lncli/cmd_open_channel.go b/cmd/commands/cmd_open_channel.go similarity index 99% rename from cmd/lncli/cmd_open_channel.go rename to cmd/commands/cmd_open_channel.go index f0585ed47a..b4fe83f20b 100644 --- a/cmd/lncli/cmd_open_channel.go +++ b/cmd/commands/cmd_open_channel.go @@ -1,4 +1,4 @@ -package main +package commands import ( "bytes" diff --git a/cmd/lncli/cmd_payments.go b/cmd/commands/cmd_payments.go similarity index 95% rename from cmd/lncli/cmd_payments.go rename to cmd/commands/cmd_payments.go index 228dd3415f..e28d5e8c99 100644 --- a/cmd/lncli/cmd_payments.go +++ b/cmd/commands/cmd_payments.go @@ -1,4 +1,4 @@ -package main +package commands import ( "bytes" @@ -25,6 +25,7 @@ import ( "github.com/lightningnetwork/lnd/record" "github.com/lightningnetwork/lnd/routing/route" "github.com/urfave/cli" + "google.golang.org/grpc" ) const ( @@ -152,8 +153,8 @@ var ( } ) -// paymentFlags returns common flags for sendpayment and payinvoice. -func paymentFlags() []cli.Flag { +// PaymentFlags returns common flags for sendpayment and payinvoice. +func PaymentFlags() []cli.Flag { return []cli.Flag{ cli.StringFlag{ Name: "pay_req", @@ -202,7 +203,7 @@ func paymentFlags() []cli.Flag { } } -var sendPaymentCommand = cli.Command{ +var SendPaymentCommand = cli.Command{ Name: "sendpayment", Category: "Payments", Usage: "Send a payment over lightning.", @@ -226,7 +227,7 @@ var sendPaymentCommand = cli.Command{ `, ArgsUsage: "dest amt payment_hash final_cltv_delta pay_addr | " + "--pay_req=R [--pay_addr=H]", - Flags: append(paymentFlags(), + Flags: append(PaymentFlags(), cli.StringFlag{ Name: "dest, d", Usage: "the compressed identity pubkey of the " + @@ -253,7 +254,7 @@ var sendPaymentCommand = cli.Command{ Usage: "will generate a pre-image and encode it in the sphinx packet, a dest must be set [experimental]", }, ), - Action: sendPayment, + Action: SendPayment, } // retrieveFeeLimit retrieves the fee limit based on the different fee limit @@ -324,13 +325,16 @@ func parsePayAddr(ctx *cli.Context, args cli.Args) ([]byte, error) { return payAddr, nil } -func sendPayment(ctx *cli.Context) error { +func SendPayment(ctx *cli.Context) error { // Show command help if no arguments provided if ctx.NArg() == 0 && ctx.NumFlags() == 0 { _ = cli.ShowCommandHelp(ctx, "sendpayment") return nil } + conn := getClientConn(ctx, false) + defer conn.Close() + args := ctx.Args() // If a payment request was provided, we can exit early since all of the @@ -357,7 +361,9 @@ func sendPayment(ctx *cli.Context) error { req.PaymentAddr = payAddr - return sendPaymentRequest(ctx, req) + return SendPaymentRequest( + ctx, req, conn, conn, routerRPCSendPayment, + ) } var ( @@ -466,19 +472,29 @@ func sendPayment(ctx *cli.Context) error { req.PaymentAddr = payAddr - return sendPaymentRequest(ctx, req) + return SendPaymentRequest(ctx, req, conn, conn, routerRPCSendPayment) } -func sendPaymentRequest(ctx *cli.Context, - req *routerrpc.SendPaymentRequest) error { +// SendPaymentFn is a function type that abstracts the SendPaymentV2 call of the +// router client. +type SendPaymentFn func(ctx context.Context, payConn grpc.ClientConnInterface, + req *routerrpc.SendPaymentRequest) (PaymentResultStream, error) - ctxc := getContext() +// routerRPCSendPayment is the default implementation of the SendPaymentFn type +// that uses the lnd routerrpc.SendPaymentV2 call. +func routerRPCSendPayment(ctx context.Context, payConn grpc.ClientConnInterface, + req *routerrpc.SendPaymentRequest) (PaymentResultStream, error) { - conn := getClientConn(ctx, false) - defer conn.Close() + return routerrpc.NewRouterClient(payConn).SendPaymentV2(ctx, req) +} - client := lnrpc.NewLightningClient(conn) - routerClient := routerrpc.NewRouterClient(conn) +func SendPaymentRequest(ctx *cli.Context, req *routerrpc.SendPaymentRequest, + lnConn, paymentConn grpc.ClientConnInterface, + callSendPayment SendPaymentFn) error { + + ctxc := getContext() + + lnClient := lnrpc.NewLightningClient(lnConn) outChan := ctx.Int64Slice("outgoing_chan_id") if len(outChan) != 0 { @@ -558,7 +574,7 @@ func sendPaymentRequest(ctx *cli.Context, if req.PaymentRequest != "" { // Decode payment request to find out the amount. decodeReq := &lnrpc.PayReqString{PayReq: req.PaymentRequest} - decodeResp, err := client.DecodePayReq(ctxc, decodeReq) + decodeResp, err := lnClient.DecodePayReq(ctxc, decodeReq) if err != nil { return err } @@ -602,14 +618,12 @@ func sendPaymentRequest(ctx *cli.Context, printJSON := ctx.Bool(jsonFlag.Name) req.NoInflightUpdates = !ctx.Bool(inflightUpdatesFlag.Name) && printJSON - stream, err := routerClient.SendPaymentV2(ctxc, req) + stream, err := callSendPayment(ctxc, paymentConn, req) if err != nil { return err } - finalState, err := printLivePayment( - ctxc, stream, client, printJSON, - ) + finalState, err := PrintLivePayment(ctxc, stream, lnClient, printJSON) if err != nil { return err } @@ -667,24 +681,29 @@ func trackPayment(ctx *cli.Context) error { } client := lnrpc.NewLightningClient(conn) - _, err = printLivePayment(ctxc, stream, client, ctx.Bool(jsonFlag.Name)) + _, err = PrintLivePayment(ctxc, stream, client, ctx.Bool(jsonFlag.Name)) return err } -// printLivePayment receives payment updates from the given stream and either +// PaymentResultStream is an interface that abstracts the Recv method of the +// SendPaymentV2 or TrackPaymentV2 client stream. +type PaymentResultStream interface { + Recv() (*lnrpc.Payment, error) +} + +// PrintLivePayment receives payment updates from the given stream and either // outputs them as json or as a more user-friendly formatted table. The table // option uses terminal control codes to rewrite the output. This call // terminates when the payment reaches a final state. -func printLivePayment(ctxc context.Context, - stream routerrpc.Router_TrackPaymentV2Client, - client lnrpc.LightningClient, json bool) (*lnrpc.Payment, error) { +func PrintLivePayment(ctxc context.Context, stream PaymentResultStream, + lnClient lnrpc.LightningClient, json bool) (*lnrpc.Payment, error) { // Terminal escape codes aren't supported on Windows, fall back to json. if !json && runtime.GOOS == "windows" { json = true } - aliases := newAliasCache(client) + aliases := newAliasCache(lnClient) first := true var lastLineCount int @@ -706,17 +725,17 @@ func printLivePayment(ctxc context.Context, // Write raw json to stdout. printRespJSON(payment) } else { - table := formatPayment(ctxc, payment, aliases) + resultTable := formatPayment(ctxc, payment, aliases) // Clear all previously written lines and print the // updated table. clearLines(lastLineCount) - fmt.Print(table) + fmt.Print(resultTable) // Store the number of lines written for the next update // pass. lastLineCount = 0 - for _, b := range table { + for _, b := range resultTable { if b == '\n' { lastLineCount++ } @@ -874,7 +893,7 @@ var payInvoiceCommand = cli.Command{ This command is a shortcut for 'sendpayment --pay_req='. `, ArgsUsage: "pay_req", - Flags: append(paymentFlags(), + Flags: append(PaymentFlags(), cli.Int64Flag{ Name: "amt", Usage: "(optional) number of satoshis to fulfill the " + @@ -885,6 +904,9 @@ var payInvoiceCommand = cli.Command{ } func payInvoice(ctx *cli.Context) error { + conn := getClientConn(ctx, false) + defer conn.Close() + args := ctx.Args() var payReq string @@ -905,7 +927,7 @@ func payInvoice(ctx *cli.Context) error { Cancelable: ctx.Bool(cancelableFlag.Name), } - return sendPaymentRequest(ctx, req) + return SendPaymentRequest(ctx, req, conn, conn, routerRPCSendPayment) } var sendToRouteCommand = cli.Command{ diff --git a/cmd/lncli/cmd_profile.go b/cmd/commands/cmd_profile.go similarity index 99% rename from cmd/lncli/cmd_profile.go rename to cmd/commands/cmd_profile.go index 3b1bf64875..6767964eb4 100644 --- a/cmd/lncli/cmd_profile.go +++ b/cmd/commands/cmd_profile.go @@ -1,4 +1,4 @@ -package main +package commands import ( "fmt" diff --git a/cmd/lncli/cmd_state.go b/cmd/commands/cmd_state.go similarity index 98% rename from cmd/lncli/cmd_state.go rename to cmd/commands/cmd_state.go index afca13e9d6..c2522b721b 100644 --- a/cmd/lncli/cmd_state.go +++ b/cmd/commands/cmd_state.go @@ -1,4 +1,4 @@ -package main +package commands import ( "context" diff --git a/cmd/lncli/cmd_update_chan_status.go b/cmd/commands/cmd_update_chan_status.go similarity index 99% rename from cmd/lncli/cmd_update_chan_status.go rename to cmd/commands/cmd_update_chan_status.go index 23c22f0b16..3525f7c5c6 100644 --- a/cmd/lncli/cmd_update_chan_status.go +++ b/cmd/commands/cmd_update_chan_status.go @@ -1,4 +1,4 @@ -package main +package commands import ( "errors" diff --git a/cmd/lncli/cmd_version.go b/cmd/commands/cmd_version.go similarity index 98% rename from cmd/lncli/cmd_version.go rename to cmd/commands/cmd_version.go index 99cc729953..9e7a2b0775 100644 --- a/cmd/lncli/cmd_version.go +++ b/cmd/commands/cmd_version.go @@ -1,4 +1,4 @@ -package main +package commands import ( "fmt" diff --git a/cmd/lncli/cmd_walletunlocker.go b/cmd/commands/cmd_walletunlocker.go similarity index 99% rename from cmd/lncli/cmd_walletunlocker.go rename to cmd/commands/cmd_walletunlocker.go index 650ef80ea5..844b48f3d6 100644 --- a/cmd/lncli/cmd_walletunlocker.go +++ b/cmd/commands/cmd_walletunlocker.go @@ -1,4 +1,4 @@ -package main +package commands import ( "bufio" diff --git a/cmd/lncli/commands.go b/cmd/commands/commands.go similarity index 97% rename from cmd/lncli/commands.go rename to cmd/commands/commands.go index fefef6ab28..2c1191ab2b 100644 --- a/cmd/lncli/commands.go +++ b/cmd/commands/commands.go @@ -1,4 +1,4 @@ -package main +package commands import ( "bufio" @@ -11,6 +11,7 @@ import ( "io" "math" "os" + "regexp" "strconv" "strings" "sync" @@ -41,8 +42,47 @@ const ( defaultUtxoMinConf = 1 ) -var errBadChanPoint = errors.New("expecting chan_point to be in format of: " + - "txid:index") +var ( + errBadChanPoint = errors.New( + "expecting chan_point to be in format of: txid:index", + ) + + customDataPattern = regexp.MustCompile( + `"custom_channel_data":\s*"([0-9a-f]+)"`, + ) +) + +// replaceCustomData replaces the custom channel data hex string with the +// decoded custom channel data in the JSON response. +func replaceCustomData(jsonBytes []byte) ([]byte, error) { + // If there's nothing to replace, return the original JSON. + if !customDataPattern.Match(jsonBytes) { + return jsonBytes, nil + } + + replacedBytes := customDataPattern.ReplaceAllFunc( + jsonBytes, func(match []byte) []byte { + encoded := customDataPattern.FindStringSubmatch( + string(match), + )[1] + decoded, err := hex.DecodeString(encoded) + if err != nil { + return match + } + + return []byte("\"custom_channel_data\":" + + string(decoded)) + }, + ) + + var buf bytes.Buffer + err := json.Indent(&buf, replacedBytes, "", " ") + if err != nil { + return nil, err + } + + return buf.Bytes(), nil +} func getContext() context.Context { shutdownInterceptor, err := signal.Intercept() @@ -66,9 +106,9 @@ func printJSON(resp interface{}) { } var out bytes.Buffer - json.Indent(&out, b, "", "\t") - out.WriteString("\n") - out.WriteTo(os.Stdout) + _ = json.Indent(&out, b, "", " ") + _, _ = out.WriteString("\n") + _, _ = out.WriteTo(os.Stdout) } func printRespJSON(resp proto.Message) { @@ -78,7 +118,13 @@ func printRespJSON(resp proto.Message) { return } - fmt.Printf("%s\n", jsonBytes) + jsonBytesReplaced, err := replaceCustomData(jsonBytes) + if err != nil { + fmt.Println("unable to replace custom data: ", err) + jsonBytesReplaced = jsonBytes + } + + fmt.Printf("%s\n", jsonBytesReplaced) } // actionDecorator is used to add additional information and error handling @@ -1442,15 +1488,15 @@ func walletBalance(ctx *cli.Context) error { return nil } -var channelBalanceCommand = cli.Command{ +var ChannelBalanceCommand = cli.Command{ Name: "channelbalance", Category: "Channels", Usage: "Returns the sum of the total available channel balance across " + "all open channels.", - Action: actionDecorator(channelBalance), + Action: actionDecorator(ChannelBalance), } -func channelBalance(ctx *cli.Context) error { +func ChannelBalance(ctx *cli.Context) error { ctxc := getContext() client, cleanUp := getClient(ctx) defer cleanUp() @@ -1575,7 +1621,7 @@ func pendingChannels(ctx *cli.Context) error { return nil } -var listChannelsCommand = cli.Command{ +var ListChannelsCommand = cli.Command{ Name: "listchannels", Category: "Channels", Usage: "List all open channels.", @@ -1608,7 +1654,7 @@ var listChannelsCommand = cli.Command{ "order to improve performance", }, }, - Action: actionDecorator(listChannels), + Action: actionDecorator(ListChannels), } var listAliasesCommand = cli.Command{ @@ -1616,10 +1662,10 @@ var listAliasesCommand = cli.Command{ Category: "Channels", Usage: "List all aliases.", Flags: []cli.Flag{}, - Action: actionDecorator(listaliases), + Action: actionDecorator(listAliases), } -func listaliases(ctx *cli.Context) error { +func listAliases(ctx *cli.Context) error { ctxc := getContext() client, cleanUp := getClient(ctx) defer cleanUp() @@ -1636,7 +1682,7 @@ func listaliases(ctx *cli.Context) error { return nil } -func listChannels(ctx *cli.Context) error { +func ListChannels(ctx *cli.Context) error { ctxc := getContext() client, cleanUp := getClient(ctx) defer cleanUp() diff --git a/cmd/lncli/commands_test.go b/cmd/commands/commands_test.go similarity index 58% rename from cmd/lncli/commands_test.go rename to cmd/commands/commands_test.go index a1f967561e..bab2c8d2ba 100644 --- a/cmd/lncli/commands_test.go +++ b/cmd/commands/commands_test.go @@ -1,4 +1,4 @@ -package main +package commands import ( "encoding/hex" @@ -120,3 +120,82 @@ func TestParseTimeLockDelta(t *testing.T) { } } } + +// TestReplaceCustomData tests that hex encoded custom data can be formatted as +// JSON in the console output. +func TestReplaceCustomData(t *testing.T) { + t.Parallel() + + testCases := []struct { + name string + data string + replaceData string + expected string + expectedErr string + }{ + { + name: "no replacement necessary", + data: "foo", + expected: "foo", + }, + { + name: "valid json with replacement", + data: "{\"foo\":\"bar\",\"custom_channel_data\":\"" + + hex.EncodeToString([]byte( + "{\"bar\":\"baz\"}", + )) + "\"}", + expected: `{ + "foo": "bar", + "custom_channel_data": { + "bar": "baz" + } +}`, + }, + { + name: "valid json with replacement and space", + data: "{\"foo\":\"bar\",\"custom_channel_data\": \"" + + hex.EncodeToString([]byte( + "{\"bar\":\"baz\"}", + )) + "\"}", + expected: `{ + "foo": "bar", + "custom_channel_data": { + "bar": "baz" + } +}`, + }, + { + name: "doesn't match pattern, returned identical", + data: "this ain't even json, and no custom data " + + "either", + expected: "this ain't even json, and no custom data " + + "either", + }, + { + name: "invalid json", + data: "this ain't json, " + + "\"custom_channel_data\":\"a\"", + expectedErr: "invalid character 'h' in literal true", + }, + { + name: "valid json, invalid hex, just formatted", + data: "{\"custom_channel_data\":\"f\"}", + expected: "{\n \"custom_channel_data\": \"f\"\n}", + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + result, err := replaceCustomData([]byte(tc.data)) + + if tc.expectedErr != "" { + require.ErrorContains(t, err, tc.expectedErr) + return + } + + require.NoError(t, err) + + require.Equal(t, tc.expected, string(result)) + }) + } +} diff --git a/cmd/lncli/devrpc_active.go b/cmd/commands/devrpc_active.go similarity index 98% rename from cmd/lncli/devrpc_active.go rename to cmd/commands/devrpc_active.go index da3f08a97d..8d1960e461 100644 --- a/cmd/lncli/devrpc_active.go +++ b/cmd/commands/devrpc_active.go @@ -1,7 +1,7 @@ //go:build dev // +build dev -package main +package commands import ( "fmt" diff --git a/cmd/lncli/devrpc_default.go b/cmd/commands/devrpc_default.go similarity index 90% rename from cmd/lncli/devrpc_default.go rename to cmd/commands/devrpc_default.go index b9362cb421..1c5b482c32 100644 --- a/cmd/lncli/devrpc_default.go +++ b/cmd/commands/devrpc_default.go @@ -1,7 +1,7 @@ //go:build !dev // +build !dev -package main +package commands import "github.com/urfave/cli" diff --git a/cmd/lncli/invoicesrpc_active.go b/cmd/commands/invoicesrpc_active.go similarity index 99% rename from cmd/lncli/invoicesrpc_active.go rename to cmd/commands/invoicesrpc_active.go index 823af67bf8..0ee767c8b2 100644 --- a/cmd/lncli/invoicesrpc_active.go +++ b/cmd/commands/invoicesrpc_active.go @@ -1,7 +1,7 @@ //go:build invoicesrpc // +build invoicesrpc -package main +package commands import ( "encoding/hex" diff --git a/cmd/lncli/invoicesrpc_default.go b/cmd/commands/invoicesrpc_default.go similarity index 92% rename from cmd/lncli/invoicesrpc_default.go rename to cmd/commands/invoicesrpc_default.go index cca3c14e9f..e925e55d69 100644 --- a/cmd/lncli/invoicesrpc_default.go +++ b/cmd/commands/invoicesrpc_default.go @@ -1,7 +1,7 @@ //go:build !invoicesrpc // +build !invoicesrpc -package main +package commands import "github.com/urfave/cli" diff --git a/cmd/lncli/macaroon_jar.go b/cmd/commands/macaroon_jar.go similarity index 99% rename from cmd/lncli/macaroon_jar.go rename to cmd/commands/macaroon_jar.go index f54f29a26c..d3a4345b0b 100644 --- a/cmd/lncli/macaroon_jar.go +++ b/cmd/commands/macaroon_jar.go @@ -1,4 +1,4 @@ -package main +package commands import ( "encoding/base64" diff --git a/cmd/lncli/macaroon_jar_test.go b/cmd/commands/macaroon_jar_test.go similarity index 99% rename from cmd/lncli/macaroon_jar_test.go rename to cmd/commands/macaroon_jar_test.go index 8e1d1c6bd4..6d76dce848 100644 --- a/cmd/lncli/macaroon_jar_test.go +++ b/cmd/commands/macaroon_jar_test.go @@ -1,4 +1,4 @@ -package main +package commands import ( "encoding/hex" diff --git a/cmd/commands/main.go b/cmd/commands/main.go new file mode 100644 index 0000000000..71a8531d9a --- /dev/null +++ b/cmd/commands/main.go @@ -0,0 +1,601 @@ +// Copyright (c) 2013-2017 The btcsuite developers +// Copyright (c) 2015-2016 The Decred developers +// Copyright (C) 2015-2024 The Lightning Network Developers + +package commands + +import ( + "context" + "crypto/tls" + "fmt" + "net" + "os" + "path/filepath" + "strings" + "syscall" + + "github.com/btcsuite/btcd/btcutil" + "github.com/btcsuite/btcd/chaincfg" + "github.com/lightningnetwork/lnd" + "github.com/lightningnetwork/lnd/build" + "github.com/lightningnetwork/lnd/lncfg" + "github.com/lightningnetwork/lnd/lnrpc" + "github.com/lightningnetwork/lnd/macaroons" + "github.com/lightningnetwork/lnd/tor" + "github.com/urfave/cli" + "golang.org/x/term" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials" + "google.golang.org/grpc/metadata" +) + +const ( + defaultDataDir = "data" + defaultChainSubDir = "chain" + defaultTLSCertFilename = "tls.cert" + defaultMacaroonFilename = "admin.macaroon" + defaultRPCPort = "10009" + defaultRPCHostPort = "localhost:" + defaultRPCPort + + envVarRPCServer = "LNCLI_RPCSERVER" + envVarLNDDir = "LNCLI_LNDDIR" + envVarSOCKSProxy = "LNCLI_SOCKSPROXY" + envVarTLSCertPath = "LNCLI_TLSCERTPATH" + envVarChain = "LNCLI_CHAIN" + envVarNetwork = "LNCLI_NETWORK" + envVarMacaroonPath = "LNCLI_MACAROONPATH" + envVarMacaroonTimeout = "LNCLI_MACAROONTIMEOUT" + envVarMacaroonIP = "LNCLI_MACAROONIP" + envVarProfile = "LNCLI_PROFILE" + envVarMacFromJar = "LNCLI_MACFROMJAR" +) + +var ( + DefaultLndDir = btcutil.AppDataDir("lnd", false) + defaultTLSCertPath = filepath.Join( + DefaultLndDir, defaultTLSCertFilename, + ) + + // maxMsgRecvSize is the largest message our client will receive. We + // set this to 200MiB atm. + maxMsgRecvSize = grpc.MaxCallRecvMsgSize(lnrpc.MaxGrpcMsgSize) +) + +func fatal(err error) { + fmt.Fprintf(os.Stderr, "[lncli] %v\n", err) + os.Exit(1) +} + +func getWalletUnlockerClient(ctx *cli.Context) (lnrpc.WalletUnlockerClient, + func()) { + + conn := getClientConn(ctx, true) + + cleanUp := func() { + conn.Close() + } + + return lnrpc.NewWalletUnlockerClient(conn), cleanUp +} + +func getStateServiceClient(ctx *cli.Context) (lnrpc.StateClient, func()) { + conn := getClientConn(ctx, true) + + cleanUp := func() { + conn.Close() + } + + return lnrpc.NewStateClient(conn), cleanUp +} + +func getClient(ctx *cli.Context) (lnrpc.LightningClient, func()) { + conn := getClientConn(ctx, false) + + cleanUp := func() { + conn.Close() + } + + return lnrpc.NewLightningClient(conn), cleanUp +} + +func getClientConn(ctx *cli.Context, skipMacaroons bool) *grpc.ClientConn { + // First, we'll get the selected stored profile or an ephemeral one + // created from the global options in the CLI context. + profile, err := getGlobalOptions(ctx, skipMacaroons) + if err != nil { + fatal(fmt.Errorf("could not load global options: %w", err)) + } + + // Create a dial options array. + opts := []grpc.DialOption{ + grpc.WithUnaryInterceptor( + addMetadataUnaryInterceptor(profile.Metadata), + ), + grpc.WithStreamInterceptor( + addMetaDataStreamInterceptor(profile.Metadata), + ), + } + + if profile.Insecure { + opts = append(opts, grpc.WithInsecure()) + } else { + // Load the specified TLS certificate. + certPool, err := profile.cert() + if err != nil { + fatal(fmt.Errorf("could not create cert pool: %w", err)) + } + + // Build transport credentials from the certificate pool. If + // there is no certificate pool, we expect the server to use a + // non-self-signed certificate such as a certificate obtained + // from Let's Encrypt. + var creds credentials.TransportCredentials + if certPool != nil { + creds = credentials.NewClientTLSFromCert(certPool, "") + } else { + // Fallback to the system pool. Using an empty tls + // config is an alternative to x509.SystemCertPool(). + // That call is not supported on Windows. + creds = credentials.NewTLS(&tls.Config{}) + } + + opts = append(opts, grpc.WithTransportCredentials(creds)) + } + + // Only process macaroon credentials if --no-macaroons isn't set and + // if we're not skipping macaroon processing. + if !profile.NoMacaroons && !skipMacaroons { + // Find out which macaroon to load. + macName := profile.Macaroons.Default + if ctx.GlobalIsSet("macfromjar") { + macName = ctx.GlobalString("macfromjar") + } + var macEntry *macaroonEntry + for _, entry := range profile.Macaroons.Jar { + if entry.Name == macName { + macEntry = entry + break + } + } + if macEntry == nil { + fatal(fmt.Errorf("macaroon with name '%s' not found "+ + "in profile", macName)) + } + + // Get and possibly decrypt the specified macaroon. + // + // TODO(guggero): Make it possible to cache the password so we + // don't need to ask for it every time. + mac, err := macEntry.loadMacaroon(readPassword) + if err != nil { + fatal(fmt.Errorf("could not load macaroon: %w", err)) + } + + macConstraints := []macaroons.Constraint{ + // We add a time-based constraint to prevent replay of + // the macaroon. It's good for 60 seconds by default to + // make up for any discrepancy between client and server + // clocks, but leaking the macaroon before it becomes + // invalid makes it possible for an attacker to reuse + // the macaroon. In addition, the validity time of the + // macaroon is extended by the time the server clock is + // behind the client clock, or shortened by the time the + // server clock is ahead of the client clock (or invalid + // altogether if, in the latter case, this time is more + // than 60 seconds). + // TODO(aakselrod): add better anti-replay protection. + macaroons.TimeoutConstraint(profile.Macaroons.Timeout), + + // Lock macaroon down to a specific IP address. + macaroons.IPLockConstraint(profile.Macaroons.IP), + + // ... Add more constraints if needed. + } + + // Apply constraints to the macaroon. + constrainedMac, err := macaroons.AddConstraints( + mac, macConstraints..., + ) + if err != nil { + fatal(err) + } + + // Now we append the macaroon credentials to the dial options. + cred, err := macaroons.NewMacaroonCredential(constrainedMac) + if err != nil { + fatal(fmt.Errorf("error cloning mac: %w", err)) + } + opts = append(opts, grpc.WithPerRPCCredentials(cred)) + } + + // If a socksproxy server is specified we use a tor dialer + // to connect to the grpc server. + if ctx.GlobalIsSet("socksproxy") { + socksProxy := ctx.GlobalString("socksproxy") + torDialer := func(_ context.Context, addr string) (net.Conn, + error) { + + return tor.Dial( + addr, socksProxy, false, false, + tor.DefaultConnTimeout, + ) + } + opts = append(opts, grpc.WithContextDialer(torDialer)) + } else { + // We need to use a custom dialer so we can also connect to + // unix sockets and not just TCP addresses. + genericDialer := lncfg.ClientAddressDialer(defaultRPCPort) + opts = append(opts, grpc.WithContextDialer(genericDialer)) + } + + opts = append(opts, grpc.WithDefaultCallOptions(maxMsgRecvSize)) + + conn, err := grpc.Dial(profile.RPCServer, opts...) + if err != nil { + fatal(fmt.Errorf("unable to connect to RPC server: %w", err)) + } + + return conn +} + +// addMetadataUnaryInterceptor returns a grpc client side interceptor that +// appends any key-value metadata strings to the outgoing context of a grpc +// unary call. +func addMetadataUnaryInterceptor( + md map[string]string) grpc.UnaryClientInterceptor { + + return func(ctx context.Context, method string, req, reply interface{}, + cc *grpc.ClientConn, invoker grpc.UnaryInvoker, + opts ...grpc.CallOption) error { + + outCtx := contextWithMetadata(ctx, md) + return invoker(outCtx, method, req, reply, cc, opts...) + } +} + +// addMetaDataStreamInterceptor returns a grpc client side interceptor that +// appends any key-value metadata strings to the outgoing context of a grpc +// stream call. +func addMetaDataStreamInterceptor( + md map[string]string) grpc.StreamClientInterceptor { + + return func(ctx context.Context, desc *grpc.StreamDesc, + cc *grpc.ClientConn, method string, streamer grpc.Streamer, + opts ...grpc.CallOption) (grpc.ClientStream, error) { + + outCtx := contextWithMetadata(ctx, md) + return streamer(outCtx, desc, cc, method, opts...) + } +} + +// contextWithMetaData appends the given metadata key-value pairs to the given +// context. +func contextWithMetadata(ctx context.Context, + md map[string]string) context.Context { + + kvPairs := make([]string, 0, 2*len(md)) + for k, v := range md { + kvPairs = append(kvPairs, k, v) + } + + return metadata.AppendToOutgoingContext(ctx, kvPairs...) +} + +// extractPathArgs parses the TLS certificate and macaroon paths from the +// command. +func extractPathArgs(ctx *cli.Context) (string, string, error) { + network := strings.ToLower(ctx.GlobalString("network")) + switch network { + case "mainnet", "testnet", "regtest", "simnet", "signet": + default: + return "", "", fmt.Errorf("unknown network: %v", network) + } + + // We'll now fetch the lnddir so we can make a decision on how to + // properly read the macaroons (if needed) and also the cert. This will + // either be the default, or will have been overwritten by the end + // user. + lndDir := lncfg.CleanAndExpandPath(ctx.GlobalString("lnddir")) + + // If the macaroon path as been manually provided, then we'll only + // target the specified file. + var macPath string + if ctx.GlobalString("macaroonpath") != "" { + macPath = lncfg.CleanAndExpandPath(ctx.GlobalString( + "macaroonpath", + )) + } else { + // Otherwise, we'll go into the path: + // lnddir/data/chain// in order to fetch the + // macaroon that we need. + macPath = filepath.Join( + lndDir, defaultDataDir, defaultChainSubDir, + lnd.BitcoinChainName, network, defaultMacaroonFilename, + ) + } + + tlsCertPath := lncfg.CleanAndExpandPath(ctx.GlobalString("tlscertpath")) + + // If a custom lnd directory was set, we'll also check if custom paths + // for the TLS cert and macaroon file were set as well. If not, we'll + // override their paths so they can be found within the custom lnd + // directory set. This allows us to set a custom lnd directory, along + // with custom paths to the TLS cert and macaroon file. + if lndDir != DefaultLndDir { + tlsCertPath = filepath.Join(lndDir, defaultTLSCertFilename) + } + + return tlsCertPath, macPath, nil +} + +// checkNotBothSet accepts two flag names, a and b, and checks that only flag a +// or flag b can be set, but not both. It returns the name of the flag or an +// error. +func checkNotBothSet(ctx *cli.Context, a, b string) (string, error) { + if ctx.IsSet(a) && ctx.IsSet(b) { + return "", fmt.Errorf( + "either %s or %s should be set, but not both", a, b, + ) + } + + if ctx.IsSet(a) { + return a, nil + } + + return b, nil +} + +func Main() { + app := cli.NewApp() + app.Name = "lncli" + app.Version = build.Version() + " commit=" + build.Commit + app.Usage = "control plane for your Lightning Network Daemon (lnd)" + app.Flags = []cli.Flag{ + cli.StringFlag{ + Name: "rpcserver", + Value: defaultRPCHostPort, + Usage: "The host:port of LN daemon.", + EnvVar: envVarRPCServer, + }, + cli.StringFlag{ + Name: "lnddir", + Value: DefaultLndDir, + Usage: "The path to lnd's base directory.", + TakesFile: true, + EnvVar: envVarLNDDir, + }, + cli.StringFlag{ + Name: "socksproxy", + Usage: "The host:port of a SOCKS proxy through " + + "which all connections to the LN " + + "daemon will be established over.", + EnvVar: envVarSOCKSProxy, + }, + cli.StringFlag{ + Name: "tlscertpath", + Value: defaultTLSCertPath, + Usage: "The path to lnd's TLS certificate.", + TakesFile: true, + EnvVar: envVarTLSCertPath, + }, + cli.StringFlag{ + Name: "chain, c", + Usage: "The chain lnd is running on, e.g. bitcoin.", + Value: "bitcoin", + EnvVar: envVarChain, + }, + cli.StringFlag{ + Name: "network, n", + Usage: "The network lnd is running on, e.g. mainnet, " + + "testnet, etc.", + Value: "mainnet", + EnvVar: envVarNetwork, + }, + cli.BoolFlag{ + Name: "no-macaroons", + Usage: "Disable macaroon authentication.", + }, + cli.StringFlag{ + Name: "macaroonpath", + Usage: "The path to macaroon file.", + TakesFile: true, + EnvVar: envVarMacaroonPath, + }, + cli.Int64Flag{ + Name: "macaroontimeout", + Value: 60, + Usage: "Anti-replay macaroon validity time in " + + "seconds.", + EnvVar: envVarMacaroonTimeout, + }, + cli.StringFlag{ + Name: "macaroonip", + Usage: "If set, lock macaroon to specific IP address.", + EnvVar: envVarMacaroonIP, + }, + cli.StringFlag{ + Name: "profile, p", + Usage: "Instead of reading settings from command " + + "line parameters or using the default " + + "profile, use a specific profile. If " + + "a default profile is set, this flag can be " + + "set to an empty string to disable reading " + + "values from the profiles file.", + EnvVar: envVarProfile, + }, + cli.StringFlag{ + Name: "macfromjar", + Usage: "Use this macaroon from the profile's " + + "macaroon jar instead of the default one. " + + "Can only be used if profiles are defined.", + EnvVar: envVarMacFromJar, + }, + cli.StringSliceFlag{ + Name: "metadata", + Usage: "This flag can be used to specify a key-value " + + "pair that should be appended to the " + + "outgoing context before the request is sent " + + "to lnd. This flag may be specified multiple " + + "times. The format is: \"key:value\".", + }, + cli.BoolFlag{ + Name: "insecure", + Usage: "Connect to the rpc server without TLS " + + "authentication", + Hidden: true, + }, + } + app.Commands = []cli.Command{ + createCommand, + createWatchOnlyCommand, + unlockCommand, + changePasswordCommand, + newAddressCommand, + estimateFeeCommand, + sendManyCommand, + sendCoinsCommand, + listUnspentCommand, + connectCommand, + disconnectCommand, + openChannelCommand, + batchOpenChannelCommand, + closeChannelCommand, + closeAllChannelsCommand, + abandonChannelCommand, + listPeersCommand, + walletBalanceCommand, + ChannelBalanceCommand, + getInfoCommand, + getDebugInfoCommand, + encryptDebugPackageCommand, + decryptDebugPackageCommand, + getRecoveryInfoCommand, + pendingChannelsCommand, + SendPaymentCommand, + payInvoiceCommand, + sendToRouteCommand, + AddInvoiceCommand, + lookupInvoiceCommand, + listInvoicesCommand, + ListChannelsCommand, + closedChannelsCommand, + listPaymentsCommand, + describeGraphCommand, + getNodeMetricsCommand, + getChanInfoCommand, + getNodeInfoCommand, + queryRoutesCommand, + getNetworkInfoCommand, + debugLevelCommand, + decodePayReqCommand, + listChainTxnsCommand, + stopCommand, + signMessageCommand, + verifyMessageCommand, + feeReportCommand, + updateChannelPolicyCommand, + forwardingHistoryCommand, + exportChanBackupCommand, + verifyChanBackupCommand, + restoreChanBackupCommand, + bakeMacaroonCommand, + listMacaroonIDsCommand, + deleteMacaroonIDCommand, + listPermissionsCommand, + printMacaroonCommand, + constrainMacaroonCommand, + trackPaymentCommand, + versionCommand, + profileSubCommand, + getStateCommand, + deletePaymentsCommand, + sendCustomCommand, + subscribeCustomCommand, + fishCompletionCommand, + listAliasesCommand, + estimateRouteFeeCommand, + generateManPageCommand, + } + + // Add any extra commands determined by build flags. + app.Commands = append(app.Commands, autopilotCommands()...) + app.Commands = append(app.Commands, invoicesCommands()...) + app.Commands = append(app.Commands, neutrinoCommands()...) + app.Commands = append(app.Commands, routerCommands()...) + app.Commands = append(app.Commands, walletCommands()...) + app.Commands = append(app.Commands, watchtowerCommands()...) + app.Commands = append(app.Commands, wtclientCommands()...) + app.Commands = append(app.Commands, devCommands()...) + app.Commands = append(app.Commands, peersCommands()...) + app.Commands = append(app.Commands, chainCommands()...) + + if err := app.Run(os.Args); err != nil { + fatal(err) + } +} + +// readPassword reads a password from the terminal. This requires there to be an +// actual TTY so passing in a password from stdin won't work. +func readPassword(text string) ([]byte, error) { + fmt.Print(text) + + // The variable syscall.Stdin is of a different type in the Windows API + // that's why we need the explicit cast. And of course the linter + // doesn't like it either. + pw, err := term.ReadPassword(int(syscall.Stdin)) //nolint:unconvert + fmt.Println() + + return pw, err +} + +// networkParams parses the global network flag into a chaincfg.Params. +func networkParams(ctx *cli.Context) (*chaincfg.Params, error) { + network := strings.ToLower(ctx.GlobalString("network")) + switch network { + case "mainnet": + return &chaincfg.MainNetParams, nil + + case "testnet": + return &chaincfg.TestNet3Params, nil + + case "regtest": + return &chaincfg.RegressionNetParams, nil + + case "simnet": + return &chaincfg.SimNetParams, nil + + case "signet": + return &chaincfg.SigNetParams, nil + + default: + return nil, fmt.Errorf("unknown network: %v", network) + } +} + +// parseCoinSelectionStrategy parses a coin selection strategy string +// from the CLI to its lnrpc.CoinSelectionStrategy counterpart proto type. +func parseCoinSelectionStrategy(ctx *cli.Context) ( + lnrpc.CoinSelectionStrategy, error) { + + strategy := ctx.String(coinSelectionStrategyFlag.Name) + if !ctx.IsSet(coinSelectionStrategyFlag.Name) { + return lnrpc.CoinSelectionStrategy_STRATEGY_USE_GLOBAL_CONFIG, + nil + } + + switch strategy { + case "global-config": + return lnrpc.CoinSelectionStrategy_STRATEGY_USE_GLOBAL_CONFIG, + nil + + case "largest": + return lnrpc.CoinSelectionStrategy_STRATEGY_LARGEST, nil + + case "random": + return lnrpc.CoinSelectionStrategy_STRATEGY_RANDOM, nil + + default: + return 0, fmt.Errorf("unknown coin selection strategy "+ + "%v", strategy) + } +} diff --git a/cmd/lncli/neutrino_active.go b/cmd/commands/neutrino_active.go similarity index 99% rename from cmd/lncli/neutrino_active.go rename to cmd/commands/neutrino_active.go index 099da46c6e..f34c7cc0e2 100644 --- a/cmd/lncli/neutrino_active.go +++ b/cmd/commands/neutrino_active.go @@ -1,7 +1,7 @@ //go:build neutrinorpc // +build neutrinorpc -package main +package commands import ( "github.com/lightningnetwork/lnd/lnrpc/neutrinorpc" diff --git a/cmd/lncli/neutrino_default.go b/cmd/commands/neutrino_default.go similarity index 92% rename from cmd/lncli/neutrino_default.go rename to cmd/commands/neutrino_default.go index f1f1de404b..b269e12386 100644 --- a/cmd/lncli/neutrino_default.go +++ b/cmd/commands/neutrino_default.go @@ -1,7 +1,7 @@ //go:build !neutrinorpc // +build !neutrinorpc -package main +package commands import "github.com/urfave/cli" diff --git a/cmd/lncli/peersrpc_active.go b/cmd/commands/peersrpc_active.go similarity index 99% rename from cmd/lncli/peersrpc_active.go rename to cmd/commands/peersrpc_active.go index c044166d36..0736750c73 100644 --- a/cmd/lncli/peersrpc_active.go +++ b/cmd/commands/peersrpc_active.go @@ -1,7 +1,7 @@ //go:build peersrpc // +build peersrpc -package main +package commands import ( "fmt" diff --git a/cmd/lncli/peersrpc_default.go b/cmd/commands/peersrpc_default.go similarity index 91% rename from cmd/lncli/peersrpc_default.go rename to cmd/commands/peersrpc_default.go index 24cb2b8134..57c8aa7a97 100644 --- a/cmd/lncli/peersrpc_default.go +++ b/cmd/commands/peersrpc_default.go @@ -1,7 +1,7 @@ //go:build !peersrpc // +build !peersrpc -package main +package commands import "github.com/urfave/cli" diff --git a/cmd/lncli/profile.go b/cmd/commands/profile.go similarity index 99% rename from cmd/lncli/profile.go rename to cmd/commands/profile.go index efbdfc147a..0d63ca93c9 100644 --- a/cmd/lncli/profile.go +++ b/cmd/commands/profile.go @@ -1,4 +1,4 @@ -package main +package commands import ( "bytes" diff --git a/cmd/lncli/routerrpc.go b/cmd/commands/routerrpc.go similarity index 95% rename from cmd/lncli/routerrpc.go rename to cmd/commands/routerrpc.go index 30b8922249..82211affdf 100644 --- a/cmd/lncli/routerrpc.go +++ b/cmd/commands/routerrpc.go @@ -1,4 +1,4 @@ -package main +package commands import "github.com/urfave/cli" diff --git a/cmd/lncli/types.go b/cmd/commands/types.go similarity index 99% rename from cmd/lncli/types.go rename to cmd/commands/types.go index a93d3e2c68..2a82e7100f 100644 --- a/cmd/lncli/types.go +++ b/cmd/commands/types.go @@ -1,4 +1,4 @@ -package main +package commands import ( "encoding/hex" diff --git a/cmd/lncli/walletrpc_active.go b/cmd/commands/walletrpc_active.go similarity index 99% rename from cmd/lncli/walletrpc_active.go rename to cmd/commands/walletrpc_active.go index 5fd4830e28..f4b7039a89 100644 --- a/cmd/lncli/walletrpc_active.go +++ b/cmd/commands/walletrpc_active.go @@ -1,7 +1,7 @@ //go:build walletrpc // +build walletrpc -package main +package commands import ( "bytes" diff --git a/cmd/lncli/walletrpc_default.go b/cmd/commands/walletrpc_default.go similarity index 91% rename from cmd/lncli/walletrpc_default.go rename to cmd/commands/walletrpc_default.go index d6670e4499..90c627c2a5 100644 --- a/cmd/lncli/walletrpc_default.go +++ b/cmd/commands/walletrpc_default.go @@ -1,7 +1,7 @@ //go:build !walletrpc // +build !walletrpc -package main +package commands import "github.com/urfave/cli" diff --git a/cmd/lncli/walletrpc_types.go b/cmd/commands/walletrpc_types.go similarity index 99% rename from cmd/lncli/walletrpc_types.go rename to cmd/commands/walletrpc_types.go index ab251d18f2..4369151fad 100644 --- a/cmd/lncli/walletrpc_types.go +++ b/cmd/commands/walletrpc_types.go @@ -1,4 +1,4 @@ -package main +package commands import "github.com/lightningnetwork/lnd/lnrpc/walletrpc" diff --git a/cmd/lncli/watchtower_active.go b/cmd/commands/watchtower_active.go similarity index 98% rename from cmd/lncli/watchtower_active.go rename to cmd/commands/watchtower_active.go index 9c31c6ec4b..bc5cd19695 100644 --- a/cmd/lncli/watchtower_active.go +++ b/cmd/commands/watchtower_active.go @@ -1,7 +1,7 @@ //go:build watchtowerrpc // +build watchtowerrpc -package main +package commands import ( "github.com/lightningnetwork/lnd/lnrpc/watchtowerrpc" diff --git a/cmd/lncli/watchtower_default.go b/cmd/commands/watchtower_default.go similarity index 92% rename from cmd/lncli/watchtower_default.go rename to cmd/commands/watchtower_default.go index e3db3ccf36..c958a66bdc 100644 --- a/cmd/lncli/watchtower_default.go +++ b/cmd/commands/watchtower_default.go @@ -1,7 +1,7 @@ //go:build !watchtowerrpc // +build !watchtowerrpc -package main +package commands import "github.com/urfave/cli" diff --git a/cmd/lncli/wtclient.go b/cmd/commands/wtclient.go similarity index 99% rename from cmd/lncli/wtclient.go rename to cmd/commands/wtclient.go index d73f6ca612..075861b981 100644 --- a/cmd/lncli/wtclient.go +++ b/cmd/commands/wtclient.go @@ -1,4 +1,4 @@ -package main +package commands import ( "encoding/hex" diff --git a/cmd/lncli/main.go b/cmd/lncli/main.go index b1554fb070..ea5195a0c7 100644 --- a/cmd/lncli/main.go +++ b/cmd/lncli/main.go @@ -1,594 +1,11 @@ // Copyright (c) 2013-2017 The btcsuite developers // Copyright (c) 2015-2016 The Decred developers -// Copyright (C) 2015-2022 The Lightning Network Developers +// Copyright (C) 2015-2024 The Lightning Network Developers package main -import ( - "context" - "crypto/tls" - "fmt" - "net" - "os" - "path/filepath" - "strings" - "syscall" - - "github.com/btcsuite/btcd/btcutil" - "github.com/btcsuite/btcd/chaincfg" - "github.com/lightningnetwork/lnd" - "github.com/lightningnetwork/lnd/build" - "github.com/lightningnetwork/lnd/lncfg" - "github.com/lightningnetwork/lnd/lnrpc" - "github.com/lightningnetwork/lnd/macaroons" - "github.com/lightningnetwork/lnd/tor" - "github.com/urfave/cli" - "golang.org/x/term" - "google.golang.org/grpc" - "google.golang.org/grpc/credentials" - "google.golang.org/grpc/metadata" -) - -const ( - defaultDataDir = "data" - defaultChainSubDir = "chain" - defaultTLSCertFilename = "tls.cert" - defaultMacaroonFilename = "admin.macaroon" - defaultRPCPort = "10009" - defaultRPCHostPort = "localhost:" + defaultRPCPort - - envVarRPCServer = "LNCLI_RPCSERVER" - envVarLNDDir = "LNCLI_LNDDIR" - envVarSOCKSProxy = "LNCLI_SOCKSPROXY" - envVarTLSCertPath = "LNCLI_TLSCERTPATH" - envVarChain = "LNCLI_CHAIN" - envVarNetwork = "LNCLI_NETWORK" - envVarMacaroonPath = "LNCLI_MACAROONPATH" - envVarMacaroonTimeout = "LNCLI_MACAROONTIMEOUT" - envVarMacaroonIP = "LNCLI_MACAROONIP" - envVarProfile = "LNCLI_PROFILE" - envVarMacFromJar = "LNCLI_MACFROMJAR" -) - -var ( - defaultLndDir = btcutil.AppDataDir("lnd", false) - defaultTLSCertPath = filepath.Join(defaultLndDir, defaultTLSCertFilename) - - // maxMsgRecvSize is the largest message our client will receive. We - // set this to 200MiB atm. - maxMsgRecvSize = grpc.MaxCallRecvMsgSize(lnrpc.MaxGrpcMsgSize) -) - -func fatal(err error) { - fmt.Fprintf(os.Stderr, "[lncli] %v\n", err) - os.Exit(1) -} - -func getWalletUnlockerClient(ctx *cli.Context) (lnrpc.WalletUnlockerClient, func()) { - conn := getClientConn(ctx, true) - - cleanUp := func() { - conn.Close() - } - - return lnrpc.NewWalletUnlockerClient(conn), cleanUp -} - -func getStateServiceClient(ctx *cli.Context) (lnrpc.StateClient, func()) { - conn := getClientConn(ctx, true) - - cleanUp := func() { - conn.Close() - } - - return lnrpc.NewStateClient(conn), cleanUp -} - -func getClient(ctx *cli.Context) (lnrpc.LightningClient, func()) { - conn := getClientConn(ctx, false) - - cleanUp := func() { - conn.Close() - } - - return lnrpc.NewLightningClient(conn), cleanUp -} - -func getClientConn(ctx *cli.Context, skipMacaroons bool) *grpc.ClientConn { - // First, we'll get the selected stored profile or an ephemeral one - // created from the global options in the CLI context. - profile, err := getGlobalOptions(ctx, skipMacaroons) - if err != nil { - fatal(fmt.Errorf("could not load global options: %w", err)) - } - - // Create a dial options array. - opts := []grpc.DialOption{ - grpc.WithUnaryInterceptor( - addMetadataUnaryInterceptor(profile.Metadata), - ), - grpc.WithStreamInterceptor( - addMetaDataStreamInterceptor(profile.Metadata), - ), - } - - if profile.Insecure { - opts = append(opts, grpc.WithInsecure()) - } else { - // Load the specified TLS certificate. - certPool, err := profile.cert() - if err != nil { - fatal(fmt.Errorf("could not create cert pool: %w", err)) - } - - // Build transport credentials from the certificate pool. If - // there is no certificate pool, we expect the server to use a - // non-self-signed certificate such as a certificate obtained - // from Let's Encrypt. - var creds credentials.TransportCredentials - if certPool != nil { - creds = credentials.NewClientTLSFromCert(certPool, "") - } else { - // Fallback to the system pool. Using an empty tls - // config is an alternative to x509.SystemCertPool(). - // That call is not supported on Windows. - creds = credentials.NewTLS(&tls.Config{}) - } - - opts = append(opts, grpc.WithTransportCredentials(creds)) - } - - // Only process macaroon credentials if --no-macaroons isn't set and - // if we're not skipping macaroon processing. - if !profile.NoMacaroons && !skipMacaroons { - // Find out which macaroon to load. - macName := profile.Macaroons.Default - if ctx.GlobalIsSet("macfromjar") { - macName = ctx.GlobalString("macfromjar") - } - var macEntry *macaroonEntry - for _, entry := range profile.Macaroons.Jar { - if entry.Name == macName { - macEntry = entry - break - } - } - if macEntry == nil { - fatal(fmt.Errorf("macaroon with name '%s' not found "+ - "in profile", macName)) - } - - // Get and possibly decrypt the specified macaroon. - // - // TODO(guggero): Make it possible to cache the password so we - // don't need to ask for it every time. - mac, err := macEntry.loadMacaroon(readPassword) - if err != nil { - fatal(fmt.Errorf("could not load macaroon: %w", err)) - } - - macConstraints := []macaroons.Constraint{ - // We add a time-based constraint to prevent replay of - // the macaroon. It's good for 60 seconds by default to - // make up for any discrepancy between client and server - // clocks, but leaking the macaroon before it becomes - // invalid makes it possible for an attacker to reuse - // the macaroon. In addition, the validity time of the - // macaroon is extended by the time the server clock is - // behind the client clock, or shortened by the time the - // server clock is ahead of the client clock (or invalid - // altogether if, in the latter case, this time is more - // than 60 seconds). - // TODO(aakselrod): add better anti-replay protection. - macaroons.TimeoutConstraint(profile.Macaroons.Timeout), - - // Lock macaroon down to a specific IP address. - macaroons.IPLockConstraint(profile.Macaroons.IP), - - // ... Add more constraints if needed. - } - - // Apply constraints to the macaroon. - constrainedMac, err := macaroons.AddConstraints( - mac, macConstraints..., - ) - if err != nil { - fatal(err) - } - - // Now we append the macaroon credentials to the dial options. - cred, err := macaroons.NewMacaroonCredential(constrainedMac) - if err != nil { - fatal(fmt.Errorf("error cloning mac: %w", err)) - } - opts = append(opts, grpc.WithPerRPCCredentials(cred)) - } - - // If a socksproxy server is specified we use a tor dialer - // to connect to the grpc server. - if ctx.GlobalIsSet("socksproxy") { - socksProxy := ctx.GlobalString("socksproxy") - torDialer := func(_ context.Context, addr string) (net.Conn, - error) { - - return tor.Dial( - addr, socksProxy, false, false, - tor.DefaultConnTimeout, - ) - } - opts = append(opts, grpc.WithContextDialer(torDialer)) - } else { - // We need to use a custom dialer so we can also connect to - // unix sockets and not just TCP addresses. - genericDialer := lncfg.ClientAddressDialer(defaultRPCPort) - opts = append(opts, grpc.WithContextDialer(genericDialer)) - } - - opts = append(opts, grpc.WithDefaultCallOptions(maxMsgRecvSize)) - - conn, err := grpc.Dial(profile.RPCServer, opts...) - if err != nil { - fatal(fmt.Errorf("unable to connect to RPC server: %w", err)) - } - - return conn -} - -// addMetadataUnaryInterceptor returns a grpc client side interceptor that -// appends any key-value metadata strings to the outgoing context of a grpc -// unary call. -func addMetadataUnaryInterceptor( - md map[string]string) grpc.UnaryClientInterceptor { - - return func(ctx context.Context, method string, req, reply interface{}, - cc *grpc.ClientConn, invoker grpc.UnaryInvoker, - opts ...grpc.CallOption) error { - - outCtx := contextWithMetadata(ctx, md) - return invoker(outCtx, method, req, reply, cc, opts...) - } -} - -// addMetaDataStreamInterceptor returns a grpc client side interceptor that -// appends any key-value metadata strings to the outgoing context of a grpc -// stream call. -func addMetaDataStreamInterceptor( - md map[string]string) grpc.StreamClientInterceptor { - - return func(ctx context.Context, desc *grpc.StreamDesc, - cc *grpc.ClientConn, method string, streamer grpc.Streamer, - opts ...grpc.CallOption) (grpc.ClientStream, error) { - - outCtx := contextWithMetadata(ctx, md) - return streamer(outCtx, desc, cc, method, opts...) - } -} - -// contextWithMetaData appends the given metadata key-value pairs to the given -// context. -func contextWithMetadata(ctx context.Context, - md map[string]string) context.Context { - - kvPairs := make([]string, 0, 2*len(md)) - for k, v := range md { - kvPairs = append(kvPairs, k, v) - } - - return metadata.AppendToOutgoingContext(ctx, kvPairs...) -} - -// extractPathArgs parses the TLS certificate and macaroon paths from the -// command. -func extractPathArgs(ctx *cli.Context) (string, string, error) { - network := strings.ToLower(ctx.GlobalString("network")) - switch network { - case "mainnet", "testnet", "regtest", "simnet", "signet": - default: - return "", "", fmt.Errorf("unknown network: %v", network) - } - - // We'll now fetch the lnddir so we can make a decision on how to - // properly read the macaroons (if needed) and also the cert. This will - // either be the default, or will have been overwritten by the end - // user. - lndDir := lncfg.CleanAndExpandPath(ctx.GlobalString("lnddir")) - - // If the macaroon path as been manually provided, then we'll only - // target the specified file. - var macPath string - if ctx.GlobalString("macaroonpath") != "" { - macPath = lncfg.CleanAndExpandPath(ctx.GlobalString("macaroonpath")) - } else { - // Otherwise, we'll go into the path: - // lnddir/data/chain// in order to fetch the - // macaroon that we need. - macPath = filepath.Join( - lndDir, defaultDataDir, defaultChainSubDir, - lnd.BitcoinChainName, network, defaultMacaroonFilename, - ) - } - - tlsCertPath := lncfg.CleanAndExpandPath(ctx.GlobalString("tlscertpath")) - - // If a custom lnd directory was set, we'll also check if custom paths - // for the TLS cert and macaroon file were set as well. If not, we'll - // override their paths so they can be found within the custom lnd - // directory set. This allows us to set a custom lnd directory, along - // with custom paths to the TLS cert and macaroon file. - if lndDir != defaultLndDir { - tlsCertPath = filepath.Join(lndDir, defaultTLSCertFilename) - } - - return tlsCertPath, macPath, nil -} - -// checkNotBothSet accepts two flag names, a and b, and checks that only flag a -// or flag b can be set, but not both. It returns the name of the flag or an -// error. -func checkNotBothSet(ctx *cli.Context, a, b string) (string, error) { - if ctx.IsSet(a) && ctx.IsSet(b) { - return "", fmt.Errorf( - "either %s or %s should be set, but not both", a, b, - ) - } - - if ctx.IsSet(a) { - return a, nil - } - - return b, nil -} +import "github.com/lightningnetwork/lnd/cmd/commands" func main() { - app := cli.NewApp() - app.Name = "lncli" - app.Version = build.Version() + " commit=" + build.Commit - app.Usage = "control plane for your Lightning Network Daemon (lnd)" - app.Flags = []cli.Flag{ - cli.StringFlag{ - Name: "rpcserver", - Value: defaultRPCHostPort, - Usage: "The host:port of LN daemon.", - EnvVar: envVarRPCServer, - }, - cli.StringFlag{ - Name: "lnddir", - Value: defaultLndDir, - Usage: "The path to lnd's base directory.", - TakesFile: true, - EnvVar: envVarLNDDir, - }, - cli.StringFlag{ - Name: "socksproxy", - Usage: "The host:port of a SOCKS proxy through " + - "which all connections to the LN " + - "daemon will be established over.", - EnvVar: envVarSOCKSProxy, - }, - cli.StringFlag{ - Name: "tlscertpath", - Value: defaultTLSCertPath, - Usage: "The path to lnd's TLS certificate.", - TakesFile: true, - EnvVar: envVarTLSCertPath, - }, - cli.StringFlag{ - Name: "chain, c", - Usage: "The chain lnd is running on, e.g. bitcoin.", - Value: "bitcoin", - EnvVar: envVarChain, - }, - cli.StringFlag{ - Name: "network, n", - Usage: "The network lnd is running on, e.g. mainnet, " + - "testnet, etc.", - Value: "mainnet", - EnvVar: envVarNetwork, - }, - cli.BoolFlag{ - Name: "no-macaroons", - Usage: "Disable macaroon authentication.", - }, - cli.StringFlag{ - Name: "macaroonpath", - Usage: "The path to macaroon file.", - TakesFile: true, - EnvVar: envVarMacaroonPath, - }, - cli.Int64Flag{ - Name: "macaroontimeout", - Value: 60, - Usage: "Anti-replay macaroon validity time in " + - "seconds.", - EnvVar: envVarMacaroonTimeout, - }, - cli.StringFlag{ - Name: "macaroonip", - Usage: "If set, lock macaroon to specific IP address.", - EnvVar: envVarMacaroonIP, - }, - cli.StringFlag{ - Name: "profile, p", - Usage: "Instead of reading settings from command " + - "line parameters or using the default " + - "profile, use a specific profile. If " + - "a default profile is set, this flag can be " + - "set to an empty string to disable reading " + - "values from the profiles file.", - EnvVar: envVarProfile, - }, - cli.StringFlag{ - Name: "macfromjar", - Usage: "Use this macaroon from the profile's " + - "macaroon jar instead of the default one. " + - "Can only be used if profiles are defined.", - EnvVar: envVarMacFromJar, - }, - cli.StringSliceFlag{ - Name: "metadata", - Usage: "This flag can be used to specify a key-value " + - "pair that should be appended to the " + - "outgoing context before the request is sent " + - "to lnd. This flag may be specified multiple " + - "times. The format is: \"key:value\".", - }, - cli.BoolFlag{ - Name: "insecure", - Usage: "Connect to the rpc server without TLS " + - "authentication", - Hidden: true, - }, - } - app.Commands = []cli.Command{ - createCommand, - createWatchOnlyCommand, - unlockCommand, - changePasswordCommand, - newAddressCommand, - estimateFeeCommand, - sendManyCommand, - sendCoinsCommand, - listUnspentCommand, - connectCommand, - disconnectCommand, - openChannelCommand, - batchOpenChannelCommand, - closeChannelCommand, - closeAllChannelsCommand, - abandonChannelCommand, - listPeersCommand, - walletBalanceCommand, - channelBalanceCommand, - getInfoCommand, - getDebugInfoCommand, - encryptDebugPackageCommand, - decryptDebugPackageCommand, - getRecoveryInfoCommand, - pendingChannelsCommand, - sendPaymentCommand, - payInvoiceCommand, - sendToRouteCommand, - addInvoiceCommand, - lookupInvoiceCommand, - listInvoicesCommand, - listChannelsCommand, - closedChannelsCommand, - listPaymentsCommand, - describeGraphCommand, - getNodeMetricsCommand, - getChanInfoCommand, - getNodeInfoCommand, - queryRoutesCommand, - getNetworkInfoCommand, - debugLevelCommand, - decodePayReqCommand, - listChainTxnsCommand, - stopCommand, - signMessageCommand, - verifyMessageCommand, - feeReportCommand, - updateChannelPolicyCommand, - forwardingHistoryCommand, - exportChanBackupCommand, - verifyChanBackupCommand, - restoreChanBackupCommand, - bakeMacaroonCommand, - listMacaroonIDsCommand, - deleteMacaroonIDCommand, - listPermissionsCommand, - printMacaroonCommand, - constrainMacaroonCommand, - trackPaymentCommand, - versionCommand, - profileSubCommand, - getStateCommand, - deletePaymentsCommand, - sendCustomCommand, - subscribeCustomCommand, - fishCompletionCommand, - listAliasesCommand, - estimateRouteFeeCommand, - generateManPageCommand, - } - - // Add any extra commands determined by build flags. - app.Commands = append(app.Commands, autopilotCommands()...) - app.Commands = append(app.Commands, invoicesCommands()...) - app.Commands = append(app.Commands, neutrinoCommands()...) - app.Commands = append(app.Commands, routerCommands()...) - app.Commands = append(app.Commands, walletCommands()...) - app.Commands = append(app.Commands, watchtowerCommands()...) - app.Commands = append(app.Commands, wtclientCommands()...) - app.Commands = append(app.Commands, devCommands()...) - app.Commands = append(app.Commands, peersCommands()...) - app.Commands = append(app.Commands, chainCommands()...) - - if err := app.Run(os.Args); err != nil { - fatal(err) - } -} - -// readPassword reads a password from the terminal. This requires there to be an -// actual TTY so passing in a password from stdin won't work. -func readPassword(text string) ([]byte, error) { - fmt.Print(text) - - // The variable syscall.Stdin is of a different type in the Windows API - // that's why we need the explicit cast. And of course the linter - // doesn't like it either. - pw, err := term.ReadPassword(int(syscall.Stdin)) // nolint:unconvert - fmt.Println() - return pw, err -} - -// networkParams parses the global network flag into a chaincfg.Params. -func networkParams(ctx *cli.Context) (*chaincfg.Params, error) { - network := strings.ToLower(ctx.GlobalString("network")) - switch network { - case "mainnet": - return &chaincfg.MainNetParams, nil - - case "testnet": - return &chaincfg.TestNet3Params, nil - - case "regtest": - return &chaincfg.RegressionNetParams, nil - - case "simnet": - return &chaincfg.SimNetParams, nil - - case "signet": - return &chaincfg.SigNetParams, nil - - default: - return nil, fmt.Errorf("unknown network: %v", network) - } -} - -// parseCoinSelectionStrategy parses a coin selection strategy string -// from the CLI to its lnrpc.CoinSelectionStrategy counterpart proto type. -func parseCoinSelectionStrategy(ctx *cli.Context) ( - lnrpc.CoinSelectionStrategy, error) { - - strategy := ctx.String(coinSelectionStrategyFlag.Name) - if !ctx.IsSet(coinSelectionStrategyFlag.Name) { - return lnrpc.CoinSelectionStrategy_STRATEGY_USE_GLOBAL_CONFIG, - nil - } - - switch strategy { - case "global-config": - return lnrpc.CoinSelectionStrategy_STRATEGY_USE_GLOBAL_CONFIG, - nil - - case "largest": - return lnrpc.CoinSelectionStrategy_STRATEGY_LARGEST, nil - - case "random": - return lnrpc.CoinSelectionStrategy_STRATEGY_RANDOM, nil - - default: - return 0, fmt.Errorf("unknown coin selection strategy "+ - "%v", strategy) - } + commands.Main() } From 29f139225bd96aab0124d70b7430c818ebff50d8 Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Thu, 11 Apr 2024 14:22:17 +0200 Subject: [PATCH 018/218] lnwallet: export AnchorSize --- lnwallet/channel.go | 8 ++++---- lnwallet/channel_test.go | 8 ++++---- lnwallet/commitment.go | 10 +++++----- lnwallet/reservation.go | 2 +- lnwallet/test_utils.go | 2 +- lnwallet/transactions_test.go | 2 +- 6 files changed, 16 insertions(+), 16 deletions(-) diff --git a/lnwallet/channel.go b/lnwallet/channel.go index 737c1d92e9..59d9921a7c 100644 --- a/lnwallet/channel.go +++ b/lnwallet/channel.go @@ -7726,7 +7726,7 @@ func NewAnchorResolution(chanState *channeldb.OpenChannel, WitnessScript: anchorWitnessScript, Output: &wire.TxOut{ PkScript: localAnchor.PkScript(), - Value: int64(anchorSize), + Value: int64(AnchorSize), }, HashType: sweepSigHash(chanState.ChanType), } @@ -7738,7 +7738,7 @@ func NewAnchorResolution(chanState *channeldb.OpenChannel, //nolint:lll signDesc.PrevOutputFetcher = txscript.NewCannedPrevOutputFetcher( - localAnchor.PkScript(), int64(anchorSize), + localAnchor.PkScript(), int64(AnchorSize), ) // For anchor outputs with taproot channels, the key desc is @@ -8281,7 +8281,7 @@ func (lc *LightningChannel) LocalBalanceDust() bool { // regain the stats allocated to the anchor outputs with the co-op // close transaction. if chanState.ChanType.HasAnchors() && chanState.IsInitiator { - localBalance += 2 * anchorSize + localBalance += 2 * AnchorSize } return localBalance <= chanState.LocalChanCfg.DustLimit @@ -8301,7 +8301,7 @@ func (lc *LightningChannel) RemoteBalanceDust() bool { // regain the stats allocated to the anchor outputs with the co-op // close transaction. if chanState.ChanType.HasAnchors() && !chanState.IsInitiator { - remoteBalance += 2 * anchorSize + remoteBalance += 2 * AnchorSize } return remoteBalance <= chanState.RemoteChanCfg.DustLimit diff --git a/lnwallet/channel_test.go b/lnwallet/channel_test.go index 0281c59bd4..a159e61fe2 100644 --- a/lnwallet/channel_test.go +++ b/lnwallet/channel_test.go @@ -705,7 +705,7 @@ func TestCooperativeChannelClosure(t *testing.T) { testCoopClose(t, &coopCloseTestCase{ chanType: channeldb.SingleFunderTweaklessBit | channeldb.AnchorOutputsBit, - anchorAmt: anchorSize * 2, + anchorAmt: AnchorSize * 2, }) }) } @@ -815,7 +815,7 @@ func TestForceClose(t *testing.T) { chanType: channeldb.SingleFunderTweaklessBit | channeldb.AnchorOutputsBit, expectedCommitWeight: input.AnchorCommitWeight, - anchorAmt: anchorSize * 2, + anchorAmt: AnchorSize * 2, }) }) t.Run("taproot", func(t *testing.T) { @@ -824,7 +824,7 @@ func TestForceClose(t *testing.T) { channeldb.AnchorOutputsBit | channeldb.SimpleTaprootFeatureBit, expectedCommitWeight: input.TaprootCommitWeight, - anchorAmt: anchorSize * 2, + anchorAmt: AnchorSize * 2, }) }) } @@ -910,7 +910,7 @@ func testForceClose(t *testing.T, testCase *forceCloseTestCase) { t.Fatal("commit tx not referenced by anchor res") } if anchorRes.AnchorSignDescriptor.Output.Value != - int64(anchorSize) { + int64(AnchorSize) { t.Fatal("unexpected anchor size") } diff --git a/lnwallet/commitment.go b/lnwallet/commitment.go index 2cf58f494e..6b6e8f5c21 100644 --- a/lnwallet/commitment.go +++ b/lnwallet/commitment.go @@ -17,8 +17,8 @@ import ( "github.com/lightningnetwork/lnd/lnwire" ) -// anchorSize is the constant anchor output size. -const anchorSize = btcutil.Amount(330) +// AnchorSize is the constant anchor output size. +const AnchorSize = btcutil.Amount(330) // DefaultAnchorsCommitMaxFeeRateSatPerVByte is the default max fee rate in // sat/vbyte the initiator will use for anchor channels. This should be enough @@ -942,7 +942,7 @@ func CreateCommitTx(chanType channeldb.ChannelType, if localOutput || numHTLCs > 0 { commitTx.AddTxOut(&wire.TxOut{ PkScript: localAnchor.PkScript(), - Value: int64(anchorSize), + Value: int64(AnchorSize), }) } @@ -951,7 +951,7 @@ func CreateCommitTx(chanType channeldb.ChannelType, if remoteOutput || numHTLCs > 0 { commitTx.AddTxOut(&wire.TxOut{ PkScript: remoteAnchor.PkScript(), - Value: int64(anchorSize), + Value: int64(AnchorSize), }) } } @@ -976,7 +976,7 @@ func CoopCloseBalance(chanType channeldb.ChannelType, isInitiator bool, // Since the initiator's balance also is stored after subtracting the // anchor values, add that back in case this was an anchor commitment. if chanType.HasAnchors() { - initiatorDelta += 2 * anchorSize + initiatorDelta += 2 * AnchorSize } // The initiator will pay the full coop close fee, subtract that value diff --git a/lnwallet/reservation.go b/lnwallet/reservation.go index 529db11418..df6c8fd940 100644 --- a/lnwallet/reservation.go +++ b/lnwallet/reservation.go @@ -261,7 +261,7 @@ func NewChannelReservation(capacity, localFundingAmt btcutil.Amount, // addition to the two anchor outputs. feeMSat := lnwire.NewMSatFromSatoshis(commitFee) if req.CommitType.HasAnchors() { - feeMSat += 2 * lnwire.NewMSatFromSatoshis(anchorSize) + feeMSat += 2 * lnwire.NewMSatFromSatoshis(AnchorSize) } // Used to cut down on verbosity. diff --git a/lnwallet/test_utils.go b/lnwallet/test_utils.go index 1e5af77031..5f575e3090 100644 --- a/lnwallet/test_utils.go +++ b/lnwallet/test_utils.go @@ -258,7 +258,7 @@ func CreateTestChannels(t *testing.T, chanType channeldb.ChannelType, commitFee := calcStaticFee(chanType, 0) var anchorAmt btcutil.Amount if chanType.HasAnchors() { - anchorAmt += 2 * anchorSize + anchorAmt += 2 * AnchorSize } aliceBalance := lnwire.NewMSatFromSatoshis( diff --git a/lnwallet/transactions_test.go b/lnwallet/transactions_test.go index 4c7fbed53b..e9751c6b9b 100644 --- a/lnwallet/transactions_test.go +++ b/lnwallet/transactions_test.go @@ -925,7 +925,7 @@ func createTestChannelsForVectors(tc *testContext, chanType channeldb.ChannelTyp var anchorAmt btcutil.Amount if chanType.HasAnchors() { - anchorAmt = 2 * anchorSize + anchorAmt = 2 * AnchorSize } remoteCommitTx, localCommitTx, err := CreateCommitmentTxns( From 89f75e7d1bd9d74f5f81ba200d40f96a4c23ef4a Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Thu, 11 Apr 2024 14:23:59 +0200 Subject: [PATCH 019/218] lnwallet: export GenTaprootHtlcScript --- lnwallet/commitment.go | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/lnwallet/commitment.go b/lnwallet/commitment.go index 6b6e8f5c21..dadf23431d 100644 --- a/lnwallet/commitment.go +++ b/lnwallet/commitment.go @@ -1075,9 +1075,9 @@ func genSegwitV0HtlcScript(chanType channeldb.ChannelType, }, nil } -// genTaprootHtlcScript generates the HTLC scripts for a taproot+musig2 +// GenTaprootHtlcScript generates the HTLC scripts for a taproot+musig2 // channel. -func genTaprootHtlcScript(isIncoming bool, whoseCommit lntypes.ChannelParty, +func GenTaprootHtlcScript(isIncoming bool, whoseCommit lntypes.ChannelParty, timeout uint32, rHash [32]byte, keyRing *CommitmentKeyRing, ) (*input.HtlcScriptTree, error) { @@ -1138,8 +1138,7 @@ func genTaprootHtlcScript(isIncoming bool, whoseCommit lntypes.ChannelParty, // along side the multiplexer. func genHtlcScript(chanType channeldb.ChannelType, isIncoming bool, whoseCommit lntypes.ChannelParty, timeout uint32, rHash [32]byte, - keyRing *CommitmentKeyRing, -) (input.ScriptDescriptor, error) { + keyRing *CommitmentKeyRing) (input.ScriptDescriptor, error) { if !chanType.IsTaproot() { return genSegwitV0HtlcScript( @@ -1148,7 +1147,7 @@ func genHtlcScript(chanType channeldb.ChannelType, isIncoming bool, ) } - return genTaprootHtlcScript( + return GenTaprootHtlcScript( isIncoming, whoseCommit, timeout, rHash, keyRing, ) } From 4fd1abfe5272eb8deaffb2aadc8771f044aaa0ef Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Thu, 11 Apr 2024 14:26:15 +0200 Subject: [PATCH 020/218] lnwallet: add Tree() method, fix formatting --- input/script_desc.go | 11 ++++--- input/script_utils.go | 75 ++++++++++++++++++++++++++---------------- lnwallet/commitment.go | 28 ++++++++-------- 3 files changed, 66 insertions(+), 48 deletions(-) diff --git a/input/script_desc.go b/input/script_desc.go index a32ae66316..17b58b7219 100644 --- a/input/script_desc.go +++ b/input/script_desc.go @@ -33,17 +33,17 @@ const ( ScriptPathDelay ) -// ScriptDesciptor is an interface that abstracts over the various ways a +// ScriptDescriptor is an interface that abstracts over the various ways a // pkScript can be spent from an output. This supports both normal p2wsh -// (witness script, etc), and also tapscript paths which have distinct +// (witness script, etc.), and also tapscript paths which have distinct // tapscript leaves. type ScriptDescriptor interface { // PkScript is the public key script that commits to the final // contract. PkScript() []byte - // WitnessScript returns the witness script that we'll use when signing - // for the remote party, and also verifying signatures on our + // WitnessScriptToSign returns the witness script that we'll use when + // signing for the remote party, and also verifying signatures on our // transactions. As an example, when we create an outgoing HTLC for the // remote party, we want to sign their success path. // @@ -73,6 +73,9 @@ type TapscriptDescriptor interface { // TapScriptTree returns the underlying tapscript tree. TapScriptTree() *txscript.IndexedTapScriptTree + + // Tree returns the underlying ScriptTree. + Tree() ScriptTree } // ScriptTree holds the contents needed to spend a script within a tapscript diff --git a/input/script_utils.go b/input/script_utils.go index 104c242510..d801846f8c 100644 --- a/input/script_utils.go +++ b/input/script_utils.go @@ -689,8 +689,8 @@ func (h *HtlcScriptTree) WitnessScriptForPath(path ScriptPath) ([]byte, error) { // CtrlBlockForPath returns the control block for the given spending path. For // script types that don't have a control block, nil is returned. -func (h *HtlcScriptTree) CtrlBlockForPath(path ScriptPath, -) (*txscript.ControlBlock, error) { +func (h *HtlcScriptTree) CtrlBlockForPath( + path ScriptPath) (*txscript.ControlBlock, error) { switch path { case ScriptPathSuccess: @@ -708,6 +708,11 @@ func (h *HtlcScriptTree) CtrlBlockForPath(path ScriptPath, } } +// Tree returns the underlying ScriptTree of the HtlcScriptTree. +func (h *HtlcScriptTree) Tree() ScriptTree { + return h.ScriptTree +} + // A compile time check to ensure HtlcScriptTree implements the // TapscriptMultiplexer interface. var _ TapscriptDescriptor = (*HtlcScriptTree)(nil) @@ -1690,9 +1695,9 @@ func TaprootSecondLevelScriptTree(revokeKey, delayKey *btcec.PublicKey, }, nil } -// WitnessScript returns the witness script that we'll use when signing for the -// remote party, and also verifying signatures on our transactions. As an -// example, when we create an outgoing HTLC for the remote party, we want to +// WitnessScriptToSign returns the witness script that we'll use when signing +// for the remote party, and also verifying signatures on our transactions. As +// an example, when we create an outgoing HTLC for the remote party, we want to // sign their success path. func (s *SecondLevelScriptTree) WitnessScriptToSign() []byte { return s.SuccessTapLeaf.Script @@ -1700,8 +1705,8 @@ func (s *SecondLevelScriptTree) WitnessScriptToSign() []byte { // WitnessScriptForPath returns the witness script for the given spending path. // An error is returned if the path is unknown. -func (s *SecondLevelScriptTree) WitnessScriptForPath(path ScriptPath, -) ([]byte, error) { +func (s *SecondLevelScriptTree) WitnessScriptForPath( + path ScriptPath) ([]byte, error) { switch path { case ScriptPathDelay: @@ -1716,8 +1721,8 @@ func (s *SecondLevelScriptTree) WitnessScriptForPath(path ScriptPath, // CtrlBlockForPath returns the control block for the given spending path. For // script types that don't have a control block, nil is returned. -func (s *SecondLevelScriptTree) CtrlBlockForPath(path ScriptPath, -) (*txscript.ControlBlock, error) { +func (s *SecondLevelScriptTree) CtrlBlockForPath( + path ScriptPath) (*txscript.ControlBlock, error) { switch path { case ScriptPathDelay: @@ -1733,6 +1738,11 @@ func (s *SecondLevelScriptTree) CtrlBlockForPath(path ScriptPath, } } +// Tree returns the underlying ScriptTree of the SecondLevelScriptTree. +func (s *SecondLevelScriptTree) Tree() ScriptTree { + return s.ScriptTree +} + // A compile time check to ensure SecondLevelScriptTree implements the // TapscriptDescriptor interface. var _ TapscriptDescriptor = (*SecondLevelScriptTree)(nil) @@ -2069,9 +2079,9 @@ type CommitScriptTree struct { // TapscriptDescriptor interface. var _ TapscriptDescriptor = (*CommitScriptTree)(nil) -// WitnessScript returns the witness script that we'll use when signing for the -// remote party, and also verifying signatures on our transactions. As an -// example, when we create an outgoing HTLC for the remote party, we want to +// WitnessScriptToSign returns the witness script that we'll use when signing +// for the remote party, and also verifying signatures on our transactions. As +// an example, when we create an outgoing HTLC for the remote party, we want to // sign their success path. func (c *CommitScriptTree) WitnessScriptToSign() []byte { // TODO(roasbeef): abstraction leak here? always dependent @@ -2080,8 +2090,8 @@ func (c *CommitScriptTree) WitnessScriptToSign() []byte { // WitnessScriptForPath returns the witness script for the given spending path. // An error is returned if the path is unknown. -func (c *CommitScriptTree) WitnessScriptForPath(path ScriptPath, -) ([]byte, error) { +func (c *CommitScriptTree) WitnessScriptForPath( + path ScriptPath) ([]byte, error) { switch path { // For the commitment output, the delay and success path are the same, @@ -2099,8 +2109,8 @@ func (c *CommitScriptTree) WitnessScriptForPath(path ScriptPath, // CtrlBlockForPath returns the control block for the given spending path. For // script types that don't have a control block, nil is returned. -func (c *CommitScriptTree) CtrlBlockForPath(path ScriptPath, -) (*txscript.ControlBlock, error) { +func (c *CommitScriptTree) CtrlBlockForPath( + path ScriptPath) (*txscript.ControlBlock, error) { switch path { case ScriptPathDelay: @@ -2120,6 +2130,11 @@ func (c *CommitScriptTree) CtrlBlockForPath(path ScriptPath, } } +// Tree returns the underlying ScriptTree of the CommitScriptTree. +func (c *CommitScriptTree) Tree() ScriptTree { + return c.ScriptTree +} + // NewLocalCommitScriptTree returns a new CommitScript tree that can be used to // create and spend the commitment output for the local party. func NewLocalCommitScriptTree(csvTimeout uint32, @@ -2241,7 +2256,7 @@ func TaprootCommitScriptToSelf(csvTimeout uint32, return commitScriptTree.TaprootKey, nil } -// MakeTaprootSCtrlBlock takes a leaf script, the internal key (usually the +// MakeTaprootCtrlBlock takes a leaf script, the internal key (usually the // revoke key), and a script tree and creates a valid control block for a spend // of the leaf. func MakeTaprootCtrlBlock(leafScript []byte, internalKey *btcec.PublicKey, @@ -2296,9 +2311,6 @@ func TaprootCommitSpendSuccess(signer Signer, signDesc *SignDescriptor, witnessStack[0] = maybeAppendSighash(sweepSig, signDesc.HashType) witnessStack[1] = signDesc.WitnessScript witnessStack[2] = ctrlBlockBytes - if err != nil { - return nil, err - } return witnessStack, nil } @@ -2773,8 +2785,8 @@ type AnchorScriptTree struct { // NewAnchorScriptTree makes a new script tree for an anchor output with the // passed anchor key. -func NewAnchorScriptTree(anchorKey *btcec.PublicKey, -) (*AnchorScriptTree, error) { +func NewAnchorScriptTree( + anchorKey *btcec.PublicKey) (*AnchorScriptTree, error) { // The main script used is just a OP_16 CSV (anyone can sweep after 16 // blocks). @@ -2810,9 +2822,9 @@ func NewAnchorScriptTree(anchorKey *btcec.PublicKey, }, nil } -// WitnessScript returns the witness script that we'll use when signing for the -// remote party, and also verifying signatures on our transactions. As an -// example, when we create an outgoing HTLC for the remote party, we want to +// WitnessScriptToSign returns the witness script that we'll use when signing +// for the remote party, and also verifying signatures on our transactions. As +// an example, when we create an outgoing HTLC for the remote party, we want to // sign their success path. func (a *AnchorScriptTree) WitnessScriptToSign() []byte { return a.SweepLeaf.Script @@ -2820,8 +2832,8 @@ func (a *AnchorScriptTree) WitnessScriptToSign() []byte { // WitnessScriptForPath returns the witness script for the given spending path. // An error is returned if the path is unknown. -func (a *AnchorScriptTree) WitnessScriptForPath(path ScriptPath, -) ([]byte, error) { +func (a *AnchorScriptTree) WitnessScriptForPath( + path ScriptPath) ([]byte, error) { switch path { case ScriptPathDelay: @@ -2836,8 +2848,8 @@ func (a *AnchorScriptTree) WitnessScriptForPath(path ScriptPath, // CtrlBlockForPath returns the control block for the given spending path. For // script types that don't have a control block, nil is returned. -func (a *AnchorScriptTree) CtrlBlockForPath(path ScriptPath, -) (*txscript.ControlBlock, error) { +func (a *AnchorScriptTree) CtrlBlockForPath( + path ScriptPath) (*txscript.ControlBlock, error) { switch path { case ScriptPathDelay: @@ -2853,6 +2865,11 @@ func (a *AnchorScriptTree) CtrlBlockForPath(path ScriptPath, } } +// Tree returns the underlying ScriptTree of the AnchorScriptTree. +func (a *AnchorScriptTree) Tree() ScriptTree { + return a.ScriptTree +} + // A compile time check to ensure AnchorScriptTree implements the // TapscriptDescriptor interface. var _ TapscriptDescriptor = (*AnchorScriptTree)(nil) diff --git a/lnwallet/commitment.go b/lnwallet/commitment.go index dadf23431d..f32408814c 100644 --- a/lnwallet/commitment.go +++ b/lnwallet/commitment.go @@ -200,9 +200,9 @@ func (w *WitnessScriptDesc) PkScript() []byte { return w.OutputScript } -// WitnessScript returns the witness script that we'll use when signing for the -// remote party, and also verifying signatures on our transactions. As an -// example, when we create an outgoing HTLC for the remote party, we want to +// WitnessScriptToSign returns the witness script that we'll use when signing +// for the remote party, and also verifying signatures on our transactions. As +// an example, when we create an outgoing HTLC for the remote party, we want to // sign their success path. func (w *WitnessScriptDesc) WitnessScriptToSign() []byte { return w.WitnessScript @@ -210,10 +210,10 @@ func (w *WitnessScriptDesc) WitnessScriptToSign() []byte { // WitnessScriptForPath returns the witness script for the given spending path. // An error is returned if the path is unknown. This is useful as when -// constructing a contrl block for a given path, one also needs witness script +// constructing a control block for a given path, one also needs witness script // being signed. -func (w *WitnessScriptDesc) WitnessScriptForPath(_ input.ScriptPath, -) ([]byte, error) { +func (w *WitnessScriptDesc) WitnessScriptForPath( + _ input.ScriptPath) ([]byte, error) { return w.WitnessScript, nil } @@ -532,8 +532,8 @@ func CommitScriptAnchors(chanType channeldb.ChannelType, input.ScriptDescriptor, input.ScriptDescriptor, error) { var ( - anchorScript func(key *btcec.PublicKey) ( - input.ScriptDescriptor, error) + anchorScript func( + key *btcec.PublicKey) (input.ScriptDescriptor, error) keySelector func(*channeldb.ChannelConfig, bool) *btcec.PublicKey @@ -544,12 +544,10 @@ func CommitScriptAnchors(chanType channeldb.ChannelType, // level key is now the (relative) local delay and remote public key, // since these are fully revealed once the commitment hits the chain. case chanType.IsTaproot(): - anchorScript = func(key *btcec.PublicKey, - ) (input.ScriptDescriptor, error) { + anchorScript = func( + key *btcec.PublicKey) (input.ScriptDescriptor, error) { - return input.NewAnchorScriptTree( - key, - ) + return input.NewAnchorScriptTree(key) } keySelector = func(cfg *channeldb.ChannelConfig, @@ -567,8 +565,8 @@ func CommitScriptAnchors(chanType channeldb.ChannelType, default: // For normal channels, we'll create a p2wsh script based on // the target key. - anchorScript = func(key *btcec.PublicKey, - ) (input.ScriptDescriptor, error) { + anchorScript = func( + key *btcec.PublicKey) (input.ScriptDescriptor, error) { script, err := input.CommitScriptAnchor(key) if err != nil { From 2d6ba8c0569cede4ebe2bdc43ad10ed3f391e59e Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Thu, 2 May 2024 14:38:13 +0200 Subject: [PATCH 021/218] cmd/commands: export StripPrefix --- cmd/commands/arg_parse.go | 4 ++-- cmd/commands/arg_parse_test.go | 2 +- cmd/commands/cmd_invoice.go | 2 +- cmd/commands/cmd_payments.go | 6 +++--- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/cmd/commands/arg_parse.go b/cmd/commands/arg_parse.go index 045f35509a..1d8bbe1988 100644 --- a/cmd/commands/arg_parse.go +++ b/cmd/commands/arg_parse.go @@ -42,7 +42,7 @@ func parseTime(s string, base time.Time) (uint64, error) { var lightningPrefix = "lightning:" -// stripPrefix removes accidentally copied 'lightning:' prefix. -func stripPrefix(s string) string { +// StripPrefix removes accidentally copied 'lightning:' prefix. +func StripPrefix(s string) string { return strings.TrimSpace(strings.TrimPrefix(s, lightningPrefix)) } diff --git a/cmd/commands/arg_parse_test.go b/cmd/commands/arg_parse_test.go index ead411fe61..35751098e4 100644 --- a/cmd/commands/arg_parse_test.go +++ b/cmd/commands/arg_parse_test.go @@ -111,7 +111,7 @@ func TestStripPrefix(t *testing.T) { t.Parallel() for _, test := range stripPrefixTests { - actual := stripPrefix(test.in) + actual := StripPrefix(test.in) require.Equal(t, test.expected, actual) } } diff --git a/cmd/commands/cmd_invoice.go b/cmd/commands/cmd_invoice.go index cd806b7003..5ea4c1e0b0 100644 --- a/cmd/commands/cmd_invoice.go +++ b/cmd/commands/cmd_invoice.go @@ -408,7 +408,7 @@ func decodePayReq(ctx *cli.Context) error { } resp, err := client.DecodePayReq(ctxc, &lnrpc.PayReqString{ - PayReq: stripPrefix(payreq), + PayReq: StripPrefix(payreq), }) if err != nil { return err diff --git a/cmd/commands/cmd_payments.go b/cmd/commands/cmd_payments.go index e28d5e8c99..c8787a6cc1 100644 --- a/cmd/commands/cmd_payments.go +++ b/cmd/commands/cmd_payments.go @@ -341,7 +341,7 @@ func SendPayment(ctx *cli.Context) error { // details of the payment are encoded within the request. if ctx.IsSet("pay_req") { req := &routerrpc.SendPaymentRequest{ - PaymentRequest: stripPrefix(ctx.String("pay_req")), + PaymentRequest: StripPrefix(ctx.String("pay_req")), Amt: ctx.Int64("amt"), DestCustomRecords: make(map[uint64][]byte), Amp: ctx.Bool(ampFlag.Name), @@ -920,7 +920,7 @@ func payInvoice(ctx *cli.Context) error { } req := &routerrpc.SendPaymentRequest{ - PaymentRequest: stripPrefix(payReq), + PaymentRequest: StripPrefix(payReq), Amt: ctx.Int64("amt"), DestCustomRecords: make(map[uint64][]byte), Amp: ctx.Bool(ampFlag.Name), @@ -1922,7 +1922,7 @@ func estimateRouteFee(ctx *cli.Context) error { req.AmtSat = amtSat case ctx.IsSet("pay_req"): - req.PaymentRequest = stripPrefix(ctx.String("pay_req")) + req.PaymentRequest = StripPrefix(ctx.String("pay_req")) if ctx.IsSet("timeout") { req.Timeout = uint32(ctx.Duration("timeout").Seconds()) } From 1447493081a20c6a05a5dc0f25534f6987967327 Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Thu, 23 May 2024 13:11:38 +0200 Subject: [PATCH 022/218] server: fix logging of pubkey --- server.go | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/server.go b/server.go index 69f19d9e0b..cc4c45e832 100644 --- a/server.go +++ b/server.go @@ -4136,11 +4136,12 @@ func (s *server) peerInitializer(p *peer.Brontide) { s.wg.Add(1) go s.peerTerminationWatcher(p, ready) + pubBytes := p.IdentityKey().SerializeCompressed() + // Start the peer! If an error occurs, we Disconnect the peer, which // will unblock the peerTerminationWatcher. if err := p.Start(); err != nil { - srvrLog.Warnf("Starting peer=%v got error: %v", - p.IdentityKey(), err) + srvrLog.Warnf("Starting peer=%x got error: %v", pubBytes, err) p.Disconnect(fmt.Errorf("unable to start peer: %w", err)) return @@ -4150,13 +4151,15 @@ func (s *server) peerInitializer(p *peer.Brontide) { // was successful, and to begin watching the peer's wait group. close(ready) - pubStr := string(p.IdentityKey().SerializeCompressed()) - s.mu.Lock() defer s.mu.Unlock() // Check if there are listeners waiting for this peer to come online. srvrLog.Debugf("Notifying that peer %v is online", p) + + // TODO(guggero): Do a proper conversion to a string everywhere, or use + // route.Vertex as the key type of peerConnectedListeners. + pubStr := string(pubBytes) for _, peerChan := range s.peerConnectedListeners[pubStr] { select { case peerChan <- p: From 830dd49fa87845c99a0fdfb78e8d12149fe11b30 Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Fri, 24 May 2024 17:19:50 +0200 Subject: [PATCH 023/218] routing+lnrpc: fix log statements --- lnrpc/routerrpc/router_server.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lnrpc/routerrpc/router_server.go b/lnrpc/routerrpc/router_server.go index 044c03ac70..30dba22272 100644 --- a/lnrpc/routerrpc/router_server.go +++ b/lnrpc/routerrpc/router_server.go @@ -1313,7 +1313,7 @@ func (s *Server) trackPayment(subscription routing.ControlTowerSubscriber, // Otherwise, we will log and return the error as the stream has // received an error from the payment lifecycle. - log.Errorf("TrackPayment got error for payment %x: %v", identifier, err) + log.Errorf("TrackPayment got error for payment %v: %v", identifier, err) return err } From e71627f893379173a7864ca3347523b40f245421 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Tue, 12 Mar 2024 12:25:53 -0400 Subject: [PATCH 024/218] channeldb: consolidate root bucket TLVs into new struct In this commit, we consolidate the root bucket TLVs into a new struct. This makes it easier to see all the new TLV fields at a glance. We also convert TLV usage to use the new type param based APis. --- channeldb/channel.go | 240 ++++++++++++++++++++++++++----------------- 1 file changed, 145 insertions(+), 95 deletions(-) diff --git a/channeldb/channel.go b/channeldb/channel.go index 4e3db2fc1d..ec449613e4 100644 --- a/channeldb/channel.go +++ b/channeldb/channel.go @@ -226,27 +226,82 @@ const ( // A tlv type definition used to serialize an outpoint's indexStatus // for use in the outpoint index. indexStatusType tlv.Type = 0 +) - // A tlv type definition used to serialize and deserialize a KeyLocator - // from the database. - keyLocType tlv.Type = 1 +// openChannelTlvData houses the new data fields that are stored for each +// channel in a TLV stream within the root bucket. This is stored as a TLV +// stream appended to the existing hard-coded fields in the channel's root +// bucket. New fields being added to the channel state should be added here. +// +// NOTE: This struct is used for serialization purposes only and its fields +// should be accessed via the OpenChannel struct while in memory. +type openChannelTlvData struct { + // revokeKeyLoc is the key locator for the revocation key. + revokeKeyLoc tlv.RecordT[tlv.TlvType1, keyLocRecord] - // A tlv type used to serialize and deserialize the - // `InitialLocalBalance` field. - initialLocalBalanceType tlv.Type = 2 + // initialLocalBalance is the initial local balance of the channel. + initialLocalBalance tlv.RecordT[tlv.TlvType2, uint64] - // A tlv type used to serialize and deserialize the - // `InitialRemoteBalance` field. - initialRemoteBalanceType tlv.Type = 3 + // initialRemoteBalance is the initial remote balance of the channel. + initialRemoteBalance tlv.RecordT[tlv.TlvType3, uint64] - // A tlv type definition used to serialize and deserialize the - // confirmed ShortChannelID for a zero-conf channel. - realScidType tlv.Type = 4 + // realScid is the real short channel ID of the channel corresponding to + // the on-chain outpoint. + realScid tlv.RecordT[tlv.TlvType4, lnwire.ShortChannelID] - // A tlv type definition used to serialize and deserialize the - // Memo for the channel channel. - channelMemoType tlv.Type = 5 -) + // memo is an optional text field that gives context to the user about + // the channel. + memo tlv.OptionalRecordT[tlv.TlvType5, []byte] +} + +// encode serializes the openChannelTlvData to the given io.Writer. +func (c *openChannelTlvData) encode(w io.Writer) error { + tlvRecords := []tlv.Record{ + c.revokeKeyLoc.Record(), + c.initialLocalBalance.Record(), + c.initialRemoteBalance.Record(), + c.realScid.Record(), + } + c.memo.WhenSome(func(memo tlv.RecordT[tlv.TlvType5, []byte]) { + tlvRecords = append(tlvRecords, memo.Record()) + }) + + // Create the tlv stream. + tlvStream, err := tlv.NewStream(tlvRecords...) + if err != nil { + return err + } + + return tlvStream.Encode(w) +} + +// decode deserializes the openChannelTlvData from the given io.Reader. +func (c *openChannelTlvData) decode(r io.Reader) error { + memo := c.memo.Zero() + + // Create the tlv stream. + tlvStream, err := tlv.NewStream( + c.revokeKeyLoc.Record(), + c.initialLocalBalance.Record(), + c.initialRemoteBalance.Record(), + c.realScid.Record(), + memo.Record(), + ) + if err != nil { + return err + } + + tlvs, err := tlvStream.DecodeWithParsedTypes(r) + if err != nil { + return err + } + + if _, ok := tlvs[memo.TlvType()]; ok { + c.memo = tlv.SomeRecordT(memo) + } + + return nil +} // indexStatus is an enum-like type that describes what state the // outpoint is in. Currently only two possible values. @@ -867,6 +922,10 @@ type OpenChannel struct { // channel that will be useful to our future selves. Memo []byte + // TapscriptRoot is an optional tapscript root used to derive the MuSig2 + // funding output. + TapscriptRoot fn.Option[chainhash.Hash] + // TODO(roasbeef): eww Db *ChannelStateDB @@ -1025,6 +1084,48 @@ func (c *OpenChannel) SetBroadcastHeight(height uint32) { c.FundingBroadcastHeight = height } +// amendTlvData updates the channel with the given auxiliary TLV data. +func (c *OpenChannel) amendTlvData(auxData openChannelTlvData) { + c.RevocationKeyLocator = auxData.revokeKeyLoc.Val.KeyLocator + c.InitialLocalBalance = lnwire.MilliSatoshi( + auxData.initialLocalBalance.Val, + ) + c.InitialRemoteBalance = lnwire.MilliSatoshi( + auxData.initialRemoteBalance.Val, + ) + c.confirmedScid = auxData.realScid.Val + + auxData.memo.WhenSomeV(func(memo []byte) { + c.Memo = memo + }) +} + +// extractTlvData creates a new openChannelTlvData from the given channel. +func (c *OpenChannel) extractTlvData() openChannelTlvData { + auxData := openChannelTlvData{ + revokeKeyLoc: tlv.NewRecordT[tlv.TlvType1]( + keyLocRecord{c.RevocationKeyLocator}, + ), + initialLocalBalance: tlv.NewPrimitiveRecord[tlv.TlvType2]( + uint64(c.InitialLocalBalance), + ), + initialRemoteBalance: tlv.NewPrimitiveRecord[tlv.TlvType3]( + uint64(c.InitialRemoteBalance), + ), + realScid: tlv.NewRecordT[tlv.TlvType4]( + c.confirmedScid, + ), + } + + if len(c.Memo) != 0 { + auxData.memo = tlv.SomeRecordT( + tlv.NewPrimitiveRecord[tlv.TlvType5](c.Memo), + ) + } + + return auxData +} + // Refresh updates the in-memory channel state using the latest state observed // on disk. func (c *OpenChannel) Refresh() error { @@ -4030,32 +4131,9 @@ func putChanInfo(chanBucket kvdb.RwBucket, channel *OpenChannel) error { return err } - // Convert balance fields into uint64. - localBalance := uint64(channel.InitialLocalBalance) - remoteBalance := uint64(channel.InitialRemoteBalance) - - // Create the tlv stream. - tlvStream, err := tlv.NewStream( - // Write the RevocationKeyLocator as the first entry in a tlv - // stream. - MakeKeyLocRecord( - keyLocType, &channel.RevocationKeyLocator, - ), - tlv.MakePrimitiveRecord( - initialLocalBalanceType, &localBalance, - ), - tlv.MakePrimitiveRecord( - initialRemoteBalanceType, &remoteBalance, - ), - MakeScidRecord(realScidType, &channel.confirmedScid), - tlv.MakePrimitiveRecord(channelMemoType, &channel.Memo), - ) - if err != nil { - return err - } - - if err := tlvStream.Encode(&w); err != nil { - return err + auxData := channel.extractTlvData() + if err := auxData.encode(&w); err != nil { + return fmt.Errorf("unable to encode aux data: %w", err) } if err := chanBucket.Put(chanInfoKey, w.Bytes()); err != nil { @@ -4244,45 +4322,14 @@ func fetchChanInfo(chanBucket kvdb.RBucket, channel *OpenChannel) error { } } - // Create balance fields in uint64, and Memo field as byte slice. - var ( - localBalance uint64 - remoteBalance uint64 - memo []byte - ) - - // Create the tlv stream. - tlvStream, err := tlv.NewStream( - // Write the RevocationKeyLocator as the first entry in a tlv - // stream. - MakeKeyLocRecord( - keyLocType, &channel.RevocationKeyLocator, - ), - tlv.MakePrimitiveRecord( - initialLocalBalanceType, &localBalance, - ), - tlv.MakePrimitiveRecord( - initialRemoteBalanceType, &remoteBalance, - ), - MakeScidRecord(realScidType, &channel.confirmedScid), - tlv.MakePrimitiveRecord(channelMemoType, &memo), - ) - if err != nil { - return err - } - - if err := tlvStream.Decode(r); err != nil { - return err + var auxData openChannelTlvData + if err := auxData.decode(r); err != nil { + return fmt.Errorf("unable to decode aux data: %w", err) } - // Attach the balance fields. - channel.InitialLocalBalance = lnwire.MilliSatoshi(localBalance) - channel.InitialRemoteBalance = lnwire.MilliSatoshi(remoteBalance) - - // Attach the memo field if non-empty. - if len(memo) > 0 { - channel.Memo = memo - } + // Assign all the relevant fields from the aux data into the actual + // open channel. + channel.amendTlvData(auxData) channel.Packager = NewChannelPackager(channel.ShortChannelID) @@ -4440,6 +4487,25 @@ func deleteThawHeight(chanBucket kvdb.RwBucket) error { return chanBucket.Delete(frozenChanKey) } +// keyLocRecord is a wrapper struct around keychain.KeyLocator to implement the +// tlv.RecordProducer interface. +type keyLocRecord struct { + keychain.KeyLocator +} + +// Record creates a Record out of a KeyLocator using the passed Type and the +// EKeyLocator and DKeyLocator functions. The size will always be 8 as +// KeyFamily is uint32 and the Index is uint32. +// +// NOTE: This is part of the tlv.RecordProducer interface. +func (k *keyLocRecord) Record() tlv.Record { + // Note that we set the type here as zero, as when used with a + // tlv.RecordT, the type param will be used as the type. + return tlv.MakeStaticRecord( + 0, &k.KeyLocator, 8, EKeyLocator, DKeyLocator, + ) +} + // EKeyLocator is an encoder for keychain.KeyLocator. func EKeyLocator(w io.Writer, val interface{}, buf *[8]byte) error { if v, ok := val.(*keychain.KeyLocator); ok { @@ -4468,22 +4534,6 @@ func DKeyLocator(r io.Reader, val interface{}, buf *[8]byte, l uint64) error { return tlv.NewTypeForDecodingErr(val, "keychain.KeyLocator", l, 8) } -// MakeKeyLocRecord creates a Record out of a KeyLocator using the passed -// Type and the EKeyLocator and DKeyLocator functions. The size will always be -// 8 as KeyFamily is uint32 and the Index is uint32. -func MakeKeyLocRecord(typ tlv.Type, keyLoc *keychain.KeyLocator) tlv.Record { - return tlv.MakeStaticRecord(typ, keyLoc, 8, EKeyLocator, DKeyLocator) -} - -// MakeScidRecord creates a Record out of a ShortChannelID using the passed -// Type and the EShortChannelID and DShortChannelID functions. The size will -// always be 8 for the ShortChannelID. -func MakeScidRecord(typ tlv.Type, scid *lnwire.ShortChannelID) tlv.Record { - return tlv.MakeStaticRecord( - typ, scid, 8, lnwire.EShortChannelID, lnwire.DShortChannelID, - ) -} - // ShutdownInfo contains various info about the shutdown initiation of a // channel. type ShutdownInfo struct { From 74cb1f48adfc73bb790856ee0b764a96fe40de56 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Wed, 13 Mar 2024 09:15:54 -0400 Subject: [PATCH 025/218] channeldb: add optional TapscriptRoot field + feature bit --- channeldb/channel.go | 33 +++++++++++++++++++++++++++++++++ channeldb/channel_test.go | 8 +++++++- 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/channeldb/channel.go b/channeldb/channel.go index ec449613e4..f29a9ec196 100644 --- a/channeldb/channel.go +++ b/channeldb/channel.go @@ -252,6 +252,10 @@ type openChannelTlvData struct { // memo is an optional text field that gives context to the user about // the channel. memo tlv.OptionalRecordT[tlv.TlvType5, []byte] + + // tapscriptRoot is the optional Tapscript root the channel funding + // output commits to. + tapscriptRoot tlv.OptionalRecordT[tlv.TlvType6, [32]byte] } // encode serializes the openChannelTlvData to the given io.Writer. @@ -265,6 +269,11 @@ func (c *openChannelTlvData) encode(w io.Writer) error { c.memo.WhenSome(func(memo tlv.RecordT[tlv.TlvType5, []byte]) { tlvRecords = append(tlvRecords, memo.Record()) }) + c.tapscriptRoot.WhenSome( + func(root tlv.RecordT[tlv.TlvType6, [32]byte]) { + tlvRecords = append(tlvRecords, root.Record()) + }, + ) // Create the tlv stream. tlvStream, err := tlv.NewStream(tlvRecords...) @@ -278,6 +287,7 @@ func (c *openChannelTlvData) encode(w io.Writer) error { // decode deserializes the openChannelTlvData from the given io.Reader. func (c *openChannelTlvData) decode(r io.Reader) error { memo := c.memo.Zero() + tapscriptRoot := c.tapscriptRoot.Zero() // Create the tlv stream. tlvStream, err := tlv.NewStream( @@ -286,6 +296,7 @@ func (c *openChannelTlvData) decode(r io.Reader) error { c.initialRemoteBalance.Record(), c.realScid.Record(), memo.Record(), + tapscriptRoot.Record(), ) if err != nil { return err @@ -299,6 +310,9 @@ func (c *openChannelTlvData) decode(r io.Reader) error { if _, ok := tlvs[memo.TlvType()]; ok { c.memo = tlv.SomeRecordT(memo) } + if _, ok := tlvs[tapscriptRoot.TlvType()]; ok { + c.tapscriptRoot = tlv.SomeRecordT(tapscriptRoot) + } return nil } @@ -380,6 +394,11 @@ const ( // SimpleTaprootFeatureBit indicates that the simple-taproot-chans // feature bit was negotiated during the lifetime of the channel. SimpleTaprootFeatureBit ChannelType = 1 << 10 + + // TapscriptRootBit indicates that this is a MuSig2 channel with a top + // level tapscript commitment. This MUST be set along with the + // SimpleTaprootFeatureBit. + TapscriptRootBit ChannelType = 1 << 11 ) // IsSingleFunder returns true if the channel type if one of the known single @@ -450,6 +469,12 @@ func (c ChannelType) IsTaproot() bool { return c&SimpleTaprootFeatureBit == SimpleTaprootFeatureBit } +// HasTapscriptRoot returns true if the channel is using a top level tapscript +// root commitment. +func (c ChannelType) HasTapscriptRoot() bool { + return c&TapscriptRootBit == TapscriptRootBit +} + // ChannelStateBounds are the parameters from OpenChannel and AcceptChannel // that are responsible for providing bounds on the state space of the abstract // channel state. These values must be remembered for normal channel operation @@ -1098,6 +1123,9 @@ func (c *OpenChannel) amendTlvData(auxData openChannelTlvData) { auxData.memo.WhenSomeV(func(memo []byte) { c.Memo = memo }) + auxData.tapscriptRoot.WhenSomeV(func(h [32]byte) { + c.TapscriptRoot = fn.Some[chainhash.Hash](h) + }) } // extractTlvData creates a new openChannelTlvData from the given channel. @@ -1122,6 +1150,11 @@ func (c *OpenChannel) extractTlvData() openChannelTlvData { tlv.NewPrimitiveRecord[tlv.TlvType5](c.Memo), ) } + c.TapscriptRoot.WhenSome(func(h chainhash.Hash) { + auxData.tapscriptRoot = tlv.SomeRecordT( + tlv.NewPrimitiveRecord[tlv.TlvType6, [32]byte](h), + ) + }) return auxData } diff --git a/channeldb/channel_test.go b/channeldb/channel_test.go index a7f3c1ebee..84aae96225 100644 --- a/channeldb/channel_test.go +++ b/channeldb/channel_test.go @@ -17,6 +17,7 @@ import ( "github.com/davecgh/go-spew/spew" "github.com/lightningnetwork/lnd/channeldb/models" "github.com/lightningnetwork/lnd/clock" + "github.com/lightningnetwork/lnd/fn" "github.com/lightningnetwork/lnd/keychain" "github.com/lightningnetwork/lnd/kvdb" "github.com/lightningnetwork/lnd/lnmock" @@ -173,7 +174,7 @@ func fundingPointOption(chanPoint wire.OutPoint) testChannelOption { } // channelIDOption is an option which sets the short channel ID of the channel. -var channelIDOption = func(chanID lnwire.ShortChannelID) testChannelOption { +func channelIDOption(chanID lnwire.ShortChannelID) testChannelOption { return func(params *testChannelParams) { params.channel.ShortChannelID = chanID } @@ -326,6 +327,9 @@ func createTestChannelState(t *testing.T, cdb *ChannelStateDB) *OpenChannel { uniqueOutputIndex.Add(1) op := wire.OutPoint{Hash: key, Index: uniqueOutputIndex.Load()} + var tapscriptRoot chainhash.Hash + copy(tapscriptRoot[:], bytes.Repeat([]byte{1}, 32)) + return &OpenChannel{ ChanType: SingleFunderBit | FrozenBit, ChainHash: key, @@ -368,6 +372,8 @@ func createTestChannelState(t *testing.T, cdb *ChannelStateDB) *OpenChannel { ThawHeight: uint32(defaultPendingHeight), InitialLocalBalance: lnwire.MilliSatoshi(9000), InitialRemoteBalance: lnwire.MilliSatoshi(3000), + Memo: []byte("test"), + TapscriptRoot: fn.Some(tapscriptRoot), } } From 1e71b1e544bedf3965a337ff29e6d33980fb9076 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Wed, 13 Mar 2024 10:46:33 -0400 Subject: [PATCH 026/218] multi: add new tapscript root option to GenTaprootFundingScript This'll allow us to create a funding output that uses musig2, but uses a tapscript tweak rather than a normal BIP 86 tweak. --- contractcourt/chain_watcher.go | 2 +- funding/manager.go | 2 ++ graph/builder.go | 4 +++- input/script_utils.go | 24 +++++++++++++++--------- itest/lnd_funding_test.go | 2 ++ lnwallet/chanfunding/canned_assembler.go | 7 ++++--- lnwallet/channel.go | 1 + lnwallet/wallet.go | 10 ++++++++-- 8 files changed, 36 insertions(+), 16 deletions(-) diff --git a/contractcourt/chain_watcher.go b/contractcourt/chain_watcher.go index 3cbc7422de..155fca2a99 100644 --- a/contractcourt/chain_watcher.go +++ b/contractcourt/chain_watcher.go @@ -308,7 +308,7 @@ func (c *chainWatcher) Start() error { ) if chanState.ChanType.IsTaproot() { c.fundingPkScript, _, err = input.GenTaprootFundingScript( - localKey, remoteKey, 0, + localKey, remoteKey, 0, fn.None[chainhash.Hash](), ) if err != nil { return err diff --git a/funding/manager.go b/funding/manager.go index 61b03200e1..ad62860906 100644 --- a/funding/manager.go +++ b/funding/manager.go @@ -24,6 +24,7 @@ import ( "github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/channeldb/models" "github.com/lightningnetwork/lnd/discovery" + "github.com/lightningnetwork/lnd/fn" "github.com/lightningnetwork/lnd/graph" "github.com/lightningnetwork/lnd/input" "github.com/lightningnetwork/lnd/keychain" @@ -2899,6 +2900,7 @@ func makeFundingScript(channel *channeldb.OpenChannel) ([]byte, error) { if channel.ChanType.IsTaproot() { pkScript, _, err := input.GenTaprootFundingScript( localKey, remoteKey, int64(channel.Capacity), + fn.None[chainhash.Hash](), ) if err != nil { return nil, err diff --git a/graph/builder.go b/graph/builder.go index 82a36eb36a..7f7823ab4d 100644 --- a/graph/builder.go +++ b/graph/builder.go @@ -11,12 +11,14 @@ import ( "github.com/btcsuite/btcd/btcec/v2" "github.com/btcsuite/btcd/btcutil" + "github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/btcsuite/btcd/wire" "github.com/go-errors/errors" "github.com/lightningnetwork/lnd/batch" "github.com/lightningnetwork/lnd/chainntnfs" "github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/channeldb/models" + "github.com/lightningnetwork/lnd/fn" "github.com/lightningnetwork/lnd/input" "github.com/lightningnetwork/lnd/kvdb" "github.com/lightningnetwork/lnd/lnutils" @@ -1138,7 +1140,7 @@ func makeFundingScript(bitcoinKey1, bitcoinKey2 []byte, } fundingScript, _, err := input.GenTaprootFundingScript( - pubKey1, pubKey2, 0, + pubKey1, pubKey2, 0, fn.None[chainhash.Hash](), ) if err != nil { return nil, err diff --git a/input/script_utils.go b/input/script_utils.go index d801846f8c..d3a6dc9d31 100644 --- a/input/script_utils.go +++ b/input/script_utils.go @@ -11,8 +11,10 @@ import ( "github.com/btcsuite/btcd/btcec/v2/schnorr" "github.com/btcsuite/btcd/btcec/v2/schnorr/musig2" "github.com/btcsuite/btcd/btcutil" + "github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/btcsuite/btcd/txscript" "github.com/btcsuite/btcd/wire" + "github.com/lightningnetwork/lnd/fn" "github.com/lightningnetwork/lnd/lntypes" "github.com/lightningnetwork/lnd/lnutils" "golang.org/x/crypto/ripemd160" @@ -199,26 +201,30 @@ func GenFundingPkScript(aPub, bPub []byte, amt int64) ([]byte, *wire.TxOut, erro } // GenTaprootFundingScript constructs the taproot-native funding output that -// uses musig2 to create a single aggregated key to anchor the channel. +// uses MuSig2 to create a single aggregated key to anchor the channel. func GenTaprootFundingScript(aPub, bPub *btcec.PublicKey, - amt int64) ([]byte, *wire.TxOut, error) { + amt int64, tapscriptRoot fn.Option[chainhash.Hash]) ([]byte, + *wire.TxOut, error) { + + muSig2Opt := musig2.WithBIP86KeyTweak() + tapscriptRoot.WhenSome(func(scriptRoot chainhash.Hash) { + muSig2Opt = musig2.WithTaprootKeyTweak(scriptRoot[:]) + }) // Similar to the existing p2wsh funding script, we'll always make sure // we sort the keys before any major operations. In order to ensure // that there's no other way this output can be spent, we'll use a BIP - // 86 tweak here during aggregation. - // - // TODO(roasbeef): revisit if BIP 86 is needed here? + // 86 tweak here during aggregation, unless the user has explicitly + // specified a tapscript root. combinedKey, _, _, err := musig2.AggregateKeys( - []*btcec.PublicKey{aPub, bPub}, true, - musig2.WithBIP86KeyTweak(), + []*btcec.PublicKey{aPub, bPub}, true, muSig2Opt, ) if err != nil { return nil, nil, fmt.Errorf("unable to combine keys: %w", err) } // Now that we have the combined key, we can create a taproot pkScript - // from this, and then make the txout given the amount. + // from this, and then make the txOut given the amount. pkScript, err := PayToTaprootScript(combinedKey.FinalKey) if err != nil { return nil, nil, fmt.Errorf("unable to make taproot "+ @@ -228,7 +234,7 @@ func GenTaprootFundingScript(aPub, bPub *btcec.PublicKey, txOut := wire.NewTxOut(amt, pkScript) // For the "witness program" we just return the raw pkScript since the - // output we create can _only_ be spent with a musig2 signature. + // output we create can _only_ be spent with a MuSig2 signature. return pkScript, txOut, nil } diff --git a/itest/lnd_funding_test.go b/itest/lnd_funding_test.go index 6e2f0070cd..a1c2e292d3 100644 --- a/itest/lnd_funding_test.go +++ b/itest/lnd_funding_test.go @@ -12,6 +12,7 @@ import ( "github.com/btcsuite/btcd/txscript" "github.com/btcsuite/btcd/wire" "github.com/lightningnetwork/lnd/chainreg" + "github.com/lightningnetwork/lnd/fn" "github.com/lightningnetwork/lnd/funding" "github.com/lightningnetwork/lnd/input" "github.com/lightningnetwork/lnd/labels" @@ -1192,6 +1193,7 @@ func deriveFundingShim(ht *lntest.HarnessTest, carol, dave *node.HarnessNode, _, fundingOutput, err = input.GenTaprootFundingScript( carolKey, daveKey, int64(chanSize), + fn.None[chainhash.Hash](), ) require.NoError(ht, err) diff --git a/lnwallet/chanfunding/canned_assembler.go b/lnwallet/chanfunding/canned_assembler.go index 21dd473394..02dd7169d4 100644 --- a/lnwallet/chanfunding/canned_assembler.go +++ b/lnwallet/chanfunding/canned_assembler.go @@ -5,7 +5,9 @@ import ( "github.com/btcsuite/btcd/btcec/v2" "github.com/btcsuite/btcd/btcutil" + "github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/btcsuite/btcd/wire" + "github.com/lightningnetwork/lnd/fn" "github.com/lightningnetwork/lnd/input" "github.com/lightningnetwork/lnd/keychain" ) @@ -76,9 +78,8 @@ func (s *ShimIntent) FundingOutput() ([]byte, *wire.TxOut, error) { // Similar to the existing p2wsh script, we'll always ensure // the keys are sorted before use. return input.GenTaprootFundingScript( - s.localKey.PubKey, - s.remoteKey, - int64(totalAmt), + s.localKey.PubKey, s.remoteKey, int64(totalAmt), + fn.None[chainhash.Hash](), ) } diff --git a/lnwallet/channel.go b/lnwallet/channel.go index 59d9921a7c..b77a4a76ed 100644 --- a/lnwallet/channel.go +++ b/lnwallet/channel.go @@ -1013,6 +1013,7 @@ func (lc *LightningChannel) createSignDesc() error { if chanState.ChanType.IsTaproot() { fundingPkScript, _, err = input.GenTaprootFundingScript( localKey, remoteKey, int64(lc.channelState.Capacity), + fn.None[chainhash.Hash](), ) if err != nil { return err diff --git a/lnwallet/wallet.go b/lnwallet/wallet.go index 90a0bea2d3..bcba419f4b 100644 --- a/lnwallet/wallet.go +++ b/lnwallet/wallet.go @@ -23,6 +23,7 @@ import ( "github.com/btcsuite/btcwallet/wallet" "github.com/davecgh/go-spew/spew" "github.com/lightningnetwork/lnd/channeldb" + "github.com/lightningnetwork/lnd/fn" "github.com/lightningnetwork/lnd/input" "github.com/lightningnetwork/lnd/keychain" "github.com/lightningnetwork/lnd/lntypes" @@ -2102,6 +2103,7 @@ func (l *LightningWallet) verifyCommitSig(res *ChannelReservation, if res.musigSessions == nil { _, fundingOutput, err := input.GenTaprootFundingScript( localKey, remoteKey, channelValue, + fn.None[chainhash.Hash](), ) if err != nil { return err @@ -2341,11 +2343,14 @@ func (l *LightningWallet) handleSingleFunderSigs(req *addSingleFunderSigsMsg) { fundingTxOut *wire.TxOut ) if chanType.IsTaproot() { - fundingWitnessScript, fundingTxOut, err = input.GenTaprootFundingScript( //nolint:lll + //nolint:lll + fundingWitnessScript, fundingTxOut, err = input.GenTaprootFundingScript( ourKey.PubKey, theirKey.PubKey, channelValue, + fn.None[chainhash.Hash](), ) } else { - fundingWitnessScript, fundingTxOut, err = input.GenFundingPkScript( //nolint:lll + //nolint:lll + fundingWitnessScript, fundingTxOut, err = input.GenFundingPkScript( ourKey.PubKey.SerializeCompressed(), theirKey.PubKey.SerializeCompressed(), channelValue, ) @@ -2482,6 +2487,7 @@ func (l *LightningWallet) ValidateChannel(channelState *channeldb.OpenChannel, if channelState.ChanType.IsTaproot() { fundingScript, _, err = input.GenTaprootFundingScript( localKey, remoteKey, int64(channel.Capacity), + fn.None[chainhash.Hash](), ) if err != nil { return err From 9fd8287c7b401ec17f6a45f8033617f8bccc121b Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Wed, 13 Mar 2024 10:47:12 -0400 Subject: [PATCH 027/218] lnwallet/chanfunding: add optional tapscript root --- lnwallet/chanfunding/canned_assembler.go | 10 +++++++++- lnwallet/chanfunding/psbt_assembler.go | 1 + lnwallet/chanfunding/wallet_assembler.go | 2 +- 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/lnwallet/chanfunding/canned_assembler.go b/lnwallet/chanfunding/canned_assembler.go index 02dd7169d4..177cc35c38 100644 --- a/lnwallet/chanfunding/canned_assembler.go +++ b/lnwallet/chanfunding/canned_assembler.go @@ -58,6 +58,14 @@ type ShimIntent struct { // generate an aggregate key to use as the taproot-native multi-sig // output. musig2 bool + + // tapscriptRoot is the root of the tapscript tree that will be used to + // create the funding output. This field will only be utilized if the + // MuSig2 flag above is set to true. + // + // TODO(roasbeef): fold above into new chan type? sum type like thing, + // includes the tapscript root, etc + tapscriptRoot fn.Option[chainhash.Hash] } // FundingOutput returns the witness script, and the output that creates the @@ -79,7 +87,7 @@ func (s *ShimIntent) FundingOutput() ([]byte, *wire.TxOut, error) { // the keys are sorted before use. return input.GenTaprootFundingScript( s.localKey.PubKey, s.remoteKey, int64(totalAmt), - fn.None[chainhash.Hash](), + s.tapscriptRoot, ) } diff --git a/lnwallet/chanfunding/psbt_assembler.go b/lnwallet/chanfunding/psbt_assembler.go index 885fb7b465..10bcd70159 100644 --- a/lnwallet/chanfunding/psbt_assembler.go +++ b/lnwallet/chanfunding/psbt_assembler.go @@ -534,6 +534,7 @@ func (p *PsbtAssembler) ProvisionChannel(req *Request) (Intent, error) { ShimIntent: ShimIntent{ localFundingAmt: p.fundingAmt, musig2: req.Musig2, + tapscriptRoot: req.TapscriptRoot, }, State: PsbtShimRegistered, BasePsbt: p.basePsbt, diff --git a/lnwallet/chanfunding/wallet_assembler.go b/lnwallet/chanfunding/wallet_assembler.go index 4f2aa759b2..da78df49f2 100644 --- a/lnwallet/chanfunding/wallet_assembler.go +++ b/lnwallet/chanfunding/wallet_assembler.go @@ -394,7 +394,6 @@ func (w *WalletAssembler) ProvisionChannel(r *Request) (Intent, error) { // we will call the specialized coin selection function for // that. case r.FundUpToMaxAmt != 0 && r.MinFundAmt != 0: - // We need to ensure that manually selected coins, which // are spent entirely on the channel funding, leave // enough funds in the wallet to cover for a reserve. @@ -539,6 +538,7 @@ func (w *WalletAssembler) ProvisionChannel(r *Request) (Intent, error) { localFundingAmt: localContributionAmt, remoteFundingAmt: r.RemoteAmt, musig2: r.Musig2, + tapscriptRoot: r.TapscriptRoot, }, InputCoins: selectedCoins, coinLeaser: w.cfg.CoinLeaser, From b7e153fd82efbbbe95dd5be7af7975ea52d0a05d Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Wed, 13 Mar 2024 10:52:07 -0400 Subject: [PATCH 028/218] multi: update GenTaprootFundingScript to pass tapscript root In most cases, we won't yet be passing a root. The option usage helps us keep the control flow mostly unchanged. --- contractcourt/chain_watcher.go | 2 +- funding/manager.go | 3 +-- graph/builder.go | 2 ++ 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/contractcourt/chain_watcher.go b/contractcourt/chain_watcher.go index 155fca2a99..8b2f758167 100644 --- a/contractcourt/chain_watcher.go +++ b/contractcourt/chain_watcher.go @@ -308,7 +308,7 @@ func (c *chainWatcher) Start() error { ) if chanState.ChanType.IsTaproot() { c.fundingPkScript, _, err = input.GenTaprootFundingScript( - localKey, remoteKey, 0, fn.None[chainhash.Hash](), + localKey, remoteKey, 0, chanState.TapscriptRoot, ) if err != nil { return err diff --git a/funding/manager.go b/funding/manager.go index ad62860906..b22f2e6919 100644 --- a/funding/manager.go +++ b/funding/manager.go @@ -24,7 +24,6 @@ import ( "github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/channeldb/models" "github.com/lightningnetwork/lnd/discovery" - "github.com/lightningnetwork/lnd/fn" "github.com/lightningnetwork/lnd/graph" "github.com/lightningnetwork/lnd/input" "github.com/lightningnetwork/lnd/keychain" @@ -2900,7 +2899,7 @@ func makeFundingScript(channel *channeldb.OpenChannel) ([]byte, error) { if channel.ChanType.IsTaproot() { pkScript, _, err := input.GenTaprootFundingScript( localKey, remoteKey, int64(channel.Capacity), - fn.None[chainhash.Hash](), + channel.TapscriptRoot, ) if err != nil { return nil, err diff --git a/graph/builder.go b/graph/builder.go index 7f7823ab4d..717d3e5ad8 100644 --- a/graph/builder.go +++ b/graph/builder.go @@ -1146,6 +1146,8 @@ func makeFundingScript(bitcoinKey1, bitcoinKey2 []byte, return nil, err } + // TODO(roasbeef): add tapscript root to gossip v1.5 + return fundingScript, nil } From 62713c127050e4d2b32f64dfc5f0701a02499bd0 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Wed, 13 Mar 2024 10:53:41 -0400 Subject: [PATCH 029/218] lnwallet: update internal funding flow w/ tapscript root This isn't hooked up yet to the funding manager, but with this commit, we can now start to write internal unit tests that handle musig2 channels with a tapscript root. --- lnwallet/reservation.go | 5 +++++ lnwallet/wallet.go | 11 ++++++++--- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/lnwallet/reservation.go b/lnwallet/reservation.go index df6c8fd940..1f0000e8e4 100644 --- a/lnwallet/reservation.go +++ b/lnwallet/reservation.go @@ -415,6 +415,10 @@ func NewChannelReservation(capacity, localFundingAmt btcutil.Amount, chanType |= channeldb.ScidAliasFeatureBit } + if req.TapscriptRoot.IsSome() { + chanType |= channeldb.TapscriptRootBit + } + return &ChannelReservation{ ourContribution: &ChannelContribution{ FundingAmount: ourBalance.ToSatoshis(), @@ -448,6 +452,7 @@ func NewChannelReservation(capacity, localFundingAmt btcutil.Amount, InitialLocalBalance: ourBalance, InitialRemoteBalance: theirBalance, Memo: req.Memo, + TapscriptRoot: req.TapscriptRoot, }, pushMSat: req.PushMSat, pendingChanID: req.PendingChanID, diff --git a/lnwallet/wallet.go b/lnwallet/wallet.go index bcba419f4b..0d6e85ec9a 100644 --- a/lnwallet/wallet.go +++ b/lnwallet/wallet.go @@ -211,6 +211,11 @@ type InitFundingReserveMsg struct { // channel that will be useful to our future selves. Memo []byte + // TapscriptRoot is the root of the tapscript tree that will be used to + // create the funding output. This is an optional field that should + // only be set for taproot channels. + TapscriptRoot fn.Option[chainhash.Hash] + // err is a channel in which all errors will be sent across. Will be // nil if this initial set is successful. // @@ -2103,7 +2108,7 @@ func (l *LightningWallet) verifyCommitSig(res *ChannelReservation, if res.musigSessions == nil { _, fundingOutput, err := input.GenTaprootFundingScript( localKey, remoteKey, channelValue, - fn.None[chainhash.Hash](), + res.partialState.TapscriptRoot, ) if err != nil { return err @@ -2346,7 +2351,7 @@ func (l *LightningWallet) handleSingleFunderSigs(req *addSingleFunderSigsMsg) { //nolint:lll fundingWitnessScript, fundingTxOut, err = input.GenTaprootFundingScript( ourKey.PubKey, theirKey.PubKey, channelValue, - fn.None[chainhash.Hash](), + pendingReservation.partialState.TapscriptRoot, ) } else { //nolint:lll @@ -2487,7 +2492,7 @@ func (l *LightningWallet) ValidateChannel(channelState *channeldb.OpenChannel, if channelState.ChanType.IsTaproot() { fundingScript, _, err = input.GenTaprootFundingScript( localKey, remoteKey, int64(channel.Capacity), - fn.None[chainhash.Hash](), + channelState.TapscriptRoot, ) if err != nil { return err From cae4f2189752242ea7313b14fa1afef03ffafe47 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Wed, 13 Mar 2024 10:54:49 -0400 Subject: [PATCH 030/218] lnwallet+peer: add tapscript root awareness to musig2 sessions With this commit, the channel is now aware of if it's a musig2 channel, that also has a tapscript root. We'll need to always pass in the tapscript root each time we: make the funding output, sign a new state, and also verify a new state. --- lnwallet/chancloser/chancloser_test.go | 7 +- lnwallet/channel.go | 21 +++-- lnwallet/musig_session.go | 107 ++++++++++++++++++------- peer/musig_chan_closer.go | 8 +- 4 files changed, 104 insertions(+), 39 deletions(-) diff --git a/lnwallet/chancloser/chancloser_test.go b/lnwallet/chancloser/chancloser_test.go index 9a90d0ab2b..a6688ed391 100644 --- a/lnwallet/chancloser/chancloser_test.go +++ b/lnwallet/chancloser/chancloser_test.go @@ -14,6 +14,7 @@ import ( "github.com/btcsuite/btcd/txscript" "github.com/btcsuite/btcd/wire" "github.com/lightningnetwork/lnd/channeldb" + "github.com/lightningnetwork/lnd/fn" "github.com/lightningnetwork/lnd/input" "github.com/lightningnetwork/lnd/keychain" "github.com/lightningnetwork/lnd/lntypes" @@ -178,8 +179,9 @@ func (m *mockChannel) RemoteUpfrontShutdownScript() lnwire.DeliveryAddress { } func (m *mockChannel) CreateCloseProposal(fee btcutil.Amount, - localScript, remoteScript []byte, _ ...lnwallet.ChanCloseOpt, -) (input.Signature, *chainhash.Hash, btcutil.Amount, error) { + localScript, remoteScript []byte, + _ ...lnwallet.ChanCloseOpt) (input.Signature, *chainhash.Hash, + btcutil.Amount, error) { if m.chanType.IsTaproot() { return lnwallet.NewMusigPartialSig( @@ -188,6 +190,7 @@ func (m *mockChannel) CreateCloseProposal(fee btcutil.Amount, R: new(btcec.PublicKey), }, lnwire.Musig2Nonce{}, lnwire.Musig2Nonce{}, nil, + fn.None[chainhash.Hash](), ), nil, 0, nil } diff --git a/lnwallet/channel.go b/lnwallet/channel.go index b77a4a76ed..4079d961dd 100644 --- a/lnwallet/channel.go +++ b/lnwallet/channel.go @@ -1013,7 +1013,7 @@ func (lc *LightningChannel) createSignDesc() error { if chanState.ChanType.IsTaproot() { fundingPkScript, _, err = input.GenTaprootFundingScript( localKey, remoteKey, int64(lc.channelState.Capacity), - fn.None[chainhash.Hash](), + chanState.TapscriptRoot, ) if err != nil { return err @@ -6089,11 +6089,15 @@ func (lc *LightningChannel) getSignedCommitTx() (*wire.MsgTx, error) { "verification nonce: %w", err) } + tapscriptTweak := fn.MapOption(TapscriptRootToTweak)( + lc.channelState.TapscriptRoot, + ) + // Now that we have the local nonce, we'll re-create the musig // session we had for this height. musigSession := NewPartialMusigSession( *localNonce, ourKey, theirKey, lc.Signer, - &lc.fundingOutput, LocalMusigCommit, + &lc.fundingOutput, LocalMusigCommit, tapscriptTweak, ) var remoteSig lnwire.PartialSigWithNonce @@ -8704,12 +8708,13 @@ func (lc *LightningChannel) InitRemoteMusigNonces(remoteNonce *musig2.Nonces, // TODO(roasbeef): propagate rename of signing and verification nonces sessionCfg := &MusigSessionCfg{ - LocalKey: localChanCfg.MultiSigKey, - RemoteKey: remoteChanCfg.MultiSigKey, - LocalNonce: *localNonce, - RemoteNonce: *remoteNonce, - Signer: lc.Signer, - InputTxOut: &lc.fundingOutput, + LocalKey: localChanCfg.MultiSigKey, + RemoteKey: remoteChanCfg.MultiSigKey, + LocalNonce: *localNonce, + RemoteNonce: *remoteNonce, + Signer: lc.Signer, + InputTxOut: &lc.fundingOutput, + TapscriptTweak: lc.channelState.TapscriptRoot, } lc.musigSessions = NewMusigPairSession( sessionCfg, diff --git a/lnwallet/musig_session.go b/lnwallet/musig_session.go index ecc60d07f1..c3214d3f2f 100644 --- a/lnwallet/musig_session.go +++ b/lnwallet/musig_session.go @@ -8,8 +8,10 @@ import ( "github.com/btcsuite/btcd/btcec/v2" "github.com/btcsuite/btcd/btcec/v2/schnorr" "github.com/btcsuite/btcd/btcec/v2/schnorr/musig2" + "github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/btcsuite/btcd/txscript" "github.com/btcsuite/btcd/wire" + "github.com/lightningnetwork/lnd/fn" "github.com/lightningnetwork/lnd/input" "github.com/lightningnetwork/lnd/keychain" "github.com/lightningnetwork/lnd/lnwire" @@ -37,6 +39,20 @@ var ( ErrSessionNotFinalized = fmt.Errorf("musig2 session not finalized") ) +// tapscriptRootToSignOpt is a function that takes a tapscript root and returns +// a MuSig2 sign opt that'll apply the tweak when signing+verifying. +func tapscriptRootToSignOpt(root chainhash.Hash) musig2.SignOption { + return musig2.WithTaprootSignTweak(root[:]) +} + +// TapscriptRootToTweak is a helper function that converts a tapscript root +// into a tweak that can be used with the MuSig2 API. +func TapscriptRootToTweak(root chainhash.Hash) input.MuSig2Tweaks { + return input.MuSig2Tweaks{ + TaprootTweak: root[:], + } +} + // MusigPartialSig is a wrapper around the base musig2.PartialSignature type // that also includes information about the set of nonces used, and also the // signer. This allows us to implement the input.Signature interface, as that @@ -54,25 +70,30 @@ type MusigPartialSig struct { // signerKeys is the set of public keys of all signers. signerKeys []*btcec.PublicKey + + // tapscriptTweak is an optional tweak, that if specified, will be used + // instead of the normal BIP 86 tweak when validating the signature. + tapscriptTweak fn.Option[chainhash.Hash] } -// NewMusigPartialSig creates a new musig partial signature. -func NewMusigPartialSig(sig *musig2.PartialSignature, - signerNonce, combinedNonce lnwire.Musig2Nonce, - signerKeys []*btcec.PublicKey) *MusigPartialSig { +// NewMusigPartialSig creates a new MuSig2 partial signature. +func NewMusigPartialSig(sig *musig2.PartialSignature, signerNonce, + combinedNonce lnwire.Musig2Nonce, signerKeys []*btcec.PublicKey, + tapscriptTweak fn.Option[chainhash.Hash]) *MusigPartialSig { return &MusigPartialSig{ - sig: sig, - signerNonce: signerNonce, - combinedNonce: combinedNonce, - signerKeys: signerKeys, + sig: sig, + signerNonce: signerNonce, + combinedNonce: combinedNonce, + signerKeys: signerKeys, + tapscriptTweak: tapscriptTweak, } } // FromWireSig maps a wire partial sig to this internal type that we'll use to // perform signature validation. -func (p *MusigPartialSig) FromWireSig(sig *lnwire.PartialSigWithNonce, -) *MusigPartialSig { +func (p *MusigPartialSig) FromWireSig( + sig *lnwire.PartialSigWithNonce) *MusigPartialSig { p.sig = &musig2.PartialSignature{ S: &sig.Sig, @@ -135,9 +156,15 @@ func (p *MusigPartialSig) Verify(msg []byte, pub *btcec.PublicKey) bool { var m [32]byte copy(m[:], msg) + // If we have a tapscript tweak, then we'll use that as a tweak + // otherwise, we'll fall back to the normal BIP 86 sign tweak. + signOpts := fn.MapOption(tapscriptRootToSignOpt)( + p.tapscriptTweak, + ).UnwrapOr(musig2.WithBip86SignTweak()) + return p.sig.Verify( p.signerNonce, p.combinedNonce, p.signerKeys, pub, m, - musig2.WithSortedKeys(), musig2.WithBip86SignTweak(), + musig2.WithSortedKeys(), signOpts, ) } @@ -160,6 +187,14 @@ func (n *MusigNoncePair) String() string { n.SigningNonce.PubNonce[:]) } +// TapscriptRootToTweak is a function that takes a MuSig2 taproot tweak and +// returns the root hash of the tapscript tree. +func muSig2TweakToRoot(tweak input.MuSig2Tweaks) chainhash.Hash { + var root chainhash.Hash + copy(root[:], tweak.TaprootTweak) + return root +} + // MusigSession abstracts over the details of a logical musig session. A single // session is used for each commitment transactions. The sessions use a JIT // nonce style, wherein part of the session can be created using only the @@ -197,15 +232,20 @@ type MusigSession struct { // commitType tracks if this is the session for the local or remote // commitment. commitType MusigCommitType + + // tapscriptTweak is an optional tweak, that if specified, will be used + // instead of the normal BIP 86 tweak when creating the MuSig2 + // aggregate key and session. + tapscriptTweak fn.Option[input.MuSig2Tweaks] } // NewPartialMusigSession creates a new musig2 session given only the // verification nonce (local nonce), and the other information that has already // been bound to the session. func NewPartialMusigSession(verificationNonce musig2.Nonces, - localKey, remoteKey keychain.KeyDescriptor, - signer input.MuSig2Signer, inputTxOut *wire.TxOut, - commitType MusigCommitType) *MusigSession { + localKey, remoteKey keychain.KeyDescriptor, signer input.MuSig2Signer, + inputTxOut *wire.TxOut, commitType MusigCommitType, + tapscriptTweak fn.Option[input.MuSig2Tweaks]) *MusigSession { signerKeys := []*btcec.PublicKey{localKey.PubKey, remoteKey.PubKey} @@ -214,13 +254,14 @@ func NewPartialMusigSession(verificationNonce musig2.Nonces, } return &MusigSession{ - nonces: nonces, - remoteKey: remoteKey, - localKey: localKey, - inputTxOut: inputTxOut, - signerKeys: signerKeys, - signer: signer, - commitType: commitType, + nonces: nonces, + remoteKey: remoteKey, + localKey: localKey, + inputTxOut: inputTxOut, + signerKeys: signerKeys, + signer: signer, + commitType: commitType, + tapscriptTweak: tapscriptTweak, } } @@ -254,9 +295,9 @@ func (m *MusigSession) FinalizeSession(signingNonce musig2.Nonces) error { remoteNonce = m.nonces.SigningNonce } - tweakDesc := input.MuSig2Tweaks{ + tweakDesc := m.tapscriptTweak.UnwrapOr(input.MuSig2Tweaks{ TaprootBIP0086Tweak: true, - } + }) m.session, err = m.signer.MuSig2CreateSession( input.MuSig2Version100RC2, m.localKey.KeyLocator, m.signerKeys, &tweakDesc, [][musig2.PubNonceSize]byte{remoteNonce.PubNonce}, @@ -351,8 +392,11 @@ func (m *MusigSession) SignCommit(tx *wire.MsgTx) (*MusigPartialSig, error) { return nil, err } + tapscriptRoot := fn.MapOption(muSig2TweakToRoot)(m.tapscriptTweak) + return NewMusigPartialSig( sig, m.session.PublicNonce, m.combinedNonce, m.signerKeys, + tapscriptRoot, ), nil } @@ -364,7 +408,7 @@ func (m *MusigSession) Refresh(verificationNonce *musig2.Nonces, return NewPartialMusigSession( *verificationNonce, m.localKey, m.remoteKey, m.signer, - m.inputTxOut, m.commitType, + m.inputTxOut, m.commitType, m.tapscriptTweak, ), nil } @@ -451,9 +495,11 @@ func (m *MusigSession) VerifyCommitSig(commitTx *wire.MsgTx, // When we verify a commitment signature, we always assume that we're // verifying a signature on our local commitment. Therefore, we'll use: // their remote nonce, and also public key. + tapscriptRoot := fn.MapOption(muSig2TweakToRoot)(m.tapscriptTweak) partialSig := NewMusigPartialSig( &musig2.PartialSignature{S: &sig.Sig}, m.nonces.SigningNonce.PubNonce, m.combinedNonce, m.signerKeys, + tapscriptRoot, ) // With the partial sig loaded with the proper context, we'll now @@ -537,6 +583,10 @@ type MusigSessionCfg struct { // InputTxOut is the output that we're signing for. This will be the // funding input. InputTxOut *wire.TxOut + + // TapscriptRoot is an optional tweak that can be used to modify the + // MuSig2 public key used in the session. + TapscriptTweak fn.Option[chainhash.Hash] } // MusigPairSession houses the two musig2 sessions needed to do funding and @@ -561,13 +611,14 @@ func NewMusigPairSession(cfg *MusigSessionCfg) *MusigPairSession { // // Both sessions will be created using only the verification nonce for // the local+remote party. + tapscriptTweak := fn.MapOption(TapscriptRootToTweak)(cfg.TapscriptTweak) localSession := NewPartialMusigSession( - cfg.LocalNonce, cfg.LocalKey, cfg.RemoteKey, - cfg.Signer, cfg.InputTxOut, LocalMusigCommit, + cfg.LocalNonce, cfg.LocalKey, cfg.RemoteKey, cfg.Signer, + cfg.InputTxOut, LocalMusigCommit, tapscriptTweak, ) remoteSession := NewPartialMusigSession( - cfg.RemoteNonce, cfg.LocalKey, cfg.RemoteKey, - cfg.Signer, cfg.InputTxOut, RemoteMusigCommit, + cfg.RemoteNonce, cfg.LocalKey, cfg.RemoteKey, cfg.Signer, + cfg.InputTxOut, RemoteMusigCommit, tapscriptTweak, ) return &MusigPairSession{ diff --git a/peer/musig_chan_closer.go b/peer/musig_chan_closer.go index 6b05b1e62e..6f69a8c5b8 100644 --- a/peer/musig_chan_closer.go +++ b/peer/musig_chan_closer.go @@ -4,6 +4,7 @@ import ( "fmt" "github.com/btcsuite/btcd/btcec/v2/schnorr/musig2" + "github.com/lightningnetwork/lnd/fn" "github.com/lightningnetwork/lnd/input" "github.com/lightningnetwork/lnd/lnwallet" "github.com/lightningnetwork/lnd/lnwallet/chancloser" @@ -43,10 +44,15 @@ func (m *MusigChanCloser) ProposalClosingOpts() ( } localKey, remoteKey := m.channel.MultiSigKeys() + + tapscriptTweak := fn.MapOption(lnwallet.TapscriptRootToTweak)( + m.channel.State().TapscriptRoot, + ) + m.musigSession = lnwallet.NewPartialMusigSession( *m.remoteNonce, localKey, remoteKey, m.channel.Signer, m.channel.FundingTxOut(), - lnwallet.RemoteMusigCommit, + lnwallet.RemoteMusigCommit, tapscriptTweak, ) err := m.musigSession.FinalizeSession(*m.localNonce) From ca5dcbbf5cba204ce19ed0dceb519a04589835fa Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Wed, 13 Mar 2024 10:55:27 -0400 Subject: [PATCH 031/218] lnwallet: add initial unit tests for musig2+tapscript root chans --- lnwallet/channel_test.go | 16 ++++++++++++++++ lnwallet/test_utils.go | 16 ++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/lnwallet/channel_test.go b/lnwallet/channel_test.go index a159e61fe2..fc1b087898 100644 --- a/lnwallet/channel_test.go +++ b/lnwallet/channel_test.go @@ -386,6 +386,12 @@ func TestSimpleAddSettleWorkflow(t *testing.T) { ) }) + t.Run("taproot with tapscript root", func(t *testing.T) { + flags := channeldb.SimpleTaprootFeatureBit | + channeldb.TapscriptRootBit + testAddSettleWorkflow(t, true, flags, false) + }) + t.Run("storeFinalHtlcResolutions=true", func(t *testing.T) { testAddSettleWorkflow(t, false, 0, true) }) @@ -827,6 +833,16 @@ func TestForceClose(t *testing.T) { anchorAmt: AnchorSize * 2, }) }) + t.Run("taproot with tapscript root", func(t *testing.T) { + testForceClose(t, &forceCloseTestCase{ + chanType: channeldb.SingleFunderTweaklessBit | + channeldb.AnchorOutputsBit | + channeldb.SimpleTaprootFeatureBit | + channeldb.TapscriptRootBit, + expectedCommitWeight: input.TaprootCommitWeight, + anchorAmt: AnchorSize * 2, + }) + }) } type forceCloseTestCase struct { diff --git a/lnwallet/test_utils.go b/lnwallet/test_utils.go index 5f575e3090..5e0cac1f8e 100644 --- a/lnwallet/test_utils.go +++ b/lnwallet/test_utils.go @@ -14,6 +14,7 @@ import ( "github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/btcsuite/btcd/wire" "github.com/lightningnetwork/lnd/channeldb" + "github.com/lightningnetwork/lnd/fn" "github.com/lightningnetwork/lnd/input" "github.com/lightningnetwork/lnd/keychain" "github.com/lightningnetwork/lnd/lntypes" @@ -348,6 +349,21 @@ func CreateTestChannels(t *testing.T, chanType channeldb.ChannelType, Packager: channeldb.NewChannelPackager(shortChanID), } + // If the channel type has a tapscript root, then we'll also specify + // one here to apply to both the channels. + if chanType.HasTapscriptRoot() { + var tapscriptRoot chainhash.Hash + _, err := io.ReadFull(rand.Reader, tapscriptRoot[:]) + if err != nil { + return nil, nil, err + } + + someRoot := fn.Some(tapscriptRoot) + + aliceChannelState.TapscriptRoot = someRoot + bobChannelState.TapscriptRoot = someRoot + } + aliceSigner := input.NewMockSigner(aliceKeys, nil) bobSigner := input.NewMockSigner(bobKeys, nil) From 41bd293f337a0dbb9ac946000308b5d763aed18d Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Fri, 15 Mar 2024 11:43:21 -0400 Subject: [PATCH 032/218] input+lnwallet: update taproot scripts to accept optional aux leaf In this commit, we update all the taproot scripts to also accept an optional aux leaf. This aux leaf can be used to add more redemption paths for advanced channels, or just as an extra commitment space. --- input/script_utils.go | 113 +++++++---- input/size_test.go | 21 +- input/taproot.go | 10 + input/taproot_test.go | 189 +++++++++++++----- lnwallet/channel.go | 2 + lnwallet/commitment.go | 15 +- watchtower/blob/justice_kit.go | 9 +- watchtower/blob/justice_kit_test.go | 6 +- watchtower/lookout/justice_descriptor_test.go | 5 +- .../wtclient/backup_task_internal_test.go | 4 +- watchtower/wtclient/client_test.go | 7 +- 11 files changed, 273 insertions(+), 108 deletions(-) diff --git a/input/script_utils.go b/input/script_utils.go index d3a6dc9d31..91ca55292f 100644 --- a/input/script_utils.go +++ b/input/script_utils.go @@ -646,6 +646,13 @@ type HtlcScriptTree struct { // TimeoutTapLeaf is the tapleaf for the timeout path. TimeoutTapLeaf txscript.TapLeaf + // AuxLeaf is an auxiliary leaf that can be used to extend the base + // HTLC script tree with new spend paths, or just as extra commitment + // space. When present, this leaf will always be in the right-most area + // of the tapscript tree. + AuxLeaf AuxTapLeaf + + // htlcType is the type of HTLC script this is. htlcType htlcType } @@ -726,8 +733,8 @@ var _ TapscriptDescriptor = (*HtlcScriptTree)(nil) // senderHtlcTapScriptTree builds the tapscript tree which is used to anchor // the HTLC key for HTLCs on the sender's commitment. func senderHtlcTapScriptTree(senderHtlcKey, receiverHtlcKey, - revokeKey *btcec.PublicKey, payHash []byte, - hType htlcType) (*HtlcScriptTree, error) { + revokeKey *btcec.PublicKey, payHash []byte, hType htlcType, + auxLeaf AuxTapLeaf) (*HtlcScriptTree, error) { // First, we'll obtain the tap leaves for both the success and timeout // path. @@ -744,11 +751,14 @@ func senderHtlcTapScriptTree(senderHtlcKey, receiverHtlcKey, return nil, err } + tapLeaves := []txscript.TapLeaf{successTapLeaf, timeoutTapLeaf} + auxLeaf.WhenSome(func(l txscript.TapLeaf) { + tapLeaves = append(tapLeaves, l) + }) + // With the two leaves obtained, we'll now make the tapscript tree, // then obtain the root from that - tapscriptTree := txscript.AssembleTaprootScriptTree( - successTapLeaf, timeoutTapLeaf, - ) + tapscriptTree := txscript.AssembleTaprootScriptTree(tapLeaves...) tapScriptRoot := tapscriptTree.RootNode.TapHash() @@ -767,6 +777,7 @@ func senderHtlcTapScriptTree(senderHtlcKey, receiverHtlcKey, }, SuccessTapLeaf: successTapLeaf, TimeoutTapLeaf: timeoutTapLeaf, + AuxLeaf: auxLeaf, htlcType: hType, }, nil } @@ -801,7 +812,8 @@ func senderHtlcTapScriptTree(senderHtlcKey, receiverHtlcKey, // unilaterally spend the created output. func SenderHTLCScriptTaproot(senderHtlcKey, receiverHtlcKey, revokeKey *btcec.PublicKey, payHash []byte, - whoseCommit lntypes.ChannelParty) (*HtlcScriptTree, error) { + whoseCommit lntypes.ChannelParty, auxLeaf AuxTapLeaf) (*HtlcScriptTree, + error) { var hType htlcType if whoseCommit.IsLocal() { @@ -814,8 +826,8 @@ func SenderHTLCScriptTaproot(senderHtlcKey, receiverHtlcKey, // tree that includes the top level output script, as well as the two // tap leaf paths. return senderHtlcTapScriptTree( - senderHtlcKey, receiverHtlcKey, revokeKey, payHash, - hType, + senderHtlcKey, receiverHtlcKey, revokeKey, payHash, hType, + auxLeaf, ) } @@ -1285,8 +1297,8 @@ func ReceiverHtlcTapLeafSuccess(receiverHtlcKey *btcec.PublicKey, // receiverHtlcTapScriptTree builds the tapscript tree which is used to anchor // the HTLC key for HTLCs on the receiver's commitment. func receiverHtlcTapScriptTree(senderHtlcKey, receiverHtlcKey, - revokeKey *btcec.PublicKey, payHash []byte, - cltvExpiry uint32, hType htlcType) (*HtlcScriptTree, error) { + revokeKey *btcec.PublicKey, payHash []byte, cltvExpiry uint32, + hType htlcType, auxLeaf AuxTapLeaf) (*HtlcScriptTree, error) { // First, we'll obtain the tap leaves for both the success and timeout // path. @@ -1303,11 +1315,14 @@ func receiverHtlcTapScriptTree(senderHtlcKey, receiverHtlcKey, return nil, err } + tapLeaves := []txscript.TapLeaf{timeoutTapLeaf, successTapLeaf} + auxLeaf.WhenSome(func(l txscript.TapLeaf) { + tapLeaves = append(tapLeaves, l) + }) + // With the two leaves obtained, we'll now make the tapscript tree, // then obtain the root from that - tapscriptTree := txscript.AssembleTaprootScriptTree( - timeoutTapLeaf, successTapLeaf, - ) + tapscriptTree := txscript.AssembleTaprootScriptTree(tapLeaves...) tapScriptRoot := tapscriptTree.RootNode.TapHash() @@ -1326,6 +1341,7 @@ func receiverHtlcTapScriptTree(senderHtlcKey, receiverHtlcKey, }, SuccessTapLeaf: successTapLeaf, TimeoutTapLeaf: timeoutTapLeaf, + AuxLeaf: auxLeaf, htlcType: hType, }, nil } @@ -1361,7 +1377,7 @@ func receiverHtlcTapScriptTree(senderHtlcKey, receiverHtlcKey, func ReceiverHTLCScriptTaproot(cltvExpiry uint32, senderHtlcKey, receiverHtlcKey, revocationKey *btcec.PublicKey, payHash []byte, whoseCommit lntypes.ChannelParty, -) (*HtlcScriptTree, error) { + auxLeaf AuxTapLeaf) (*HtlcScriptTree, error) { var hType htlcType if whoseCommit.IsLocal() { @@ -1375,7 +1391,7 @@ func ReceiverHTLCScriptTaproot(cltvExpiry uint32, // tap leaf paths. return receiverHtlcTapScriptTree( senderHtlcKey, receiverHtlcKey, revocationKey, payHash, - cltvExpiry, hType, + cltvExpiry, hType, auxLeaf, ) } @@ -1604,9 +1620,9 @@ func TaprootSecondLevelTapLeaf(delayKey *btcec.PublicKey, } // SecondLevelHtlcTapscriptTree construct the indexed tapscript tree needed to -// generate the taptweak to create the final output and also control block. -func SecondLevelHtlcTapscriptTree(delayKey *btcec.PublicKey, - csvDelay uint32) (*txscript.IndexedTapScriptTree, error) { +// generate the tap tweak to create the final output and also control block. +func SecondLevelHtlcTapscriptTree(delayKey *btcec.PublicKey, csvDelay uint32, + auxLeaf AuxTapLeaf) (*txscript.IndexedTapScriptTree, error) { // First grab the second level leaf script we need to create the top // level output. @@ -1615,9 +1631,14 @@ func SecondLevelHtlcTapscriptTree(delayKey *btcec.PublicKey, return nil, err } + tapLeaves := []txscript.TapLeaf{secondLevelTapLeaf} + auxLeaf.WhenSome(func(l txscript.TapLeaf) { + tapLeaves = append(tapLeaves, l) + }) + // Now that we have the sole second level script, we can create the // tapscript tree that commits to both the leaves. - return txscript.AssembleTaprootScriptTree(secondLevelTapLeaf), nil + return txscript.AssembleTaprootScriptTree(tapLeaves...), nil } // TaprootSecondLevelHtlcScript is the uniform script that's used as the output @@ -1637,12 +1658,12 @@ func SecondLevelHtlcTapscriptTree(delayKey *btcec.PublicKey, // // The keyspend path require knowledge of the top level revocation private key. func TaprootSecondLevelHtlcScript(revokeKey, delayKey *btcec.PublicKey, - csvDelay uint32) (*btcec.PublicKey, error) { + csvDelay uint32, auxLeaf AuxTapLeaf) (*btcec.PublicKey, error) { // First, we'll make the tapscript tree that commits to the redemption // path. tapScriptTree, err := SecondLevelHtlcTapscriptTree( - delayKey, csvDelay, + delayKey, csvDelay, auxLeaf, ) if err != nil { return nil, err @@ -1667,17 +1688,21 @@ type SecondLevelScriptTree struct { // SuccessTapLeaf is the tapleaf for the redemption path. SuccessTapLeaf txscript.TapLeaf + + // AuxLeaf is an optional leaf that can be used to extend the script + // tree. + AuxLeaf AuxTapLeaf } // TaprootSecondLevelScriptTree constructs the tapscript tree used to spend the // second level HTLC output. func TaprootSecondLevelScriptTree(revokeKey, delayKey *btcec.PublicKey, - csvDelay uint32) (*SecondLevelScriptTree, error) { + csvDelay uint32, auxLeaf AuxTapLeaf) (*SecondLevelScriptTree, error) { // First, we'll make the tapscript tree that commits to the redemption // path. tapScriptTree, err := SecondLevelHtlcTapscriptTree( - delayKey, csvDelay, + delayKey, csvDelay, auxLeaf, ) if err != nil { return nil, err @@ -1698,6 +1723,7 @@ func TaprootSecondLevelScriptTree(revokeKey, delayKey *btcec.PublicKey, InternalKey: revokeKey, }, SuccessTapLeaf: tapScriptTree.LeafMerkleProofs[0].TapLeaf, + AuxLeaf: auxLeaf, }, nil } @@ -2079,6 +2105,12 @@ type CommitScriptTree struct { // RevocationLeaf is the leaf used to spend the output with the // revocation key signature. RevocationLeaf txscript.TapLeaf + + // AuxLeaf is an auxiliary leaf that can be used to extend the base + // commitment script tree with new spend paths, or just as extra + // commitment space. When present, this leaf will always be in the + // left-most or right-most area of the tapscript tree. + AuxLeaf AuxTapLeaf } // A compile time check to ensure CommitScriptTree implements the @@ -2143,8 +2175,9 @@ func (c *CommitScriptTree) Tree() ScriptTree { // NewLocalCommitScriptTree returns a new CommitScript tree that can be used to // create and spend the commitment output for the local party. -func NewLocalCommitScriptTree(csvTimeout uint32, - selfKey, revokeKey *btcec.PublicKey) (*CommitScriptTree, error) { +func NewLocalCommitScriptTree(csvTimeout uint32, selfKey, + revokeKey *btcec.PublicKey, auxLeaf AuxTapLeaf) (*CommitScriptTree, + error) { // First, we'll need to construct the tapLeaf that'll be our delay CSV // clause. @@ -2164,9 +2197,13 @@ func NewLocalCommitScriptTree(csvTimeout uint32, // the two leaves, and then obtain a root from that. delayTapLeaf := txscript.NewBaseTapLeaf(delayScript) revokeTapLeaf := txscript.NewBaseTapLeaf(revokeScript) - tapScriptTree := txscript.AssembleTaprootScriptTree( - delayTapLeaf, revokeTapLeaf, - ) + + tapLeaves := []txscript.TapLeaf{delayTapLeaf, revokeTapLeaf} + auxLeaf.WhenSome(func(l txscript.TapLeaf) { + tapLeaves = append(tapLeaves, l) + }) + + tapScriptTree := txscript.AssembleTaprootScriptTree(tapLeaves...) tapScriptRoot := tapScriptTree.RootNode.TapHash() // Now that we have our root, we can arrive at the final output script @@ -2184,6 +2221,7 @@ func NewLocalCommitScriptTree(csvTimeout uint32, }, SettleLeaf: delayTapLeaf, RevocationLeaf: revokeTapLeaf, + AuxLeaf: auxLeaf, }, nil } @@ -2253,7 +2291,7 @@ func TaprootCommitScriptToSelf(csvTimeout uint32, selfKey, revokeKey *btcec.PublicKey) (*btcec.PublicKey, error) { commitScriptTree, err := NewLocalCommitScriptTree( - csvTimeout, selfKey, revokeKey, + csvTimeout, selfKey, revokeKey, NoneTapLeaf(), ) if err != nil { return nil, err @@ -2579,7 +2617,7 @@ func CommitScriptToRemoteConfirmed(key *btcec.PublicKey) ([]byte, error) { // NewRemoteCommitScriptTree constructs a new script tree for the remote party // to sweep their funds after a hard coded 1 block delay. func NewRemoteCommitScriptTree(remoteKey *btcec.PublicKey, -) (*CommitScriptTree, error) { + auxLeaf AuxTapLeaf) (*CommitScriptTree, error) { // First, construct the remote party's tapscript they'll use to sweep // their outputs. @@ -2595,10 +2633,16 @@ func NewRemoteCommitScriptTree(remoteKey *btcec.PublicKey, return nil, err } + tapLeaf := txscript.NewBaseTapLeaf(remoteScript) + + tapLeaves := []txscript.TapLeaf{tapLeaf} + auxLeaf.WhenSome(func(l txscript.TapLeaf) { + tapLeaves = append(tapLeaves, l) + }) + // With this script constructed, we'll map that into a tapLeaf, then // make a new tapscript root from that. - tapLeaf := txscript.NewBaseTapLeaf(remoteScript) - tapScriptTree := txscript.AssembleTaprootScriptTree(tapLeaf) + tapScriptTree := txscript.AssembleTaprootScriptTree(tapLeaves...) tapScriptRoot := tapScriptTree.RootNode.TapHash() // Now that we have our root, we can arrive at the final output script @@ -2615,6 +2659,7 @@ func NewRemoteCommitScriptTree(remoteKey *btcec.PublicKey, InternalKey: &TaprootNUMSKey, }, SettleLeaf: tapLeaf, + AuxLeaf: auxLeaf, }, nil } @@ -2631,9 +2676,9 @@ func NewRemoteCommitScriptTree(remoteKey *btcec.PublicKey, // OP_CHECKSIG // 1 OP_CHECKSEQUENCEVERIFY OP_DROP func TaprootCommitScriptToRemote(remoteKey *btcec.PublicKey, -) (*btcec.PublicKey, error) { + auxLeaf AuxTapLeaf) (*btcec.PublicKey, error) { - commitScriptTree, err := NewRemoteCommitScriptTree(remoteKey) + commitScriptTree, err := NewRemoteCommitScriptTree(remoteKey, auxLeaf) if err != nil { return nil, err } diff --git a/input/size_test.go b/input/size_test.go index daa7053ccf..21e2c06ae4 100644 --- a/input/size_test.go +++ b/input/size_test.go @@ -853,7 +853,7 @@ var witnessSizeTests = []witnessSizeTest{ signer := &dummySigner{} commitScriptTree, err := input.NewLocalCommitScriptTree( testCSVDelay, testKey.PubKey(), - testKey.PubKey(), + testKey.PubKey(), input.NoneTapLeaf(), ) require.NoError(t, err) @@ -887,7 +887,7 @@ var witnessSizeTests = []witnessSizeTest{ signer := &dummySigner{} commitScriptTree, err := input.NewLocalCommitScriptTree( testCSVDelay, testKey.PubKey(), - testKey.PubKey(), + testKey.PubKey(), input.NoneTapLeaf(), ) require.NoError(t, err) @@ -921,7 +921,7 @@ var witnessSizeTests = []witnessSizeTest{ signer := &dummySigner{} //nolint:lll commitScriptTree, err := input.NewRemoteCommitScriptTree( - testKey.PubKey(), + testKey.PubKey(), input.NoneTapLeaf(), ) require.NoError(t, err) @@ -988,6 +988,7 @@ var witnessSizeTests = []witnessSizeTest{ scriptTree, err := input.SecondLevelHtlcTapscriptTree( testKey.PubKey(), testCSVDelay, + input.NoneTapLeaf(), ) require.NoError(t, err) @@ -1027,6 +1028,7 @@ var witnessSizeTests = []witnessSizeTest{ scriptTree, err := input.SecondLevelHtlcTapscriptTree( testKey.PubKey(), testCSVDelay, + input.NoneTapLeaf(), ) require.NoError(t, err) @@ -1075,6 +1077,7 @@ var witnessSizeTests = []witnessSizeTest{ htlcScriptTree, err := input.SenderHTLCScriptTaproot( senderKey.PubKey(), receiverKey.PubKey(), revokeKey.PubKey(), payHash[:], lntypes.Remote, + input.NoneTapLeaf(), ) require.NoError(t, err) @@ -1116,7 +1119,7 @@ var witnessSizeTests = []witnessSizeTest{ htlcScriptTree, err := input.ReceiverHTLCScriptTaproot( testCLTVExpiry, senderKey.PubKey(), receiverKey.PubKey(), revokeKey.PubKey(), - payHash[:], lntypes.Remote, + payHash[:], lntypes.Remote, input.NoneTapLeaf(), ) require.NoError(t, err) @@ -1158,7 +1161,7 @@ var witnessSizeTests = []witnessSizeTest{ htlcScriptTree, err := input.ReceiverHTLCScriptTaproot( testCLTVExpiry, senderKey.PubKey(), receiverKey.PubKey(), revokeKey.PubKey(), - payHash[:], lntypes.Remote, + payHash[:], lntypes.Remote, input.NoneTapLeaf(), ) require.NoError(t, err) @@ -1205,6 +1208,7 @@ var witnessSizeTests = []witnessSizeTest{ htlcScriptTree, err := input.SenderHTLCScriptTaproot( senderKey.PubKey(), receiverKey.PubKey(), revokeKey.PubKey(), payHash[:], lntypes.Remote, + input.NoneTapLeaf(), ) require.NoError(t, err) @@ -1265,6 +1269,7 @@ var witnessSizeTests = []witnessSizeTest{ htlcScriptTree, err := input.SenderHTLCScriptTaproot( senderKey.PubKey(), receiverKey.PubKey(), revokeKey.PubKey(), payHash[:], lntypes.Remote, + input.NoneTapLeaf(), ) require.NoError(t, err) @@ -1310,7 +1315,7 @@ var witnessSizeTests = []witnessSizeTest{ htlcScriptTree, err := input.ReceiverHTLCScriptTaproot( testCLTVExpiry, senderKey.PubKey(), receiverKey.PubKey(), revokeKey.PubKey(), - payHash[:], lntypes.Remote, + payHash[:], lntypes.Remote, input.NoneTapLeaf(), ) require.NoError(t, err) @@ -1396,7 +1401,7 @@ func genTimeoutTx(t *testing.T, if chanType.IsTaproot() { tapscriptTree, err = input.SenderHTLCScriptTaproot( testPubkey, testPubkey, testPubkey, testHash160, - lntypes.Remote, + lntypes.Remote, input.NoneTapLeaf(), ) require.NoError(t, err) @@ -1465,7 +1470,7 @@ func genSuccessTx(t *testing.T, chanType channeldb.ChannelType) *wire.MsgTx { if chanType.IsTaproot() { tapscriptTree, err = input.ReceiverHTLCScriptTaproot( testCLTVExpiry, testPubkey, testPubkey, testPubkey, - testHash160, lntypes.Remote, + testHash160, lntypes.Remote, input.NoneTapLeaf(), ) require.NoError(t, err) diff --git a/input/taproot.go b/input/taproot.go index 34cdb974d5..0fcd1df4e2 100644 --- a/input/taproot.go +++ b/input/taproot.go @@ -8,6 +8,7 @@ import ( "github.com/btcsuite/btcd/txscript" "github.com/btcsuite/btcd/wire" "github.com/btcsuite/btcwallet/waddrmgr" + "github.com/lightningnetwork/lnd/fn" ) const ( @@ -21,6 +22,15 @@ const ( PubKeyFormatCompressedOdd byte = 0x03 ) +// AuxTapLeaf is a type alias for an optional tapscript leaf that may be added +// to the tapscript tree of HTLC and commitment outputs. +type AuxTapLeaf = fn.Option[txscript.TapLeaf] + +// NoneTapLeaf returns an empty optional tapscript leaf. +func NoneTapLeaf() AuxTapLeaf { + return fn.None[txscript.TapLeaf]() +} + // NewTxSigHashesV0Only returns a new txscript.TxSigHashes instance that will // only calculate the sighash midstate values for segwit v0 inputs and can // therefore never be used for transactions that want to spend segwit v1 diff --git a/input/taproot_test.go b/input/taproot_test.go index 434be2dfdb..a1259be196 100644 --- a/input/taproot_test.go +++ b/input/taproot_test.go @@ -1,13 +1,16 @@ package input import ( + "bytes" "crypto/rand" + "fmt" "testing" "github.com/btcsuite/btcd/btcec/v2" "github.com/btcsuite/btcd/btcutil" "github.com/btcsuite/btcd/txscript" "github.com/btcsuite/btcd/wire" + "github.com/lightningnetwork/lnd/fn" "github.com/lightningnetwork/lnd/keychain" "github.com/lightningnetwork/lnd/lntypes" "github.com/stretchr/testify/require" @@ -31,7 +34,9 @@ type testSenderHtlcScriptTree struct { htlcAmt int64 } -func newTestSenderHtlcScriptTree(t *testing.T) *testSenderHtlcScriptTree { +func newTestSenderHtlcScriptTree(t *testing.T, + auxLeaf AuxTapLeaf) *testSenderHtlcScriptTree { + var preImage lntypes.Preimage _, err := rand.Read(preImage[:]) require.NoError(t, err) @@ -48,7 +53,7 @@ func newTestSenderHtlcScriptTree(t *testing.T) *testSenderHtlcScriptTree { payHash := preImage.Hash() htlcScriptTree, err := SenderHTLCScriptTaproot( senderKey.PubKey(), receiverKey.PubKey(), revokeKey.PubKey(), - payHash[:], lntypes.Remote, + payHash[:], lntypes.Remote, auxLeaf, ) require.NoError(t, err) @@ -207,13 +212,9 @@ func htlcSenderTimeoutWitnessGen(sigHash txscript.SigHashType, } } -// TestTaprootSenderHtlcSpend tests that all the positive and negative paths -// for the sender HTLC tapscript tree work as expected. -func TestTaprootSenderHtlcSpend(t *testing.T) { - t.Parallel() - +func testTaprootSenderHtlcSpend(t *testing.T, auxLeaf AuxTapLeaf) { // First, create a new test script tree. - htlcScriptTree := newTestSenderHtlcScriptTree(t) + htlcScriptTree := newTestSenderHtlcScriptTree(t, auxLeaf) spendTx := wire.NewMsgTx(2) spendTx.AddTxIn(&wire.TxIn{}) @@ -432,6 +433,26 @@ func TestTaprootSenderHtlcSpend(t *testing.T) { } } +// TestTaprootSenderHtlcSpend tests that all the positive and negative paths +// for the sender HTLC tapscript tree work as expected. +func TestTaprootSenderHtlcSpend(t *testing.T) { + t.Parallel() + + for _, hasAuxLeaf := range []bool{true, false} { + name := fmt.Sprintf("aux_leaf=%v", hasAuxLeaf) + t.Run(name, func(t *testing.T) { + var auxLeaf AuxTapLeaf + if hasAuxLeaf { + auxLeaf = fn.Some(txscript.NewBaseTapLeaf( + bytes.Repeat([]byte{0x01}, 32), + )) + } + + testTaprootSenderHtlcSpend(t, auxLeaf) + }) + } +} + type testReceiverHtlcScriptTree struct { preImage lntypes.Preimage @@ -452,7 +473,9 @@ type testReceiverHtlcScriptTree struct { lockTime int32 } -func newTestReceiverHtlcScriptTree(t *testing.T) *testReceiverHtlcScriptTree { +func newTestReceiverHtlcScriptTree(t *testing.T, + auxLeaf AuxTapLeaf) *testReceiverHtlcScriptTree { + var preImage lntypes.Preimage _, err := rand.Read(preImage[:]) require.NoError(t, err) @@ -471,7 +494,7 @@ func newTestReceiverHtlcScriptTree(t *testing.T) *testReceiverHtlcScriptTree { payHash := preImage.Hash() htlcScriptTree, err := ReceiverHTLCScriptTaproot( cltvExpiry, senderKey.PubKey(), receiverKey.PubKey(), - revokeKey.PubKey(), payHash[:], lntypes.Remote, + revokeKey.PubKey(), payHash[:], lntypes.Remote, auxLeaf, ) require.NoError(t, err) @@ -629,15 +652,11 @@ func htlcReceiverSuccessWitnessGen(sigHash txscript.SigHashType, } } -// TestTaprootReceiverHtlcSpend tests that all possible paths for redeeming an -// accepted HTLC (on the commitment transaction) of the receiver work properly. -func TestTaprootReceiverHtlcSpend(t *testing.T) { - t.Parallel() - +func testTaprootReceiverHtlcSpend(t *testing.T, auxLeaf AuxTapLeaf) { // We'll start by creating the HTLC script tree (contains all 3 valid // spend paths), and also a mock spend transaction that we'll be // signing below. - htlcScriptTree := newTestReceiverHtlcScriptTree(t) + htlcScriptTree := newTestReceiverHtlcScriptTree(t, auxLeaf) // TODO(roasbeef): issue with revoke key??? ctrl block even/odd @@ -891,6 +910,28 @@ func TestTaprootReceiverHtlcSpend(t *testing.T) { } } +// TestTaprootReceiverHtlcSpend tests that all possible paths for redeeming an +// accepted HTLC (on the commitment transaction) of the receiver work properly. +func TestTaprootReceiverHtlcSpend(t *testing.T) { + t.Parallel() + + for _, hasAuxLeaf := range []bool{true, false} { + name := fmt.Sprintf("aux_leaf=%v", hasAuxLeaf) + t.Run(name, func(t *testing.T) { + var auxLeaf AuxTapLeaf + if hasAuxLeaf { + auxLeaf = fn.Some( + txscript.NewBaseTapLeaf( + bytes.Repeat([]byte{0x01}, 32), + ), + ) + } + + testTaprootReceiverHtlcSpend(t, auxLeaf) + }) + } +} + type testCommitScriptTree struct { csvDelay uint32 @@ -905,7 +946,9 @@ type testCommitScriptTree struct { *CommitScriptTree } -func newTestCommitScriptTree(local bool) (*testCommitScriptTree, error) { +func newTestCommitScriptTree(local bool, + auxLeaf AuxTapLeaf) (*testCommitScriptTree, error) { + selfKey, err := btcec.NewPrivateKey() if err != nil { return nil, err @@ -925,10 +968,11 @@ func newTestCommitScriptTree(local bool) (*testCommitScriptTree, error) { if local { commitScriptTree, err = NewLocalCommitScriptTree( csvDelay, selfKey.PubKey(), revokeKey.PubKey(), + auxLeaf, ) } else { commitScriptTree, err = NewRemoteCommitScriptTree( - selfKey.PubKey(), + selfKey.PubKey(), auxLeaf, ) } if err != nil { @@ -1020,12 +1064,8 @@ func localCommitRevokeWitGen(sigHash txscript.SigHashType, } } -// TestTaprootCommitScriptToSelf tests that the taproot script for redeeming -// one's output after a force close behaves as expected. -func TestTaprootCommitScriptToSelf(t *testing.T) { - t.Parallel() - - commitScriptTree, err := newTestCommitScriptTree(true) +func testTaprootCommitScriptToSelf(t *testing.T, auxLeaf AuxTapLeaf) { + commitScriptTree, err := newTestCommitScriptTree(true, auxLeaf) require.NoError(t, err) spendTx := wire.NewMsgTx(2) @@ -1187,6 +1227,26 @@ func TestTaprootCommitScriptToSelf(t *testing.T) { } } +// TestTaprootCommitScriptToSelf tests that the taproot script for redeeming +// one's output after a force close behaves as expected. +func TestTaprootCommitScriptToSelf(t *testing.T) { + t.Parallel() + + for _, hasAuxLeaf := range []bool{true, false} { + name := fmt.Sprintf("aux_leaf=%v", hasAuxLeaf) + t.Run(name, func(t *testing.T) { + var auxLeaf AuxTapLeaf + if hasAuxLeaf { + auxLeaf = fn.Some(txscript.NewBaseTapLeaf( + bytes.Repeat([]byte{0x01}, 32), + )) + } + + testTaprootCommitScriptToSelf(t, auxLeaf) + }) + } +} + func remoteCommitSweepWitGen(sigHash txscript.SigHashType, commitScriptTree *testCommitScriptTree) witnessGen { @@ -1220,12 +1280,8 @@ func remoteCommitSweepWitGen(sigHash txscript.SigHashType, } } -// TestTaprootCommitScriptRemote tests that the remote party can properly sweep -// their output after force close. -func TestTaprootCommitScriptRemote(t *testing.T) { - t.Parallel() - - commitScriptTree, err := newTestCommitScriptTree(false) +func testTaprootCommitScriptRemote(t *testing.T, auxLeaf AuxTapLeaf) { + commitScriptTree, err := newTestCommitScriptTree(false, auxLeaf) require.NoError(t, err) spendTx := wire.NewMsgTx(2) @@ -1364,6 +1420,26 @@ func TestTaprootCommitScriptRemote(t *testing.T) { } } +// TestTaprootCommitScriptRemote tests that the remote party can properly sweep +// their output after force close. +func TestTaprootCommitScriptRemote(t *testing.T) { + t.Parallel() + + for _, hasAuxLeaf := range []bool{true, false} { + name := fmt.Sprintf("aux_leaf=%v", hasAuxLeaf) + t.Run(name, func(t *testing.T) { + var auxLeaf AuxTapLeaf + if hasAuxLeaf { + auxLeaf = fn.Some(txscript.NewBaseTapLeaf( + bytes.Repeat([]byte{0x01}, 32), + )) + } + + testTaprootCommitScriptRemote(t, auxLeaf) + }) + } +} + type testAnchorScriptTree struct { sweepKey *btcec.PrivateKey @@ -1599,25 +1675,21 @@ type testSecondLevelHtlcTree struct { tapScriptRoot []byte } -func newTestSecondLevelHtlcTree() (*testSecondLevelHtlcTree, error) { +func newTestSecondLevelHtlcTree(t *testing.T, + auxLeaf AuxTapLeaf) *testSecondLevelHtlcTree { + delayKey, err := btcec.NewPrivateKey() - if err != nil { - return nil, err - } + require.NoError(t, err) revokeKey, err := btcec.NewPrivateKey() - if err != nil { - return nil, err - } + require.NoError(t, err) const csvDelay = 6 scriptTree, err := SecondLevelHtlcTapscriptTree( - delayKey.PubKey(), csvDelay, + delayKey.PubKey(), csvDelay, auxLeaf, ) - if err != nil { - return nil, err - } + require.NoError(t, err) tapScriptRoot := scriptTree.RootNode.TapHash() @@ -1626,9 +1698,7 @@ func newTestSecondLevelHtlcTree() (*testSecondLevelHtlcTree, error) { ) pkScript, err := PayToTaprootScript(htlcKey) - if err != nil { - return nil, err - } + require.NoError(t, err) const amt = 100 @@ -1643,7 +1713,7 @@ func newTestSecondLevelHtlcTree() (*testSecondLevelHtlcTree, error) { amt: amt, scriptTree: scriptTree, tapScriptRoot: tapScriptRoot[:], - }, nil + } } func secondLevelHtlcSuccessWitGen(sigHash txscript.SigHashType, @@ -1713,13 +1783,8 @@ func secondLevelHtlcRevokeWitnessgen(sigHash txscript.SigHashType, } } -// TestTaprootSecondLevelHtlcScript tests that a channel peer can properly -// spend the second level HTLC script to resolve HTLCs. -func TestTaprootSecondLevelHtlcScript(t *testing.T) { - t.Parallel() - - htlcScriptTree, err := newTestSecondLevelHtlcTree() - require.NoError(t, err) +func testTaprootSecondLevelHtlcScript(t *testing.T, auxLeaf AuxTapLeaf) { + htlcScriptTree := newTestSecondLevelHtlcTree(t, auxLeaf) spendTx := wire.NewMsgTx(2) spendTx.AddTxIn(&wire.TxIn{}) @@ -1879,3 +1944,23 @@ func TestTaprootSecondLevelHtlcScript(t *testing.T) { }) } } + +// TestTaprootSecondLevelHtlcScript tests that a channel peer can properly +// spend the second level HTLC script to resolve HTLCs. +func TestTaprootSecondLevelHtlcScript(t *testing.T) { + t.Parallel() + + for _, hasAuxLeaf := range []bool{true, false} { + name := fmt.Sprintf("aux_leaf=%v", hasAuxLeaf) + t.Run(name, func(t *testing.T) { + var auxLeaf AuxTapLeaf + if hasAuxLeaf { + auxLeaf = fn.Some(txscript.NewBaseTapLeaf( + bytes.Repeat([]byte{0x01}, 32), + )) + } + + testTaprootSecondLevelHtlcScript(t, auxLeaf) + }) + } +} diff --git a/lnwallet/channel.go b/lnwallet/channel.go index 4079d961dd..4a9cc50e26 100644 --- a/lnwallet/channel.go +++ b/lnwallet/channel.go @@ -6701,6 +6701,7 @@ func newOutgoingHtlcResolution(signer input.Signer, //nolint:lll secondLevelScriptTree, err := input.TaprootSecondLevelScriptTree( keyRing.RevocationKey, keyRing.ToLocalKey, csvDelay, + fn.None[txscript.TapLeaf](), ) if err != nil { return nil, err @@ -6947,6 +6948,7 @@ func newIncomingHtlcResolution(signer input.Signer, //nolint:lll secondLevelScriptTree, err := input.TaprootSecondLevelScriptTree( keyRing.RevocationKey, keyRing.ToLocalKey, csvDelay, + fn.None[txscript.TapLeaf](), ) if err != nil { return nil, err diff --git a/lnwallet/commitment.go b/lnwallet/commitment.go index f32408814c..5af48eefac 100644 --- a/lnwallet/commitment.go +++ b/lnwallet/commitment.go @@ -225,9 +225,8 @@ func (w *WitnessScriptDesc) WitnessScriptForPath( // party learns of the preimage to the revocation hash, then they can claim all // the settled funds in the channel, plus the unsettled funds. func CommitScriptToSelf(chanType channeldb.ChannelType, initiator bool, - selfKey, revokeKey *btcec.PublicKey, csvDelay, leaseExpiry uint32, -) ( - input.ScriptDescriptor, error) { + selfKey, revokeKey *btcec.PublicKey, csvDelay, + leaseExpiry uint32) (input.ScriptDescriptor, error) { switch { // For taproot scripts, we'll need to make a slightly modified script @@ -237,7 +236,7 @@ func CommitScriptToSelf(chanType channeldb.ChannelType, initiator bool, // Our "redeem" script here is just the taproot witness program. case chanType.IsTaproot(): return input.NewLocalCommitScriptTree( - csvDelay, selfKey, revokeKey, + csvDelay, selfKey, revokeKey, input.NoneTapLeaf(), ) // If we are the initiator of a leased channel, then we have an @@ -321,7 +320,7 @@ func CommitScriptToRemote(chanType channeldb.ChannelType, initiator bool, // with the sole tap leaf enforcing the 1 CSV delay. case chanType.IsTaproot(): toRemoteScriptTree, err := input.NewRemoteCommitScriptTree( - remoteKey, + remoteKey, input.NoneTapLeaf(), ) if err != nil { return nil, 0, err @@ -427,7 +426,7 @@ func SecondLevelHtlcScript(chanType channeldb.ChannelType, initiator bool, // For taproot channels, the pkScript is a segwit v1 p2tr output. case chanType.IsTaproot(): return input.TaprootSecondLevelScriptTree( - revocationKey, delayKey, csvDelay, + revocationKey, delayKey, csvDelay, input.NoneTapLeaf(), ) // If we are the initiator of a leased channel, then we have an @@ -1095,6 +1094,7 @@ func GenTaprootHtlcScript(isIncoming bool, whoseCommit lntypes.ChannelParty, htlcScriptTree, err = input.ReceiverHTLCScriptTaproot( timeout, keyRing.RemoteHtlcKey, keyRing.LocalHtlcKey, keyRing.RevocationKey, rHash[:], whoseCommit, + input.NoneTapLeaf(), ) // We're being paid via an HTLC by the remote party, and the HTLC is @@ -1104,6 +1104,7 @@ func GenTaprootHtlcScript(isIncoming bool, whoseCommit lntypes.ChannelParty, htlcScriptTree, err = input.SenderHTLCScriptTaproot( keyRing.RemoteHtlcKey, keyRing.LocalHtlcKey, keyRing.RevocationKey, rHash[:], whoseCommit, + input.NoneTapLeaf(), ) // We're sending an HTLC which is being added to our commitment @@ -1113,6 +1114,7 @@ func GenTaprootHtlcScript(isIncoming bool, whoseCommit lntypes.ChannelParty, htlcScriptTree, err = input.SenderHTLCScriptTaproot( keyRing.LocalHtlcKey, keyRing.RemoteHtlcKey, keyRing.RevocationKey, rHash[:], whoseCommit, + input.NoneTapLeaf(), ) // Finally, we're paying the remote party via an HTLC, which is being @@ -1122,6 +1124,7 @@ func GenTaprootHtlcScript(isIncoming bool, whoseCommit lntypes.ChannelParty, htlcScriptTree, err = input.ReceiverHTLCScriptTaproot( timeout, keyRing.LocalHtlcKey, keyRing.RemoteHtlcKey, keyRing.RevocationKey, rHash[:], whoseCommit, + input.NoneTapLeaf(), ) } diff --git a/watchtower/blob/justice_kit.go b/watchtower/blob/justice_kit.go index 8b6c20194f..7780239f07 100644 --- a/watchtower/blob/justice_kit.go +++ b/watchtower/blob/justice_kit.go @@ -10,6 +10,7 @@ import ( "github.com/btcsuite/btcd/txscript" "github.com/btcsuite/btcd/wire" secp "github.com/decred/dcrd/dcrec/secp256k1/v4" + "github.com/lightningnetwork/lnd/fn" "github.com/lightningnetwork/lnd/input" "github.com/lightningnetwork/lnd/lnwallet" "github.com/lightningnetwork/lnd/lnwire" @@ -307,9 +308,11 @@ func newTaprootJusticeKit(sweepScript []byte, keyRing := breachInfo.KeyRing + // TODO(roasbeef): aux leaf tower updates needed + tree, err := input.NewLocalCommitScriptTree( breachInfo.RemoteDelay, keyRing.ToLocalKey, - keyRing.RevocationKey, + keyRing.RevocationKey, fn.None[txscript.TapLeaf](), ) if err != nil { return nil, err @@ -416,7 +419,9 @@ func (t *taprootJusticeKit) ToRemoteOutputSpendInfo() (*txscript.PkScript, return nil, nil, 0, err } - scriptTree, err := input.NewRemoteCommitScriptTree(toRemotePk) + scriptTree, err := input.NewRemoteCommitScriptTree( + toRemotePk, fn.None[txscript.TapLeaf](), + ) if err != nil { return nil, nil, 0, err } diff --git a/watchtower/blob/justice_kit_test.go b/watchtower/blob/justice_kit_test.go index fd12993a0a..a1d6ec9f2c 100644 --- a/watchtower/blob/justice_kit_test.go +++ b/watchtower/blob/justice_kit_test.go @@ -12,6 +12,7 @@ import ( "github.com/btcsuite/btcd/btcec/v2/schnorr" "github.com/btcsuite/btcd/txscript" "github.com/btcsuite/btcd/wire" + "github.com/lightningnetwork/lnd/fn" "github.com/lightningnetwork/lnd/input" "github.com/lightningnetwork/lnd/lnwallet" "github.com/lightningnetwork/lnd/lnwire" @@ -304,7 +305,9 @@ func TestJusticeKitRemoteWitnessConstruction(t *testing.T) { name: "taproot commitment", blobType: TypeAltruistTaprootCommit, expWitnessScript: func(pk *btcec.PublicKey) []byte { - tree, _ := input.NewRemoteCommitScriptTree(pk) + tree, _ := input.NewRemoteCommitScriptTree( + pk, fn.None[txscript.TapLeaf](), + ) return tree.SettleLeaf.Script }, @@ -461,6 +464,7 @@ func TestJusticeKitToLocalWitnessConstruction(t *testing.T) { script, _ := input.NewLocalCommitScriptTree( csvDelay, delay, rev, + fn.None[txscript.TapLeaf](), ) return script.RevocationLeaf.Script diff --git a/watchtower/lookout/justice_descriptor_test.go b/watchtower/lookout/justice_descriptor_test.go index a07b440ad5..5045b4a0f4 100644 --- a/watchtower/lookout/justice_descriptor_test.go +++ b/watchtower/lookout/justice_descriptor_test.go @@ -11,6 +11,7 @@ import ( "github.com/btcsuite/btcd/txscript" "github.com/btcsuite/btcd/wire" secp "github.com/decred/dcrd/dcrec/secp256k1/v4" + "github.com/lightningnetwork/lnd/fn" "github.com/lightningnetwork/lnd/input" "github.com/lightningnetwork/lnd/keychain" "github.com/lightningnetwork/lnd/lnwallet" @@ -123,7 +124,7 @@ func testJusticeDescriptor(t *testing.T, blobType blob.Type) { if isTaprootChannel { toLocalCommitTree, err = input.NewLocalCommitScriptTree( - csvDelay, toLocalPK, revPK, + csvDelay, toLocalPK, revPK, fn.None[txscript.TapLeaf](), ) require.NoError(t, err) @@ -174,7 +175,7 @@ func testJusticeDescriptor(t *testing.T, blobType blob.Type) { toRemoteSequence = 1 commitScriptTree, err := input.NewRemoteCommitScriptTree( - toRemotePK, + toRemotePK, fn.None[txscript.TapLeaf](), ) require.NoError(t, err) diff --git a/watchtower/wtclient/backup_task_internal_test.go b/watchtower/wtclient/backup_task_internal_test.go index 695c4f9ecd..7894631b8f 100644 --- a/watchtower/wtclient/backup_task_internal_test.go +++ b/watchtower/wtclient/backup_task_internal_test.go @@ -10,6 +10,7 @@ import ( "github.com/btcsuite/btcd/txscript" "github.com/btcsuite/btcd/wire" "github.com/lightningnetwork/lnd/channeldb" + "github.com/lightningnetwork/lnd/fn" "github.com/lightningnetwork/lnd/input" "github.com/lightningnetwork/lnd/keychain" "github.com/lightningnetwork/lnd/lnwallet" @@ -136,6 +137,7 @@ func genTaskTest( if chanType.IsTaproot() { scriptTree, _ := input.NewLocalCommitScriptTree( csvDelay, toLocalPK, revPK, + fn.None[txscript.TapLeaf](), ) pkScript, _ := input.PayToTaprootScript( @@ -189,7 +191,7 @@ func genTaskTest( if chanType.IsTaproot() { scriptTree, _ := input.NewRemoteCommitScriptTree( - toRemotePK, + toRemotePK, fn.None[txscript.TapLeaf](), ) pkScript, _ := input.PayToTaprootScript( diff --git a/watchtower/wtclient/client_test.go b/watchtower/wtclient/client_test.go index 38d9acd9f0..f3a4d5bf4e 100644 --- a/watchtower/wtclient/client_test.go +++ b/watchtower/wtclient/client_test.go @@ -19,6 +19,7 @@ import ( "github.com/lightningnetwork/lnd/chainntnfs" "github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/channelnotifier" + "github.com/lightningnetwork/lnd/fn" "github.com/lightningnetwork/lnd/input" "github.com/lightningnetwork/lnd/keychain" "github.com/lightningnetwork/lnd/kvdb" @@ -230,12 +231,14 @@ func (c *mockChannel) createRemoteCommitTx(t *testing.T) { // Construct the to-local witness script. toLocalScriptTree, err := input.NewLocalCommitScriptTree( - c.csvDelay, c.toLocalPK, c.revPK, + c.csvDelay, c.toLocalPK, c.revPK, fn.None[txscript.TapLeaf](), ) require.NoError(t, err, "unable to create to-local script") // Construct the to-remote witness script. - toRemoteScriptTree, err := input.NewRemoteCommitScriptTree(c.toRemotePK) + toRemoteScriptTree, err := input.NewRemoteCommitScriptTree( + c.toRemotePK, fn.None[txscript.TapLeaf](), + ) require.NoError(t, err, "unable to create to-remote script") // Compute the to-local witness script hash. From cd155d7d174fefd447535d91cda2446dc3b22b85 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Sun, 17 Mar 2024 15:51:50 -0400 Subject: [PATCH 033/218] channeldb: new custom blob nested TLV In this commit, for each channel, we'll now start to store an optional custom blob. This can be used to store extra information for custom channels in an opauqe manner. --- channeldb/channel.go | 153 +++++++++++++++++++++++++++++++++++++- channeldb/channel_test.go | 11 ++- 2 files changed, 159 insertions(+), 5 deletions(-) diff --git a/channeldb/channel.go b/channeldb/channel.go index f29a9ec196..9fed8e8acf 100644 --- a/channeldb/channel.go +++ b/channeldb/channel.go @@ -256,6 +256,10 @@ type openChannelTlvData struct { // tapscriptRoot is the optional Tapscript root the channel funding // output commits to. tapscriptRoot tlv.OptionalRecordT[tlv.TlvType6, [32]byte] + + // customBlob is an optional TLV encoded blob of data representing + // custom channel funding information. + customBlob tlv.OptionalRecordT[tlv.TlvType7, tlv.Blob] } // encode serializes the openChannelTlvData to the given io.Writer. @@ -274,6 +278,9 @@ func (c *openChannelTlvData) encode(w io.Writer) error { tlvRecords = append(tlvRecords, root.Record()) }, ) + c.customBlob.WhenSome(func(blob tlv.RecordT[tlv.TlvType7, tlv.Blob]) { + tlvRecords = append(tlvRecords, blob.Record()) + }) // Create the tlv stream. tlvStream, err := tlv.NewStream(tlvRecords...) @@ -288,6 +295,7 @@ func (c *openChannelTlvData) encode(w io.Writer) error { func (c *openChannelTlvData) decode(r io.Reader) error { memo := c.memo.Zero() tapscriptRoot := c.tapscriptRoot.Zero() + blob := c.customBlob.Zero() // Create the tlv stream. tlvStream, err := tlv.NewStream( @@ -297,6 +305,7 @@ func (c *openChannelTlvData) decode(r io.Reader) error { c.realScid.Record(), memo.Record(), tapscriptRoot.Record(), + blob.Record(), ) if err != nil { return err @@ -313,6 +322,9 @@ func (c *openChannelTlvData) decode(r io.Reader) error { if _, ok := tlvs[tapscriptRoot.TlvType()]; ok { c.tapscriptRoot = tlv.SomeRecordT(tapscriptRoot) } + if _, ok := tlvs[c.customBlob.TlvType()]; ok { + c.customBlob = tlv.SomeRecordT(blob) + } return nil } @@ -576,6 +588,53 @@ type ChannelConfig struct { HtlcBasePoint keychain.KeyDescriptor } +// commitTlvData stores all the optional data that may be stored as a TLV stream +// at the _end_ of the normal serialized commit on disk. +type commitTlvData struct { + // customBlob is a custom blob that may store extra data for custom + // channels. + customBlob tlv.OptionalRecordT[tlv.TlvType1, tlv.Blob] +} + +// encode encodes the aux data into the passed io.Writer. +func (c *commitTlvData) encode(w io.Writer) error { + var tlvRecords []tlv.Record + c.customBlob.WhenSome(func(blob tlv.RecordT[tlv.TlvType1, tlv.Blob]) { + tlvRecords = append(tlvRecords, blob.Record()) + }) + + // Create the tlv stream. + tlvStream, err := tlv.NewStream(tlvRecords...) + if err != nil { + return err + } + + return tlvStream.Encode(w) +} + +// decode attempts to decode the aux data from the passed io.Reader. +func (c *commitTlvData) decode(r io.Reader) error { + blob := c.customBlob.Zero() + + tlvStream, err := tlv.NewStream( + blob.Record(), + ) + if err != nil { + return err + } + + tlvs, err := tlvStream.DecodeWithParsedTypes(r) + if err != nil { + return err + } + + if _, ok := tlvs[c.customBlob.TlvType()]; ok { + c.customBlob = tlv.SomeRecordT(blob) + } + + return nil +} + // ChannelCommitment is a snapshot of the commitment state at a particular // point in the commitment chain. With each state transition, a snapshot of the // current state along with all non-settled HTLCs are recorded. These snapshots @@ -642,6 +701,11 @@ type ChannelCommitment struct { // able by us. CommitTx *wire.MsgTx + // CustomBlob is an optional blob that can be used to store information + // specific to a custom channel type. This may track some custom + // specific state for this given commitment. + CustomBlob fn.Option[tlv.Blob] + // CommitSig is one half of the signature required to fully complete // the script for the commitment transaction above. This is the // signature signed by the remote party for our version of the @@ -651,9 +715,26 @@ type ChannelCommitment struct { // Htlcs is the set of HTLC's that are pending at this particular // commitment height. Htlcs []HTLC +} - // TODO(roasbeef): pending commit pointer? - // * lets just walk through +// amendTlvData updates the channel with the given auxiliary TLV data. +func (c *ChannelCommitment) amendTlvData(auxData commitTlvData) { + auxData.customBlob.WhenSomeV(func(blob tlv.Blob) { + c.CustomBlob = fn.Some(blob) + }) +} + +// extractTlvData creates a new commitTlvData from the given commitment. +func (c *ChannelCommitment) extractTlvData() commitTlvData { + var auxData commitTlvData + + c.CustomBlob.WhenSome(func(blob tlv.Blob) { + auxData.customBlob = tlv.SomeRecordT( + tlv.NewPrimitiveRecord[tlv.TlvType1](blob), + ) + }) + + return auxData } // ChannelStatus is a bit vector used to indicate whether an OpenChannel is in @@ -951,6 +1032,12 @@ type OpenChannel struct { // funding output. TapscriptRoot fn.Option[chainhash.Hash] + // CustomBlob is an optional blob that can be used to store information + // specific to a custom channel type. This information is only created + // at channel funding time, and after wards is to be considered + // immutable. + CustomBlob fn.Option[tlv.Blob] + // TODO(roasbeef): eww Db *ChannelStateDB @@ -1126,6 +1213,9 @@ func (c *OpenChannel) amendTlvData(auxData openChannelTlvData) { auxData.tapscriptRoot.WhenSomeV(func(h [32]byte) { c.TapscriptRoot = fn.Some[chainhash.Hash](h) }) + auxData.customBlob.WhenSomeV(func(blob tlv.Blob) { + c.CustomBlob = fn.Some(blob) + }) } // extractTlvData creates a new openChannelTlvData from the given channel. @@ -1155,6 +1245,11 @@ func (c *OpenChannel) extractTlvData() openChannelTlvData { tlv.NewPrimitiveRecord[tlv.TlvType6, [32]byte](h), ) }) + c.CustomBlob.WhenSome(func(blob tlv.Blob) { + auxData.customBlob = tlv.SomeRecordT( + tlv.NewPrimitiveRecord[tlv.TlvType7](blob), + ) + }) return auxData } @@ -2824,6 +2919,14 @@ func serializeCommitDiff(w io.Writer, diff *CommitDiff) error { // nolint: dupl } } + // We'll also encode the commit aux data stream here. We do this here + // rather than above (at the call to serializeChanCommit), to ensure + // backwards compat for reads to existing non-custom channels. + auxData := diff.Commitment.extractTlvData() + if err := auxData.encode(w); err != nil { + return fmt.Errorf("unable to write aux data: %w", err) + } + return nil } @@ -2884,6 +2987,17 @@ func deserializeCommitDiff(r io.Reader) (*CommitDiff, error) { } } + // As a final step, we'll read out any aux commit data that we have at + // the end of this byte stream. We do this here to ensure backward + // compatibility, as otherwise we risk erroneously reading into the + // wrong field. + var auxData commitTlvData + if err := auxData.decode(r); err != nil { + return nil, fmt.Errorf("unable to decode aux data: %w", err) + } + + d.Commitment.amendTlvData(auxData) + return &d, nil } @@ -3862,6 +3976,13 @@ func (c *OpenChannel) Snapshot() *ChannelSnapshot { }, } + localCommit.CustomBlob.WhenSome(func(blob tlv.Blob) { + blobCopy := make([]byte, len(blob)) + copy(blobCopy, blob) + + snapshot.ChannelCommitment.CustomBlob = fn.Some(blobCopy) + }) + // Copy over the current set of HTLCs to ensure the caller can't mutate // our internal state. snapshot.Htlcs = make([]HTLC, len(localCommit.Htlcs)) @@ -4253,6 +4374,12 @@ func putChanCommitment(chanBucket kvdb.RwBucket, c *ChannelCommitment, return err } + // Before we write to disk, we'll also write our aux data as well. + auxData := c.extractTlvData() + if err := auxData.encode(&b); err != nil { + return fmt.Errorf("unable to write aux data: %w", err) + } + return chanBucket.Put(commitKey, b.Bytes()) } @@ -4398,7 +4525,9 @@ func deserializeChanCommit(r io.Reader) (ChannelCommitment, error) { return c, nil } -func fetchChanCommitment(chanBucket kvdb.RBucket, local bool) (ChannelCommitment, error) { +func fetchChanCommitment(chanBucket kvdb.RBucket, + local bool) (ChannelCommitment, error) { + var commitKey []byte if local { commitKey = append(chanCommitmentKey, byte(0x00)) @@ -4412,7 +4541,23 @@ func fetchChanCommitment(chanBucket kvdb.RBucket, local bool) (ChannelCommitment } r := bytes.NewReader(commitBytes) - return deserializeChanCommit(r) + chanCommit, err := deserializeChanCommit(r) + if err != nil { + return ChannelCommitment{}, fmt.Errorf("unable to decode "+ + "chan commit: %w", err) + } + + // We'll also check to see if we have any aux data stored as the end of + // the stream. + var auxData commitTlvData + if err := auxData.decode(r); err != nil { + return ChannelCommitment{}, fmt.Errorf("unable to decode "+ + "chan aux data: %w", err) + } + + chanCommit.amendTlvData(auxData) + + return chanCommit, nil } func fetchChanCommitments(chanBucket kvdb.RBucket, channel *OpenChannel) error { diff --git a/channeldb/channel_test.go b/channeldb/channel_test.go index 84aae96225..13d77276cb 100644 --- a/channeldb/channel_test.go +++ b/channeldb/channel_test.go @@ -351,6 +351,7 @@ func createTestChannelState(t *testing.T, cdb *ChannelStateDB) *OpenChannel { FeePerKw: btcutil.Amount(5000), CommitTx: channels.TestFundingTx, CommitSig: bytes.Repeat([]byte{1}, 71), + CustomBlob: fn.Some([]byte{1, 2, 3}), }, RemoteCommitment: ChannelCommitment{ CommitHeight: 0, @@ -360,6 +361,7 @@ func createTestChannelState(t *testing.T, cdb *ChannelStateDB) *OpenChannel { FeePerKw: btcutil.Amount(5000), CommitTx: channels.TestFundingTx, CommitSig: bytes.Repeat([]byte{1}, 71), + CustomBlob: fn.Some([]byte{4, 5, 6}), }, NumConfsRequired: 4, RemoteCurrentRevocation: privKey.PubKey(), @@ -374,6 +376,7 @@ func createTestChannelState(t *testing.T, cdb *ChannelStateDB) *OpenChannel { InitialRemoteBalance: lnwire.MilliSatoshi(3000), Memo: []byte("test"), TapscriptRoot: fn.Some(tapscriptRoot), + CustomBlob: fn.Some([]byte{1, 2, 3}), } } @@ -663,6 +666,7 @@ func TestChannelStateTransition(t *testing.T) { CommitTx: newTx, CommitSig: newSig, Htlcs: htlcs, + CustomBlob: fn.Some([]byte{4, 5, 6}), } // First update the local node's broadcastable state and also add a @@ -700,9 +704,14 @@ func TestChannelStateTransition(t *testing.T) { // have been updated. updatedChannel, err := cdb.FetchOpenChannels(channel.IdentityPub) require.NoError(t, err, "unable to fetch updated channel") - assertCommitmentEqual(t, &commitment, &updatedChannel[0].LocalCommitment) + + assertCommitmentEqual( + t, &commitment, &updatedChannel[0].LocalCommitment, + ) + numDiskUpdates, err := updatedChannel[0].CommitmentHeight() require.NoError(t, err, "unable to read commitment height from disk") + if numDiskUpdates != uint64(commitment.CommitHeight) { t.Fatalf("num disk updates doesn't match: %v vs %v", numDiskUpdates, commitment.CommitHeight) From da9ceb131b9cd1c40849082b23df0578f2964246 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Mon, 1 Apr 2024 20:00:29 -0700 Subject: [PATCH 034/218] lnwallet: export the HtlcView struct We'll need this later on to ensure we can always interact with the new aux blobs at all stages of commitment transaction construction. --- lnwallet/channel.go | 139 ++++++++++++++++++++++++--------------- lnwallet/channel_test.go | 22 +++---- lnwallet/commitment.go | 10 +-- 3 files changed, 102 insertions(+), 69 deletions(-) diff --git a/lnwallet/channel.go b/lnwallet/channel.go index 4a9cc50e26..30ae20073f 100644 --- a/lnwallet/channel.go +++ b/lnwallet/channel.go @@ -2459,18 +2459,29 @@ func HtlcIsDust(chanType channeldb.ChannelType, return (htlcAmt - htlcFee) < dustLimit } -// htlcView represents the "active" HTLCs at a particular point within the +// HtlcView represents the "active" HTLCs at a particular point within the // history of the HTLC update log. -type htlcView struct { - ourUpdates []*PaymentDescriptor - theirUpdates []*PaymentDescriptor - feePerKw chainfee.SatPerKWeight +type HtlcView struct { + // NextHeight is the height of the commitment transaction that will be + // created using this view. + NextHeight uint64 + + // OurUpdates are our outgoing HTLCs. + OurUpdates []*PaymentDescriptor + + // TheirUpdates are their incoming HTLCs. + TheirUpdates []*PaymentDescriptor + + // FeePerKw is the fee rate in sat/kw of the commitment transaction. + FeePerKw chainfee.SatPerKWeight } // fetchHTLCView returns all the candidate HTLC updates which should be // considered for inclusion within a commitment based on the passed HTLC log // indexes. -func (lc *LightningChannel) fetchHTLCView(theirLogIndex, ourLogIndex uint64) *htlcView { +func (lc *LightningChannel) fetchHTLCView(theirLogIndex, + ourLogIndex uint64) *HtlcView { + var ourHTLCs []*PaymentDescriptor for e := lc.localUpdateLog.Front(); e != nil; e = e.Next() { htlc := e.Value @@ -2495,9 +2506,9 @@ func (lc *LightningChannel) fetchHTLCView(theirLogIndex, ourLogIndex uint64) *ht } } - return &htlcView{ - ourUpdates: ourHTLCs, - theirUpdates: theirHTLCs, + return &HtlcView{ + OurUpdates: ourHTLCs, + TheirUpdates: theirHTLCs, } } @@ -2534,7 +2545,10 @@ func (lc *LightningChannel) fetchCommitmentView( if err != nil { return nil, err } - feePerKw := filteredHTLCView.feePerKw + feePerKw := filteredHTLCView.FeePerKw + + htlcView.NextHeight = nextHeight + filteredHTLCView.NextHeight = nextHeight // Actually generate unsigned commitment transaction for this view. commitTx, err := lc.commitBuilder.createUnsignedCommitmentTx( @@ -2594,12 +2608,16 @@ func (lc *LightningChannel) fetchCommitmentView( // In order to ensure _none_ of the HTLC's associated with this new // commitment are mutated, we'll manually copy over each HTLC to its // respective slice. - c.outgoingHTLCs = make([]PaymentDescriptor, len(filteredHTLCView.ourUpdates)) - for i, htlc := range filteredHTLCView.ourUpdates { + c.outgoingHTLCs = make( + []PaymentDescriptor, len(filteredHTLCView.OurUpdates), + ) + for i, htlc := range filteredHTLCView.OurUpdates { c.outgoingHTLCs[i] = *htlc } - c.incomingHTLCs = make([]PaymentDescriptor, len(filteredHTLCView.theirUpdates)) - for i, htlc := range filteredHTLCView.theirUpdates { + c.incomingHTLCs = make( + []PaymentDescriptor, len(filteredHTLCView.TheirUpdates), + ) + for i, htlc := range filteredHTLCView.TheirUpdates { c.incomingHTLCs[i] = *htlc } @@ -2634,16 +2652,17 @@ func fundingTxIn(chanState *channeldb.OpenChannel) wire.TxIn { // once for each height, and only in concert with signing a new commitment. // TODO(halseth): return htlcs to mutate instead of mutating inside // method. -func (lc *LightningChannel) evaluateHTLCView(view *htlcView, ourBalance, +func (lc *LightningChannel) evaluateHTLCView(view *HtlcView, ourBalance, theirBalance *lnwire.MilliSatoshi, nextHeight uint64, - whoseCommitChain lntypes.ChannelParty, mutateState bool, -) (*htlcView, error) { + whoseCommitChain lntypes.ChannelParty, mutateState bool) (*HtlcView, + error) { // We initialize the view's fee rate to the fee rate of the unfiltered // view. If any fee updates are found when evaluating the view, it will // be updated. - newView := &htlcView{ - feePerKw: view.feePerKw, + newView := &HtlcView{ + FeePerKw: view.FeePerKw, + NextHeight: nextHeight, } // We use two maps, one for the local log and one for the remote log to @@ -2656,7 +2675,7 @@ func (lc *LightningChannel) evaluateHTLCView(view *htlcView, ourBalance, // First we run through non-add entries in both logs, populating the // skip sets and mutating the current chain state (crediting balances, // etc) to reflect the settle/timeout entry encountered. - for _, entry := range view.ourUpdates { + for _, entry := range view.OurUpdates { switch entry.EntryType { // Skip adds for now. They will be processed below. case Add: @@ -2688,10 +2707,13 @@ func (lc *LightningChannel) evaluateHTLCView(view *htlcView, ourBalance, } skipThem[addEntry.HtlcIndex] = struct{}{} - processRemoveEntry(entry, ourBalance, theirBalance, - nextHeight, whoseCommitChain, true, mutateState) + + processRemoveEntry( + entry, ourBalance, theirBalance, nextHeight, + whoseCommitChain, true, mutateState, + ) } - for _, entry := range view.theirUpdates { + for _, entry := range view.TheirUpdates { switch entry.EntryType { // Skip adds for now. They will be processed below. case Add: @@ -2725,32 +2747,41 @@ func (lc *LightningChannel) evaluateHTLCView(view *htlcView, ourBalance, } skipUs[addEntry.HtlcIndex] = struct{}{} - processRemoveEntry(entry, ourBalance, theirBalance, - nextHeight, whoseCommitChain, false, mutateState) + + processRemoveEntry( + entry, ourBalance, theirBalance, nextHeight, + whoseCommitChain, false, mutateState, + ) } // Next we take a second pass through all the log entries, skipping any // settled HTLCs, and debiting the chain state balance due to any newly // added HTLCs. - for _, entry := range view.ourUpdates { + for _, entry := range view.OurUpdates { isAdd := entry.EntryType == Add if _, ok := skipUs[entry.HtlcIndex]; !isAdd || ok { continue } - processAddEntry(entry, ourBalance, theirBalance, nextHeight, - whoseCommitChain, false, mutateState) - newView.ourUpdates = append(newView.ourUpdates, entry) + processAddEntry( + entry, ourBalance, theirBalance, nextHeight, + whoseCommitChain, false, mutateState, + ) + + newView.OurUpdates = append(newView.OurUpdates, entry) } - for _, entry := range view.theirUpdates { + for _, entry := range view.TheirUpdates { isAdd := entry.EntryType == Add if _, ok := skipThem[entry.HtlcIndex]; !isAdd || ok { continue } - processAddEntry(entry, ourBalance, theirBalance, nextHeight, - whoseCommitChain, true, mutateState) - newView.theirUpdates = append(newView.theirUpdates, entry) + processAddEntry( + entry, ourBalance, theirBalance, nextHeight, + whoseCommitChain, true, mutateState, + ) + + newView.TheirUpdates = append(newView.TheirUpdates, entry) } return newView, nil @@ -2901,8 +2932,8 @@ func processRemoveEntry(htlc *PaymentDescriptor, ourBalance, // processFeeUpdate processes a log update that updates the current commitment // fee. func processFeeUpdate(feeUpdate *PaymentDescriptor, nextHeight uint64, - whoseCommitChain lntypes.ChannelParty, mutateState bool, view *htlcView, -) { + whoseCommitChain lntypes.ChannelParty, mutateState bool, + view *HtlcView) { // Fee updates are applied for all commitments after they are // sent/received, so we consider them being added and removed at the @@ -2923,7 +2954,7 @@ func processFeeUpdate(feeUpdate *PaymentDescriptor, nextHeight uint64, // If the update wasn't already locked in, update the current fee rate // to reflect this update. - view.feePerKw = chainfee.SatPerKWeight(feeUpdate.Amount.ToSatoshis()) + view.FeePerKw = chainfee.SatPerKWeight(feeUpdate.Amount.ToSatoshis()) if mutateState { *addHeight = nextHeight @@ -3499,10 +3530,10 @@ func (lc *LightningChannel) validateCommitmentSanity(theirLogCounter, // appropriate update log, in order to validate the sanity of the // commitment resulting from _actually adding_ this HTLC to the state. if predictOurAdd != nil { - view.ourUpdates = append(view.ourUpdates, predictOurAdd) + view.OurUpdates = append(view.OurUpdates, predictOurAdd) } if predictTheirAdd != nil { - view.theirUpdates = append(view.theirUpdates, predictTheirAdd) + view.TheirUpdates = append(view.TheirUpdates, predictTheirAdd) } ourBalance, theirBalance, commitWeight, filteredView, err := lc.computeView( @@ -3513,7 +3544,7 @@ func (lc *LightningChannel) validateCommitmentSanity(theirLogCounter, return err } - feePerKw := filteredView.feePerKw + feePerKw := filteredView.FeePerKw // Ensure that the fee being applied is enough to be relayed across the // network in a reasonable time frame. @@ -3657,7 +3688,7 @@ func (lc *LightningChannel) validateCommitmentSanity(theirLogCounter, // First check that the remote updates won't violate it's channel // constraints. err = validateUpdates( - filteredView.theirUpdates, &lc.channelState.RemoteChanCfg, + filteredView.TheirUpdates, &lc.channelState.RemoteChanCfg, ) if err != nil { return err @@ -3666,7 +3697,7 @@ func (lc *LightningChannel) validateCommitmentSanity(theirLogCounter, // Secondly check that our updates won't violate our channel // constraints. err = validateUpdates( - filteredView.ourUpdates, &lc.channelState.LocalChanCfg, + filteredView.OurUpdates, &lc.channelState.LocalChanCfg, ) if err != nil { return err @@ -4298,7 +4329,7 @@ func (lc *LightningChannel) ProcessChanSyncMsg( return updates, openedCircuits, closedCircuits, nil } -// computeView takes the given htlcView, and calculates the balances, filtered +// computeView takes the given HtlcView, and calculates the balances, filtered // view (settling unsettled HTLCs), commitment weight and feePerKw, after // applying the HTLCs to the latest commitment. The returned balances are the // balances *before* subtracting the commitment fee from the initiator's @@ -4307,10 +4338,10 @@ func (lc *LightningChannel) ProcessChanSyncMsg( // // If the updateState boolean is set true, the add and remove heights of the // HTLCs will be set to the next commitment height. -func (lc *LightningChannel) computeView(view *htlcView, +func (lc *LightningChannel) computeView(view *HtlcView, whoseCommitChain lntypes.ChannelParty, updateState bool, dryRunFee fn.Option[chainfee.SatPerKWeight]) (lnwire.MilliSatoshi, - lnwire.MilliSatoshi, lntypes.WeightUnit, *htlcView, error) { + lnwire.MilliSatoshi, lntypes.WeightUnit, *HtlcView, error) { commitChain := lc.localCommitChain dustLimit := lc.channelState.LocalChanCfg.DustLimit @@ -4341,7 +4372,7 @@ func (lc *LightningChannel) computeView(view *htlcView, // Initiate feePerKw to the last committed fee for this chain as we'll // need this to determine which HTLCs are dust, and also the final fee // rate. - view.feePerKw = commitChain.tip().feePerKw + view.FeePerKw = commitChain.tip().feePerKw // We evaluate the view at this stage, meaning settled and failed HTLCs // will remove their corresponding added HTLCs. The resulting filtered @@ -4349,12 +4380,14 @@ func (lc *LightningChannel) computeView(view *htlcView, // channel constraints to the final commitment state. If any fee // updates are found in the logs, the commitment fee rate should be // changed, so we'll also set the feePerKw to this new value. - filteredHTLCView, err := lc.evaluateHTLCView(view, &ourBalance, - &theirBalance, nextHeight, whoseCommitChain, updateState) + filteredHTLCView, err := lc.evaluateHTLCView( + view, &ourBalance, &theirBalance, nextHeight, whoseCommitChain, + updateState, + ) if err != nil { return 0, 0, 0, nil, err } - feePerKw := filteredHTLCView.feePerKw + feePerKw := filteredHTLCView.FeePerKw // Here we override the view's fee-rate if a dry-run fee-rate was // passed in. @@ -4378,7 +4411,7 @@ func (lc *LightningChannel) computeView(view *htlcView, // Now go through all HTLCs at this stage, to calculate the total // weight, needed to calculate the transaction fee. var totalHtlcWeight lntypes.WeightUnit - for _, htlc := range filteredHTLCView.ourUpdates { + for _, htlc := range filteredHTLCView.OurUpdates { if HtlcIsDust( lc.channelState.ChanType, false, whoseCommitChain, feePerKw, htlc.Amount.ToSatoshis(), dustLimit, @@ -4389,7 +4422,7 @@ func (lc *LightningChannel) computeView(view *htlcView, totalHtlcWeight += input.HTLCWeight } - for _, htlc := range filteredHTLCView.theirUpdates { + for _, htlc := range filteredHTLCView.TheirUpdates { if HtlcIsDust( lc.channelState.ChanType, true, whoseCommitChain, feePerKw, htlc.Amount.ToSatoshis(), dustLimit, @@ -7855,13 +7888,13 @@ func (lc *LightningChannel) availableBalance( } // availableCommitmentBalance attempts to calculate the balance we have -// available for HTLCs on the local/remote commitment given the htlcView. To +// available for HTLCs on the local/remote commitment given the HtlcView. To // account for sending HTLCs of different sizes, it will report the balance // available for sending non-dust HTLCs, which will be manifested on the // commitment, increasing the commitment fee we must pay as an initiator, // eating into our balance. It will make sure we won't violate the channel // reserve constraints for this amount. -func (lc *LightningChannel) availableCommitmentBalance(view *htlcView, +func (lc *LightningChannel) availableCommitmentBalance(view *HtlcView, whoseCommitChain lntypes.ChannelParty, buffer BufferType) ( lnwire.MilliSatoshi, lntypes.WeightUnit) { @@ -7891,7 +7924,7 @@ func (lc *LightningChannel) availableCommitmentBalance(view *htlcView, // Calculate the commitment fee in the case where we would add another // HTLC to the commitment, as only the balance remaining after this fee // has been paid is actually available for sending. - feePerKw := filteredView.feePerKw + feePerKw := filteredView.FeePerKw additionalHtlcFee := lnwire.NewMSatFromSatoshis( feePerKw.FeeForWeight(input.HTLCWeight), ) diff --git a/lnwallet/channel_test.go b/lnwallet/channel_test.go index fc1b087898..92e37fc993 100644 --- a/lnwallet/channel_test.go +++ b/lnwallet/channel_test.go @@ -8639,10 +8639,10 @@ func TestEvaluateView(t *testing.T) { } } - view := &htlcView{ - ourUpdates: test.ourHtlcs, - theirUpdates: test.theirHtlcs, - feePerKw: feePerKw, + view := &HtlcView{ + OurUpdates: test.ourHtlcs, + TheirUpdates: test.theirHtlcs, + FeePerKw: feePerKw, } var ( @@ -8663,17 +8663,17 @@ func TestEvaluateView(t *testing.T) { t.Fatalf("unexpected error: %v", err) } - if result.feePerKw != test.expectedFee { + if result.FeePerKw != test.expectedFee { t.Fatalf("expected fee: %v, got: %v", - test.expectedFee, result.feePerKw) + test.expectedFee, result.FeePerKw) } checkExpectedHtlcs( - t, result.ourUpdates, test.ourExpectedHtlcs, + t, result.OurUpdates, test.ourExpectedHtlcs, ) checkExpectedHtlcs( - t, result.theirUpdates, test.theirExpectedHtlcs, + t, result.TheirUpdates, test.theirExpectedHtlcs, ) if lc.channelState.TotalMSatSent != test.expectSent { @@ -8896,15 +8896,15 @@ func TestProcessFeeUpdate(t *testing.T) { EntryType: FeeUpdate, } - view := &htlcView{ - feePerKw: chainfee.SatPerKWeight(feePerKw), + view := &HtlcView{ + FeePerKw: chainfee.SatPerKWeight(feePerKw), } processFeeUpdate( update, nextHeight, test.whoseCommitChain, test.mutate, view, ) - if view.feePerKw != test.expectedFee { + if view.FeePerKw != test.expectedFee { t.Fatalf("expected fee: %v, got: %v", test.expectedFee, feePerKw) } diff --git a/lnwallet/commitment.go b/lnwallet/commitment.go index 5af48eefac..2a127e25c9 100644 --- a/lnwallet/commitment.go +++ b/lnwallet/commitment.go @@ -685,7 +685,7 @@ type unsignedCommitmentTx struct { func (cb *CommitmentBuilder) createUnsignedCommitmentTx(ourBalance, theirBalance lnwire.MilliSatoshi, whoseCommit lntypes.ChannelParty, feePerKw chainfee.SatPerKWeight, height uint64, - filteredHTLCView *htlcView, + filteredHTLCView *HtlcView, keyRing *CommitmentKeyRing) (*unsignedCommitmentTx, error) { dustLimit := cb.chanState.LocalChanCfg.DustLimit @@ -694,7 +694,7 @@ func (cb *CommitmentBuilder) createUnsignedCommitmentTx(ourBalance, } numHTLCs := int64(0) - for _, htlc := range filteredHTLCView.ourUpdates { + for _, htlc := range filteredHTLCView.OurUpdates { if HtlcIsDust( cb.chanState.ChanType, false, whoseCommit, feePerKw, htlc.Amount.ToSatoshis(), dustLimit, @@ -705,7 +705,7 @@ func (cb *CommitmentBuilder) createUnsignedCommitmentTx(ourBalance, numHTLCs++ } - for _, htlc := range filteredHTLCView.theirUpdates { + for _, htlc := range filteredHTLCView.TheirUpdates { if HtlcIsDust( cb.chanState.ChanType, true, whoseCommit, feePerKw, htlc.Amount.ToSatoshis(), dustLimit, @@ -789,7 +789,7 @@ func (cb *CommitmentBuilder) createUnsignedCommitmentTx(ourBalance, // commitment outputs and should correspond to zero values for the // purposes of sorting. cltvs := make([]uint32, len(commitTx.TxOut)) - for _, htlc := range filteredHTLCView.ourUpdates { + for _, htlc := range filteredHTLCView.OurUpdates { if HtlcIsDust( cb.chanState.ChanType, false, whoseCommit, feePerKw, htlc.Amount.ToSatoshis(), dustLimit, @@ -807,7 +807,7 @@ func (cb *CommitmentBuilder) createUnsignedCommitmentTx(ourBalance, } cltvs = append(cltvs, htlc.Timeout) // nolint:makezero } - for _, htlc := range filteredHTLCView.theirUpdates { + for _, htlc := range filteredHTLCView.TheirUpdates { if HtlcIsDust( cb.chanState.ChanType, true, whoseCommit, feePerKw, htlc.Amount.ToSatoshis(), dustLimit, From ae1b121cdd958324bc86b511a6bc77afad322670 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Mon, 1 Apr 2024 20:07:15 -0700 Subject: [PATCH 035/218] lnwallet: add custom tlv blob to internal commitment struct In this commit, we also add the custom TLV blob to the internal commitment struct that we use within the in-memory commitment linked list. This'll be useful to ensure that we're tracking the current blob for our in memory commitment for when we need to write it to disk. --- lnwallet/channel.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/lnwallet/channel.go b/lnwallet/channel.go index 30ae20073f..37f8f6cd4e 100644 --- a/lnwallet/channel.go +++ b/lnwallet/channel.go @@ -33,6 +33,7 @@ import ( "github.com/lightningnetwork/lnd/lnwallet/chainfee" "github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/shachain" + "github.com/lightningnetwork/lnd/tlv" ) var ( @@ -333,6 +334,10 @@ type commitment struct { // on this commitment transaction. incomingHTLCs []PaymentDescriptor + // customBlob stores opaque bytes that may be used by custom channels + // to store extra data for a given commitment state. + customBlob fn.Option[tlv.Blob] + // [outgoing|incoming]HTLCIndex is an index that maps an output index // on the commitment transaction to the payment descriptor that // represents the HTLC output. @@ -506,6 +511,7 @@ func (c *commitment) toDiskCommit( CommitTx: c.txn, CommitSig: c.sig, Htlcs: make([]channeldb.HTLC, 0, numHtlcs), + CustomBlob: c.customBlob, } for _, htlc := range c.outgoingHTLCs { @@ -753,6 +759,7 @@ func (lc *LightningChannel) diskCommitToMemCommit( feePerKw: chainfee.SatPerKWeight(diskCommit.FeePerKw), incomingHTLCs: incomingHtlcs, outgoingHTLCs: outgoingHtlcs, + customBlob: diskCommit.CustomBlob, } if whoseCommit.IsLocal() { commit.dustLimit = lc.channelState.LocalChanCfg.DustLimit From 9db810d67a5f159324a226e38fb56c61452e54bb Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Mon, 1 Apr 2024 19:52:21 -0700 Subject: [PATCH 036/218] input: add some utility type definitions for aux leaves In this commit, we add some useful type definitions for the aux leaf. --- input/taproot.go | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/input/taproot.go b/input/taproot.go index 0fcd1df4e2..2ca6e97236 100644 --- a/input/taproot.go +++ b/input/taproot.go @@ -31,6 +31,24 @@ func NoneTapLeaf() AuxTapLeaf { return fn.None[txscript.TapLeaf]() } +// HtlcIndex represents the monotonically increasing counter that is used to +// identify HTLCs created by a peer. +type HtlcIndex = uint64 + +// HtlcAuxLeaf is a type that represents an auxiliary leaf for an HTLC output. +// An HTLC may have up to two aux leaves: one for the output on the commitment +// transaction, and one for the second level HTLC. +type HtlcAuxLeaf struct { + AuxTapLeaf + + // SecondLevelLeaf is the auxiliary leaf for the second level HTLC + // success or timeout transaction. + SecondLevelLeaf AuxTapLeaf +} + +// HtlcAuxLeaves is a type alias for a map of optional tapscript leaves. +type HtlcAuxLeaves = map[HtlcIndex]HtlcAuxLeaf + // NewTxSigHashesV0Only returns a new txscript.TxSigHashes instance that will // only calculate the sighash midstate values for segwit v0 inputs and can // therefore never be used for transactions that want to spend segwit v1 From ef56d8654ebd744cfe5c29a9b0bc062c73ea8e66 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Sun, 17 Mar 2024 16:53:38 -0400 Subject: [PATCH 037/218] lnwallet+channeldb: add new AuxLeafStore for dynamic aux leaves In this commit, we add a new AuxLeafStore which can be used to dynamically fetch the latest aux leaves for a given state. This is useful for custom channel types that will store some extra information in the form of a custom blob, then will use that information to derive the new leaf tapscript leaves that may be attached to reach state. --- contractcourt/chain_watcher.go | 3 +- lnwallet/aux_leaf_store.go | 239 +++++++++++++++++++++++++++++++++ lnwallet/channel.go | 66 +++++++-- lnwallet/commitment.go | 138 ++++++++++++++----- lnwallet/transactions_test.go | 2 + lnwallet/wallet.go | 27 +++- 6 files changed, 430 insertions(+), 45 deletions(-) create mode 100644 lnwallet/aux_leaf_store.go diff --git a/contractcourt/chain_watcher.go b/contractcourt/chain_watcher.go index 8b2f758167..66a61fe3b2 100644 --- a/contractcourt/chain_watcher.go +++ b/contractcourt/chain_watcher.go @@ -431,7 +431,7 @@ func (c *chainWatcher) handleUnknownLocalState( } remoteScript, _, err := lnwallet.CommitScriptToRemote( c.cfg.chanState.ChanType, c.cfg.chanState.IsInitiator, - commitKeyRing.ToRemoteKey, leaseExpiry, + commitKeyRing.ToRemoteKey, leaseExpiry, input.NoneTapLeaf(), ) if err != nil { return false, err @@ -444,6 +444,7 @@ func (c *chainWatcher) handleUnknownLocalState( c.cfg.chanState.ChanType, c.cfg.chanState.IsInitiator, commitKeyRing.ToLocalKey, commitKeyRing.RevocationKey, uint32(c.cfg.chanState.LocalChanCfg.CsvDelay), leaseExpiry, + input.NoneTapLeaf(), ) if err != nil { return false, err diff --git a/lnwallet/aux_leaf_store.go b/lnwallet/aux_leaf_store.go new file mode 100644 index 0000000000..4558c2f81c --- /dev/null +++ b/lnwallet/aux_leaf_store.go @@ -0,0 +1,239 @@ +package lnwallet + +import ( + "github.com/btcsuite/btcd/btcutil" + "github.com/btcsuite/btcd/chaincfg/chainhash" + "github.com/btcsuite/btcd/wire" + "github.com/lightningnetwork/lnd/channeldb" + "github.com/lightningnetwork/lnd/fn" + "github.com/lightningnetwork/lnd/input" + "github.com/lightningnetwork/lnd/lntypes" + "github.com/lightningnetwork/lnd/lnwire" + "github.com/lightningnetwork/lnd/tlv" +) + +// CommitSortFunc is a function type alias for a function that sorts the +// commitment transaction outputs. The second parameter is a list of CLTV +// timeouts that must correspond to the number of transaction outputs, with the +// value of 0 for non-HTLC outputs. The HTLC indexes are needed to have a +// deterministic sort value for HTLCs that have the identical amount, CLTV +// timeout and payment hash (e.g. multiple MPP shards of the same payment, where +// the on-chain script would be identical). +type CommitSortFunc func(tx *wire.MsgTx, cltvs []uint32, + indexes []input.HtlcIndex) error + +// DefaultCommitSort is the default commitment sort function that sorts the +// commitment transaction inputs and outputs according to BIP69. The second +// parameter is a list of CLTV timeouts that must correspond to the number of +// transaction outputs, with the value of 0 for non-HTLC outputs. The third +// parameter is unused for the default sort function. +func DefaultCommitSort(tx *wire.MsgTx, cltvs []uint32, + _ []input.HtlcIndex) error { + + InPlaceCommitSort(tx, cltvs) + return nil +} + +// CommitAuxLeaves stores two potential auxiliary leaves for the remote and +// local output that may be used to augment the final tapscript trees of the +// commitment transaction. +type CommitAuxLeaves struct { + // LocalAuxLeaf is the local party's auxiliary leaf. + LocalAuxLeaf input.AuxTapLeaf + + // RemoteAuxLeaf is the remote party's auxiliary leaf. + RemoteAuxLeaf input.AuxTapLeaf + + // OutgoingHTLCLeaves is the set of aux leaves for the outgoing HTLCs + // on this commitment transaction. + OutgoingHtlcLeaves input.HtlcAuxLeaves + + // IncomingHTLCLeaves is the set of aux leaves for the incoming HTLCs + // on this commitment transaction. + IncomingHtlcLeaves input.HtlcAuxLeaves +} + +// AuxChanState is a struct that holds certain fields of the +// channeldb.OpenChannel struct that are used by the aux components. The data +// is copied over to prevent accidental mutation of the original channel state. +type AuxChanState struct { + // ChanType denotes which type of channel this is. + ChanType channeldb.ChannelType + + // FundingOutpoint is the outpoint of the final funding transaction. + // This value uniquely and globally identifies the channel within the + // target blockchain as specified by the chain hash parameter. + FundingOutpoint wire.OutPoint + + // ShortChannelID encodes the exact location in the chain in which the + // channel was initially confirmed. This includes: the block height, + // transaction index, and the output within the target transaction. + // + // If IsZeroConf(), then this will the "base" (very first) ALIAS scid + // and the confirmed SCID will be stored in ConfirmedScid. + ShortChannelID lnwire.ShortChannelID + + // IsInitiator is a bool which indicates if we were the original + // initiator for the channel. This value may affect how higher levels + // negotiate fees, or close the channel. + IsInitiator bool + + // Capacity is the total capacity of this channel. + Capacity btcutil.Amount + + // LocalChanCfg is the channel configuration for the local node. + LocalChanCfg channeldb.ChannelConfig + + // RemoteChanCfg is the channel configuration for the remote node. + RemoteChanCfg channeldb.ChannelConfig + + // ThawHeight is the height when a frozen channel once again becomes a + // normal channel. If this is zero, then there're no restrictions on + // this channel. If the value is lower than 500,000, then it's + // interpreted as a relative height, or an absolute height otherwise. + ThawHeight uint32 + + // TapscriptRoot is an optional tapscript root used to derive the MuSig2 + // funding output. + TapscriptRoot fn.Option[chainhash.Hash] + + // CustomBlob is an optional blob that can be used to store information + // specific to a custom channel type. This information is only created + // at channel funding time, and after wards is to be considered + // immutable. + CustomBlob fn.Option[tlv.Blob] +} + +// NewAuxChanState creates a new AuxChanState from the given channel state. +func NewAuxChanState(chanState *channeldb.OpenChannel) AuxChanState { + return AuxChanState{ + ChanType: chanState.ChanType, + FundingOutpoint: chanState.FundingOutpoint, + ShortChannelID: chanState.ShortChannelID, + IsInitiator: chanState.IsInitiator, + Capacity: chanState.Capacity, + LocalChanCfg: chanState.LocalChanCfg, + RemoteChanCfg: chanState.RemoteChanCfg, + ThawHeight: chanState.ThawHeight, + TapscriptRoot: chanState.TapscriptRoot, + CustomBlob: chanState.CustomBlob, + } +} + +// CommitDiffAuxInput is the input required to compute the diff of the auxiliary +// leaves for a commitment transaction. +type CommitDiffAuxInput struct { + // ChannelState is the static channel information of the channel this + // commitment transaction relates to. + ChannelState AuxChanState + + // PrevBlob is the blob of the previous commitment transaction. + PrevBlob tlv.Blob + + // UnfilteredView is the unfiltered, original HTLC view of the channel. + // Unfiltered in this context means that the view contains all HTLCs, + // including the canceled ones. + UnfilteredView *HtlcView + + // WhoseCommit denotes whose commitment transaction we are computing the + // diff for. + WhoseCommit lntypes.ChannelParty + + // OurBalance is the balance of the local party. + OurBalance lnwire.MilliSatoshi + + // TheirBalance is the balance of the remote party. + TheirBalance lnwire.MilliSatoshi + + // KeyRing is the key ring that can be used to derive keys for the + // commitment transaction. + KeyRing CommitmentKeyRing +} + +// CommitDiffAuxResult is the result of computing the diff of the auxiliary +// leaves for a commitment transaction. +type CommitDiffAuxResult struct { + // AuxLeaves are the auxiliary leaves for the new commitment + // transaction. + AuxLeaves fn.Option[CommitAuxLeaves] + + // CommitSortFunc is an optional function that sorts the commitment + // transaction inputs and outputs. + CommitSortFunc fn.Option[CommitSortFunc] +} + +// AuxLeafStore is used to optionally fetch auxiliary tapscript leaves for the +// commitment transaction given an opaque blob. This is also used to implement +// a state transition function for the blobs to allow them to be refreshed with +// each state. +type AuxLeafStore interface { + // FetchLeavesFromView attempts to fetch the auxiliary leaves that + // correspond to the passed aux blob, and pending original (unfiltered) + // HTLC view. + FetchLeavesFromView( + in CommitDiffAuxInput) fn.Result[CommitDiffAuxResult] + + // FetchLeavesFromCommit attempts to fetch the auxiliary leaves that + // correspond to the passed aux blob, and an existing channel + // commitment. + FetchLeavesFromCommit(chanState AuxChanState, + commit channeldb.ChannelCommitment, + keyRing CommitmentKeyRing) fn.Result[CommitDiffAuxResult] + + // FetchLeavesFromRevocation attempts to fetch the auxiliary leaves + // from a channel revocation that stores balance + blob information. + FetchLeavesFromRevocation( + r *channeldb.RevocationLog) fn.Result[CommitDiffAuxResult] + + // ApplyHtlcView serves as the state transition function for the custom + // channel's blob. Given the old blob, and an HTLC view, then a new + // blob should be returned that reflects the pending updates. + ApplyHtlcView(in CommitDiffAuxInput) fn.Result[fn.Option[tlv.Blob]] +} + +// auxLeavesFromView is used to derive the set of commit aux leaves (if any), +// that are needed to create a new commitment transaction using the original +// (unfiltered) htlc view. +func auxLeavesFromView(leafStore AuxLeafStore, chanState *channeldb.OpenChannel, + prevBlob fn.Option[tlv.Blob], originalView *HtlcView, + whoseCommit lntypes.ChannelParty, ourBalance, + theirBalance lnwire.MilliSatoshi, + keyRing CommitmentKeyRing) fn.Result[CommitDiffAuxResult] { + + return fn.MapOptionZ( + prevBlob, func(blob tlv.Blob) fn.Result[CommitDiffAuxResult] { + return leafStore.FetchLeavesFromView(CommitDiffAuxInput{ + ChannelState: NewAuxChanState(chanState), + PrevBlob: blob, + UnfilteredView: originalView, + WhoseCommit: whoseCommit, + OurBalance: ourBalance, + TheirBalance: theirBalance, + KeyRing: keyRing, + }) + }, + ) +} + +// updateAuxBlob is a helper function that attempts to update the aux blob +// given the prior and current state information. +func updateAuxBlob(leafStore AuxLeafStore, chanState *channeldb.OpenChannel, + prevBlob fn.Option[tlv.Blob], nextViewUnfiltered *HtlcView, + whoseCommit lntypes.ChannelParty, ourBalance, + theirBalance lnwire.MilliSatoshi, + keyRing CommitmentKeyRing) fn.Result[fn.Option[tlv.Blob]] { + + return fn.MapOptionZ( + prevBlob, func(blob tlv.Blob) fn.Result[fn.Option[tlv.Blob]] { + return leafStore.ApplyHtlcView(CommitDiffAuxInput{ + ChannelState: NewAuxChanState(chanState), + PrevBlob: blob, + UnfilteredView: nextViewUnfiltered, + WhoseCommit: whoseCommit, + OurBalance: ourBalance, + TheirBalance: theirBalance, + KeyRing: keyRing, + }) + }, + ) +} diff --git a/lnwallet/channel.go b/lnwallet/channel.go index 37f8f6cd4e..079587a87f 100644 --- a/lnwallet/channel.go +++ b/lnwallet/channel.go @@ -804,6 +804,10 @@ type LightningChannel struct { // machine. Signer input.Signer + // leafStore is used to retrieve extra tapscript leaves for special + // custom channel types. + leafStore fn.Option[AuxLeafStore] + // signDesc is the primary sign descriptor that is capable of signing // the commitment transaction that spends the multi-sig output. signDesc *input.SignDescriptor @@ -879,6 +883,8 @@ type channelOpts struct { localNonce *musig2.Nonces remoteNonce *musig2.Nonces + leafStore fn.Option[AuxLeafStore] + skipNonceInit bool } @@ -909,6 +915,13 @@ func WithSkipNonceInit() ChannelOpt { } } +// WithLeafStore is used to specify a custom leaf store for the channel. +func WithLeafStore(store AuxLeafStore) ChannelOpt { + return func(o *channelOpts) { + o.leafStore = fn.Some[AuxLeafStore](store) + } +} + // defaultChannelOpts returns the set of default options for a new channel. func defaultChannelOpts() *channelOpts { return &channelOpts{} @@ -950,13 +963,16 @@ func NewLightningChannel(signer input.Signer, } lc := &LightningChannel{ - Signer: signer, - sigPool: sigPool, - currentHeight: localCommit.CommitHeight, - remoteCommitChain: newCommitmentChain(), - localCommitChain: newCommitmentChain(), - channelState: state, - commitBuilder: NewCommitmentBuilder(state), + Signer: signer, + leafStore: opts.leafStore, + sigPool: sigPool, + currentHeight: localCommit.CommitHeight, + remoteCommitChain: newCommitmentChain(), + localCommitChain: newCommitmentChain(), + channelState: state, + commitBuilder: NewCommitmentBuilder( + state, opts.leafStore, + ), localUpdateLog: localUpdateLog, remoteUpdateLog: remoteUpdateLog, Capacity: state.Capacity, @@ -1988,12 +2004,14 @@ func NewBreachRetribution(chanState *channeldb.OpenChannel, stateNum uint64, leaseExpiry = chanState.ThawHeight } + // TODO(roasbeef): Actually fetch aux leaves (later commits in this PR). + // Since it is the remote breach we are reconstructing, the output // going to us will be a to-remote script with our local params. isRemoteInitiator := !chanState.IsInitiator ourScript, ourDelay, err := CommitScriptToRemote( chanState.ChanType, isRemoteInitiator, keyRing.ToRemoteKey, - leaseExpiry, + leaseExpiry, input.NoneTapLeaf(), ) if err != nil { return nil, err @@ -2003,6 +2021,7 @@ func NewBreachRetribution(chanState *channeldb.OpenChannel, stateNum uint64, theirScript, err := CommitScriptToSelf( chanState.ChanType, isRemoteInitiator, keyRing.ToLocalKey, keyRing.RevocationKey, theirDelay, leaseExpiry, + input.NoneTapLeaf(), ) if err != nil { return nil, err @@ -2560,7 +2579,8 @@ func (lc *LightningChannel) fetchCommitmentView( // Actually generate unsigned commitment transaction for this view. commitTx, err := lc.commitBuilder.createUnsignedCommitmentTx( ourBalance, theirBalance, whoseCommitChain, feePerKw, - nextHeight, filteredHTLCView, keyRing, + nextHeight, htlcView, filteredHTLCView, keyRing, + commitChain.tip(), ) if err != nil { return nil, err @@ -2595,6 +2615,23 @@ func (lc *LightningChannel) fetchCommitmentView( effFeeRate, spew.Sdump(commitTx)) } + // Given the custom blob of the past state, and this new HTLC view, + // we'll generate a new blob for the latest commitment. + newCommitBlob, err := fn.MapOptionZ( + lc.leafStore, + func(s AuxLeafStore) fn.Result[fn.Option[tlv.Blob]] { + return updateAuxBlob( + s, lc.channelState, + commitChain.tip().customBlob, htlcView, + whoseCommitChain, ourBalance, theirBalance, + *keyRing, + ) + }, + ).Unpack() + if err != nil { + return nil, fmt.Errorf("unable to fetch aux leaves: %w", err) + } + // With the commitment view created, store the resulting balances and // transaction with the other parameters for this height. c := &commitment{ @@ -2610,6 +2647,7 @@ func (lc *LightningChannel) fetchCommitmentView( feePerKw: feePerKw, dustLimit: dustLimit, whoseCommit: whoseCommitChain, + customBlob: newCommitBlob, } // In order to ensure _none_ of the HTLC's associated with this new @@ -2703,6 +2741,7 @@ func (lc *LightningChannel) evaluateHTLCView(view *HtlcView, ourBalance, if mutateState && entry.EntryType == Settle && whoseCommitChain.IsLocal() && entry.removeCommitHeightLocal == 0 { + lc.channelState.TotalMSatReceived += entry.Amount } @@ -5430,7 +5469,7 @@ func (lc *LightningChannel) ReceiveRevocation(revMsg *lnwire.RevokeAndAck) ( // before the change since the indexes are meant for the current, // revoked remote commitment. ourOutputIndex, theirOutputIndex, err := findOutputIndexesFromRemote( - revocation, lc.channelState, + revocation, lc.channelState, lc.leafStore, ) if err != nil { return nil, nil, nil, nil, err @@ -6318,12 +6357,14 @@ func NewUnilateralCloseSummary(chanState *channeldb.OpenChannel, signer input.Si commitTxBroadcast := commitSpend.SpendingTx + // TODO(roasbeef): Actually fetch aux leaves (later commits in this PR). + // Before we can generate the proper sign descriptor, we'll need to // locate the output index of our non-delayed output on the commitment // transaction. selfScript, maturityDelay, err := CommitScriptToRemote( chanState.ChanType, isRemoteInitiator, keyRing.ToRemoteKey, - leaseExpiry, + leaseExpiry, input.NoneTapLeaf(), ) if err != nil { return nil, fmt.Errorf("unable to create self commit "+ @@ -7268,6 +7309,8 @@ func NewLocalForceCloseSummary(chanState *channeldb.OpenChannel, &chanState.LocalChanCfg, &chanState.RemoteChanCfg, ) + // TODO(roasbeef): Actually fetch aux leaves (later commits in this PR). + var leaseExpiry uint32 if chanState.ChanType.HasLeaseExpiration() { leaseExpiry = chanState.ThawHeight @@ -7275,6 +7318,7 @@ func NewLocalForceCloseSummary(chanState *channeldb.OpenChannel, toLocalScript, err := CommitScriptToSelf( chanState.ChanType, chanState.IsInitiator, keyRing.ToLocalKey, keyRing.RevocationKey, csvTimeout, leaseExpiry, + input.NoneTapLeaf(), ) if err != nil { return nil, err diff --git a/lnwallet/commitment.go b/lnwallet/commitment.go index 2a127e25c9..7f2a9ce33c 100644 --- a/lnwallet/commitment.go +++ b/lnwallet/commitment.go @@ -11,6 +11,7 @@ import ( "github.com/btcsuite/btcd/txscript" "github.com/btcsuite/btcd/wire" "github.com/lightningnetwork/lnd/channeldb" + "github.com/lightningnetwork/lnd/fn" "github.com/lightningnetwork/lnd/input" "github.com/lightningnetwork/lnd/lntypes" "github.com/lightningnetwork/lnd/lnwallet/chainfee" @@ -225,8 +226,8 @@ func (w *WitnessScriptDesc) WitnessScriptForPath( // party learns of the preimage to the revocation hash, then they can claim all // the settled funds in the channel, plus the unsettled funds. func CommitScriptToSelf(chanType channeldb.ChannelType, initiator bool, - selfKey, revokeKey *btcec.PublicKey, csvDelay, - leaseExpiry uint32) (input.ScriptDescriptor, error) { + selfKey, revokeKey *btcec.PublicKey, csvDelay, leaseExpiry uint32, + auxLeaf input.AuxTapLeaf) (input.ScriptDescriptor, error) { switch { // For taproot scripts, we'll need to make a slightly modified script @@ -236,7 +237,7 @@ func CommitScriptToSelf(chanType channeldb.ChannelType, initiator bool, // Our "redeem" script here is just the taproot witness program. case chanType.IsTaproot(): return input.NewLocalCommitScriptTree( - csvDelay, selfKey, revokeKey, input.NoneTapLeaf(), + csvDelay, selfKey, revokeKey, auxLeaf, ) // If we are the initiator of a leased channel, then we have an @@ -290,8 +291,8 @@ func CommitScriptToSelf(chanType channeldb.ChannelType, initiator bool, // script for. The second return value is the CSV delay of the output script, // what must be satisfied in order to spend the output. func CommitScriptToRemote(chanType channeldb.ChannelType, initiator bool, - remoteKey *btcec.PublicKey, - leaseExpiry uint32) (input.ScriptDescriptor, uint32, error) { + remoteKey *btcec.PublicKey, leaseExpiry uint32, + auxLeaf input.AuxTapLeaf) (input.ScriptDescriptor, uint32, error) { switch { // If we are not the initiator of a leased channel, then the remote @@ -320,7 +321,7 @@ func CommitScriptToRemote(chanType channeldb.ChannelType, initiator bool, // with the sole tap leaf enforcing the 1 CSV delay. case chanType.IsTaproot(): toRemoteScriptTree, err := input.NewRemoteCommitScriptTree( - remoteKey, input.NoneTapLeaf(), + remoteKey, auxLeaf, ) if err != nil { return nil, 0, err @@ -612,7 +613,7 @@ func CommitScriptAnchors(chanType channeldb.ChannelType, // with, and abstracts the various ways of constructing commitment // transactions. type CommitmentBuilder struct { - // chanState is the underlying channels's state struct, used to + // chanState is the underlying channel's state struct, used to // determine the type of channel we are dealing with, and relevant // parameters. chanState *channeldb.OpenChannel @@ -620,18 +621,25 @@ type CommitmentBuilder struct { // obfuscator is a 48-bit state hint that's used to obfuscate the // current state number on the commitment transactions. obfuscator [StateHintSize]byte + + // auxLeafStore is an interface that allows us to fetch auxiliary + // tapscript leaves for the commitment output. + auxLeafStore fn.Option[AuxLeafStore] } // NewCommitmentBuilder creates a new CommitmentBuilder from chanState. -func NewCommitmentBuilder(chanState *channeldb.OpenChannel) *CommitmentBuilder { +func NewCommitmentBuilder(chanState *channeldb.OpenChannel, + leafStore fn.Option[AuxLeafStore]) *CommitmentBuilder { + // The anchor channel type MUST be tweakless. if chanState.ChanType.HasAnchors() && !chanState.ChanType.IsTweakless() { panic("invalid channel type combination") } return &CommitmentBuilder{ - chanState: chanState, - obfuscator: createStateHintObfuscator(chanState), + chanState: chanState, + obfuscator: createStateHintObfuscator(chanState), + auxLeafStore: leafStore, } } @@ -684,9 +692,9 @@ type unsignedCommitmentTx struct { // fees, but after anchor outputs. func (cb *CommitmentBuilder) createUnsignedCommitmentTx(ourBalance, theirBalance lnwire.MilliSatoshi, whoseCommit lntypes.ChannelParty, - feePerKw chainfee.SatPerKWeight, height uint64, - filteredHTLCView *HtlcView, - keyRing *CommitmentKeyRing) (*unsignedCommitmentTx, error) { + feePerKw chainfee.SatPerKWeight, height uint64, originalHtlcView, + filteredHTLCView *HtlcView, keyRing *CommitmentKeyRing, + prevCommit *commitment) (*unsignedCommitmentTx, error) { dustLimit := cb.chanState.LocalChanCfg.DustLimit if whoseCommit.IsRemote() { @@ -752,6 +760,23 @@ func (cb *CommitmentBuilder) createUnsignedCommitmentTx(ourBalance, err error ) + // Before we create the commitment transaction below, we'll try to see + // if there're any aux leaves that need to be a part of the tapscript + // tree. We'll only do this if we have a custom blob defined though. + auxResult, err := fn.MapOptionZ( + cb.auxLeafStore, + func(s AuxLeafStore) fn.Result[CommitDiffAuxResult] { + return auxLeavesFromView( + s, cb.chanState, prevCommit.customBlob, + originalHtlcView, whoseCommit, ourBalance, + theirBalance, *keyRing, + ) + }, + ).Unpack() + if err != nil { + return nil, fmt.Errorf("unable to fetch aux leaves: %w", err) + } + // Depending on whether the transaction is ours or not, we call // CreateCommitTx with parameters matching the perspective, to generate // a new commitment transaction with all the latest unsettled/un-timed @@ -766,6 +791,7 @@ func (cb *CommitmentBuilder) createUnsignedCommitmentTx(ourBalance, &cb.chanState.LocalChanCfg, &cb.chanState.RemoteChanCfg, ourBalance.ToSatoshis(), theirBalance.ToSatoshis(), numHTLCs, cb.chanState.IsInitiator, leaseExpiry, + auxResult.AuxLeaves, ) } else { commitTx, err = CreateCommitTx( @@ -773,6 +799,7 @@ func (cb *CommitmentBuilder) createUnsignedCommitmentTx(ourBalance, &cb.chanState.RemoteChanCfg, &cb.chanState.LocalChanCfg, theirBalance.ToSatoshis(), ourBalance.ToSatoshis(), numHTLCs, !cb.chanState.IsInitiator, leaseExpiry, + auxResult.AuxLeaves, ) } if err != nil { @@ -789,6 +816,7 @@ func (cb *CommitmentBuilder) createUnsignedCommitmentTx(ourBalance, // commitment outputs and should correspond to zero values for the // purposes of sorting. cltvs := make([]uint32, len(commitTx.TxOut)) + htlcIndexes := make([]input.HtlcIndex, len(commitTx.TxOut)) for _, htlc := range filteredHTLCView.OurUpdates { if HtlcIsDust( cb.chanState.ChanType, false, whoseCommit, feePerKw, @@ -805,7 +833,11 @@ func (cb *CommitmentBuilder) createUnsignedCommitmentTx(ourBalance, if err != nil { return nil, err } - cltvs = append(cltvs, htlc.Timeout) // nolint:makezero + + // We want to add the CLTV and HTLC index to their respective + // slices, even if we already pre-allocated them. + cltvs = append(cltvs, htlc.Timeout) //nolint + htlcIndexes = append(htlcIndexes, htlc.HtlcIndex) //nolint } for _, htlc := range filteredHTLCView.TheirUpdates { if HtlcIsDust( @@ -823,7 +855,11 @@ func (cb *CommitmentBuilder) createUnsignedCommitmentTx(ourBalance, if err != nil { return nil, err } - cltvs = append(cltvs, htlc.Timeout) // nolint:makezero + + // We want to add the CLTV and HTLC index to their respective + // slices, even if we already pre-allocated them. + cltvs = append(cltvs, htlc.Timeout) //nolint + htlcIndexes = append(htlcIndexes, htlc.HtlcIndex) //nolint } // Set the state hint of the commitment transaction to facilitate @@ -835,9 +871,16 @@ func (cb *CommitmentBuilder) createUnsignedCommitmentTx(ourBalance, } // Sort the transactions according to the agreed upon canonical - // ordering. This lets us skip sending the entire transaction over, - // instead we'll just send signatures. - InPlaceCommitSort(commitTx, cltvs) + // ordering (which might be customized for custom channel types, but + // deterministic and both parties will arrive at the same result). This + // lets us skip sending the entire transaction over, instead we'll just + // send signatures. + commitSort := auxResult.CommitSortFunc.UnwrapOr(DefaultCommitSort) + err = commitSort(commitTx, cltvs, htlcIndexes) + if err != nil { + return nil, fmt.Errorf("unable to sort commitment "+ + "transaction: %w", err) + } // Next, we'll ensure that we don't accidentally create a commitment // transaction which would be invalid by consensus. @@ -879,24 +922,33 @@ func CreateCommitTx(chanType channeldb.ChannelType, fundingOutput wire.TxIn, keyRing *CommitmentKeyRing, localChanCfg, remoteChanCfg *channeldb.ChannelConfig, amountToLocal, amountToRemote btcutil.Amount, - numHTLCs int64, initiator bool, leaseExpiry uint32) (*wire.MsgTx, error) { + numHTLCs int64, initiator bool, leaseExpiry uint32, + auxLeaves fn.Option[CommitAuxLeaves]) (*wire.MsgTx, error) { // First, we create the script for the delayed "pay-to-self" output. // This output has 2 main redemption clauses: either we can redeem the // output after a relative block delay, or the remote node can claim // the funds with the revocation key if we broadcast a revoked // commitment transaction. + localAuxLeaf := fn.MapOption(func(l CommitAuxLeaves) input.AuxTapLeaf { + return l.LocalAuxLeaf + })(auxLeaves) toLocalScript, err := CommitScriptToSelf( chanType, initiator, keyRing.ToLocalKey, keyRing.RevocationKey, uint32(localChanCfg.CsvDelay), leaseExpiry, + fn.FlattenOption(localAuxLeaf), ) if err != nil { return nil, err } // Next, we create the script paying to the remote. + remoteAuxLeaf := fn.MapOption(func(l CommitAuxLeaves) input.AuxTapLeaf { + return l.RemoteAuxLeaf + })(auxLeaves) toRemoteScript, _, err := CommitScriptToRemote( chanType, initiator, keyRing.ToRemoteKey, leaseExpiry, + fn.FlattenOption(remoteAuxLeaf), ) if err != nil { return nil, err @@ -1201,7 +1253,8 @@ func addHTLC(commitTx *wire.MsgTx, whoseCommit lntypes.ChannelParty, // output scripts and compares them against the outputs inside the commitment // to find the match. func findOutputIndexesFromRemote(revocationPreimage *chainhash.Hash, - chanState *channeldb.OpenChannel) (uint32, uint32, error) { + chanState *channeldb.OpenChannel, + leafStore fn.Option[AuxLeafStore]) (uint32, uint32, error) { // Init the output indexes as empty. ourIndex := uint32(channeldb.OutputIndexEmpty) @@ -1231,26 +1284,51 @@ func findOutputIndexesFromRemote(revocationPreimage *chainhash.Hash, leaseExpiry = chanState.ThawHeight } - // Map the scripts from our PoV. When facing a local commitment, the to - // local output belongs to us and the to remote output belongs to them. - // When facing a remote commitment, the to local output belongs to them - // and the to remote output belongs to us. + // If we have a custom blob, then we'll attempt to fetch the aux leaves + // for this state. + auxResult, err := fn.MapOptionZ( + leafStore, func(a AuxLeafStore) fn.Result[CommitDiffAuxResult] { + return a.FetchLeavesFromCommit( + NewAuxChanState(chanState), chanCommit, + *keyRing, + ) + }, + ).Unpack() + if err != nil { + return ourIndex, theirIndex, fmt.Errorf("unable to fetch aux "+ + "leaves: %w", err) + } - // Compute the to local script. From our PoV, when facing a remote - // commitment, the to local output belongs to them. + // Map the scripts from our PoV. When facing a local commitment, the + // to_local output belongs to us and the to_remote output belongs to + // them. When facing a remote commitment, the to_local output belongs to + // them and the to_remote output belongs to us. + + // Compute the to_local script. From our PoV, when facing a remote + // commitment, the to_local output belongs to them. + localAuxLeaf := fn.ChainOption( + func(l CommitAuxLeaves) input.AuxTapLeaf { + return l.LocalAuxLeaf + }, + )(auxResult.AuxLeaves) theirScript, err := CommitScriptToSelf( chanState.ChanType, isRemoteInitiator, keyRing.ToLocalKey, - keyRing.RevocationKey, theirDelay, leaseExpiry, + keyRing.RevocationKey, theirDelay, leaseExpiry, localAuxLeaf, ) if err != nil { return ourIndex, theirIndex, err } - // Compute the to remote script. From our PoV, when facing a remote - // commitment, the to remote output belongs to us. + // Compute the to_remote script. From our PoV, when facing a remote + // commitment, the to_remote output belongs to us. + remoteAuxLeaf := fn.ChainOption( + func(l CommitAuxLeaves) input.AuxTapLeaf { + return l.RemoteAuxLeaf + }, + )(auxResult.AuxLeaves) ourScript, _, err := CommitScriptToRemote( chanState.ChanType, isRemoteInitiator, keyRing.ToRemoteKey, - leaseExpiry, + leaseExpiry, remoteAuxLeaf, ) if err != nil { return ourIndex, theirIndex, err diff --git a/lnwallet/transactions_test.go b/lnwallet/transactions_test.go index e9751c6b9b..439e7ce955 100644 --- a/lnwallet/transactions_test.go +++ b/lnwallet/transactions_test.go @@ -21,6 +21,7 @@ import ( "github.com/btcsuite/btcd/txscript" "github.com/btcsuite/btcd/wire" "github.com/lightningnetwork/lnd/channeldb" + "github.com/lightningnetwork/lnd/fn" "github.com/lightningnetwork/lnd/input" "github.com/lightningnetwork/lnd/keychain" "github.com/lightningnetwork/lnd/lntypes" @@ -631,6 +632,7 @@ func testSpendValidation(t *testing.T, tweakless bool) { commitmentTx, err := CreateCommitTx( channelType, *fakeFundingTxIn, keyRing, aliceChanCfg, bobChanCfg, channelBalance, channelBalance, 0, true, 0, + fn.None[CommitAuxLeaves](), ) if err != nil { t.Fatalf("unable to create commitment transaction: %v", nil) diff --git a/lnwallet/wallet.go b/lnwallet/wallet.go index 0d6e85ec9a..39748e7e81 100644 --- a/lnwallet/wallet.go +++ b/lnwallet/wallet.go @@ -1470,6 +1470,21 @@ func (l *LightningWallet) handleFundingCancelRequest(req *fundingReserveCancelMs req.err <- nil } +// createCommitOpts is a struct that holds the options for creating a new +// commitment transaction. +type createCommitOpts struct { + auxLeaves fn.Option[CommitAuxLeaves] +} + +// defaultCommitOpts returns a new createCommitOpts with default values. +func defaultCommitOpts() createCommitOpts { + return createCommitOpts{} +} + +// CreateCommitOpt is a functional option that can be used to modify the way a +// new commitment transaction is created. +type CreateCommitOpt func(*createCommitOpts) + // CreateCommitmentTxns is a helper function that creates the initial // commitment transaction for both parties. This function is used during the // initial funding workflow as both sides must generate a signature for the @@ -1479,7 +1494,13 @@ func CreateCommitmentTxns(localBalance, remoteBalance btcutil.Amount, ourChanCfg, theirChanCfg *channeldb.ChannelConfig, localCommitPoint, remoteCommitPoint *btcec.PublicKey, fundingTxIn wire.TxIn, chanType channeldb.ChannelType, initiator bool, - leaseExpiry uint32) (*wire.MsgTx, *wire.MsgTx, error) { + leaseExpiry uint32, opts ...CreateCommitOpt) (*wire.MsgTx, *wire.MsgTx, + error) { + + options := defaultCommitOpts() + for _, optFunc := range opts { + optFunc(&options) + } localCommitmentKeys := DeriveCommitmentKeys( localCommitPoint, lntypes.Local, chanType, ourChanCfg, @@ -1493,7 +1514,7 @@ func CreateCommitmentTxns(localBalance, remoteBalance btcutil.Amount, ourCommitTx, err := CreateCommitTx( chanType, fundingTxIn, localCommitmentKeys, ourChanCfg, theirChanCfg, localBalance, remoteBalance, 0, initiator, - leaseExpiry, + leaseExpiry, options.auxLeaves, ) if err != nil { return nil, nil, err @@ -1507,7 +1528,7 @@ func CreateCommitmentTxns(localBalance, remoteBalance btcutil.Amount, theirCommitTx, err := CreateCommitTx( chanType, fundingTxIn, remoteCommitmentKeys, theirChanCfg, ourChanCfg, remoteBalance, localBalance, 0, !initiator, - leaseExpiry, + leaseExpiry, options.auxLeaves, ) if err != nil { return nil, nil, err From 12ab2c3b511cf932473b29a72f8ac7c88a83b7a7 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Mon, 1 Apr 2024 19:56:50 -0700 Subject: [PATCH 038/218] lnwallet: add TLV blob to PaymentDescriptor + htlc add In this commit, we add a TLV blob to the PaymentDescriptor struct. We also now thread through this value from the UpdateAddHTLC message to the PaymentDescriptor mapping, and the other way around. --- channeldb/channel.go | 24 ++++++++++++++++++++++++ channeldb/channel_test.go | 19 +++++++++++++++++++ lnwallet/channel.go | 14 +++++++++++++- lnwallet/channel_test.go | 24 ++++++++++++++++++------ lnwallet/payment_descriptor.go | 4 ++++ 5 files changed, 78 insertions(+), 7 deletions(-) diff --git a/channeldb/channel.go b/channeldb/channel.go index 9fed8e8acf..c21716a456 100644 --- a/channeldb/channel.go +++ b/channeldb/channel.go @@ -2580,6 +2580,12 @@ type HTLC struct { // HTLC. It is stored in the ExtraData field, which is used to store // a TLV stream of additional information associated with the HTLC. BlindingPoint lnwire.BlindingPointRecord + + // CustomRecords is a set of custom TLV records that are associated with + // this HTLC. These records are used to store additional information + // about the HTLC that is not part of the standard HTLC fields. This + // field is encoded within the ExtraData field. + CustomRecords lnwire.CustomRecords } // serializeExtraData encodes a TLV stream of extra data to be stored with a @@ -2598,6 +2604,11 @@ func (h *HTLC) serializeExtraData() error { records = append(records, &b) }) + records, err := h.CustomRecords.ExtendRecordProducers(records) + if err != nil { + return err + } + return h.ExtraData.PackRecords(records...) } @@ -2619,7 +2630,18 @@ func (h *HTLC) deserializeExtraData() error { if val, ok := tlvMap[h.BlindingPoint.TlvType()]; ok && val == nil { h.BlindingPoint = tlv.SomeRecordT(blindingPoint) + + // Remove the entry from the TLV map. Anything left in the map + // will be included in the custom records field. + delete(tlvMap, h.BlindingPoint.TlvType()) + } + + // Set the custom records field to the remaining TLV records. + customRecords, err := lnwire.NewCustomRecords(tlvMap) + if err != nil { + return err } + h.CustomRecords = customRecords return nil } @@ -2758,6 +2780,8 @@ func (h *HTLC) Copy() HTLC { copy(clone.Signature[:], h.Signature) copy(clone.RHash[:], h.RHash[:]) copy(clone.ExtraData, h.ExtraData) + clone.BlindingPoint = h.BlindingPoint + clone.CustomRecords = h.CustomRecords.Copy() return clone } diff --git a/channeldb/channel_test.go b/channeldb/channel_test.go index 13d77276cb..fa647f1582 100644 --- a/channeldb/channel_test.go +++ b/channeldb/channel_test.go @@ -1657,6 +1657,24 @@ func TestHTLCsExtraData(t *testing.T) { ), } + // Custom channel data htlc with a blinding point. + customDataHTLC := HTLC{ + Signature: testSig.Serialize(), + Incoming: false, + Amt: 10, + RHash: key, + RefundTimeout: 1, + OnionBlob: lnmock.MockOnion(), + BlindingPoint: tlv.SomeRecordT( + tlv.NewPrimitiveRecord[lnwire.BlindingPointTlvType]( + pubKey, + ), + ), + CustomRecords: map[uint64][]byte{ + uint64(lnwire.MinCustomRecordsTlvType + 3): {1, 2, 3}, + }, + } + testCases := []struct { name string htlcs []HTLC @@ -1678,6 +1696,7 @@ func TestHTLCsExtraData(t *testing.T) { mockHtlc, blindingPointHTLC, mockHtlc, + customDataHTLC, }, }, } diff --git a/lnwallet/channel.go b/lnwallet/channel.go index 079587a87f..3c724f5576 100644 --- a/lnwallet/channel.go +++ b/lnwallet/channel.go @@ -529,6 +529,7 @@ func (c *commitment) toDiskCommit( LogIndex: htlc.LogIndex, Incoming: false, BlindingPoint: htlc.BlindingPoint, + CustomRecords: htlc.CustomRecords.Copy(), } copy(h.OnionBlob[:], htlc.OnionBlob) @@ -554,8 +555,10 @@ func (c *commitment) toDiskCommit( LogIndex: htlc.LogIndex, Incoming: true, BlindingPoint: htlc.BlindingPoint, + CustomRecords: htlc.CustomRecords.Copy(), } copy(h.OnionBlob[:], htlc.OnionBlob) + if whoseCommit.IsLocal() && htlc.sig != nil { h.Signature = htlc.sig.Serialize() } @@ -653,6 +656,7 @@ func (lc *LightningChannel) diskHtlcToPayDesc(feeRate chainfee.SatPerKWeight, theirPkScript: theirP2WSH, theirWitnessScript: theirWitnessScript, BlindingPoint: htlc.BlindingPoint, + CustomRecords: htlc.CustomRecords.Copy(), }, nil } @@ -5759,6 +5763,9 @@ func (lc *LightningChannel) htlcAddDescriptor(htlc *lnwire.UpdateAddHTLC, OnionBlob: htlc.OnionBlob[:], OpenCircuitKey: openKey, BlindingPoint: htlc.BlindingPoint, + // TODO(guggero): Add custom records from HTLC here once we have + // the custom records in the HTLC struct (later commits in this + // PR). } } @@ -5799,7 +5806,9 @@ func (lc *LightningChannel) validateAddHtlc(pd *PaymentDescriptor, // ReceiveHTLC adds an HTLC to the state machine's remote update log. This // method should be called in response to receiving a new HTLC from the remote // party. -func (lc *LightningChannel) ReceiveHTLC(htlc *lnwire.UpdateAddHTLC) (uint64, error) { +func (lc *LightningChannel) ReceiveHTLC(htlc *lnwire.UpdateAddHTLC) (uint64, + error) { + lc.Lock() defer lc.Unlock() @@ -5817,6 +5826,9 @@ func (lc *LightningChannel) ReceiveHTLC(htlc *lnwire.UpdateAddHTLC) (uint64, err HtlcIndex: lc.remoteUpdateLog.htlcCounter, OnionBlob: htlc.OnionBlob[:], BlindingPoint: htlc.BlindingPoint, + // TODO(guggero): Add custom records from HTLC here once we have + // the custom records in the HTLC struct (later commits in this + // PR). } localACKedIndex := lc.remoteCommitChain.tail().ourMessageIndex diff --git a/lnwallet/channel_test.go b/lnwallet/channel_test.go index 92e37fc993..07c10c8023 100644 --- a/lnwallet/channel_test.go +++ b/lnwallet/channel_test.go @@ -2,6 +2,7 @@ package lnwallet import ( "bytes" + crand "crypto/rand" "crypto/sha256" "fmt" "math/rand" @@ -10527,19 +10528,26 @@ func assertPayDescMatchHTLC(t *testing.T, pd PaymentDescriptor, // the `Incoming`. func createRandomHTLC(t *testing.T, incoming bool) channeldb.HTLC { var onionBlob [lnwire.OnionPacketSize]byte - _, err := rand.Read(onionBlob[:]) + _, err := crand.Read(onionBlob[:]) require.NoError(t, err) var rHash [lntypes.HashSize]byte - _, err = rand.Read(rHash[:]) + _, err = crand.Read(rHash[:]) require.NoError(t, err) sig := make([]byte, 64) - _, err = rand.Read(sig) + _, err = crand.Read(sig) require.NoError(t, err) + randCustomData := make([]byte, 32) + _, err = crand.Read(randCustomData) + require.NoError(t, err) + + randCustomType := rand.Intn(255) + lnwire.MinCustomRecordsTlvType + blinding, err := pubkeyFromHex( - "0228f2af0abe322403480fb3ee172f7f1601e67d1da6cad40b54c4468d48236c39", //nolint:lll + "0228f2af0abe322403480fb3ee172f7f1601e67d1da6cad40b54c4468d48" + + "236c39", ) require.NoError(t, err) @@ -10554,9 +10562,13 @@ func createRandomHTLC(t *testing.T, incoming bool) channeldb.HTLC { HtlcIndex: rand.Uint64(), LogIndex: rand.Uint64(), BlindingPoint: tlv.SomeRecordT( - //nolint:lll - tlv.NewPrimitiveRecord[lnwire.BlindingPointTlvType](blinding), + tlv.NewPrimitiveRecord[lnwire.BlindingPointTlvType]( + blinding, + ), ), + CustomRecords: map[uint64][]byte{ + uint64(randCustomType): randCustomData, + }, } } diff --git a/lnwallet/payment_descriptor.go b/lnwallet/payment_descriptor.go index f0ac8b9f7e..6fe74bb6fb 100644 --- a/lnwallet/payment_descriptor.go +++ b/lnwallet/payment_descriptor.go @@ -221,4 +221,8 @@ type PaymentDescriptor struct { // blinded route (ie, not the introduction node) from update_add_htlc's // TLVs. BlindingPoint lnwire.BlindingPointRecord + + // CustomRecords also stores the set of optional custom records that + // may have been attached to a sent HTLC. + CustomRecords lnwire.CustomRecords } From 314a24689cbd25b77edd8ac931c8f8c81c419fba Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Sat, 30 Mar 2024 17:28:35 -0700 Subject: [PATCH 039/218] channeldb: convert HTLCEntry to use tlv.RecordT --- channeldb/channel_test.go | 24 ++-- channeldb/revocation_log.go | 201 +++++++++++++++---------------- channeldb/revocation_log_test.go | 100 +++++++++++---- lnwallet/channel.go | 18 +-- lnwallet/channel_test.go | 29 +++-- 5 files changed, 209 insertions(+), 163 deletions(-) diff --git a/channeldb/channel_test.go b/channeldb/channel_test.go index fa647f1582..4427579a79 100644 --- a/channeldb/channel_test.go +++ b/channeldb/channel_test.go @@ -593,15 +593,21 @@ func assertRevocationLogEntryEqual(t *testing.T, c *ChannelCommitment, require.Equal(t, len(r.HTLCEntries), len(c.Htlcs), "HTLCs len mismatch") for i, rHtlc := range r.HTLCEntries { cHtlc := c.Htlcs[i] - require.Equal(t, rHtlc.RHash, cHtlc.RHash, "RHash mismatch") - require.Equal(t, rHtlc.Amt, cHtlc.Amt.ToSatoshis(), - "Amt mismatch") - require.Equal(t, rHtlc.RefundTimeout, cHtlc.RefundTimeout, - "RefundTimeout mismatch") - require.EqualValues(t, rHtlc.OutputIndex, cHtlc.OutputIndex, - "OutputIndex mismatch") - require.Equal(t, rHtlc.Incoming, cHtlc.Incoming, - "Incoming mismatch") + require.Equal(t, rHtlc.RHash.Val[:], cHtlc.RHash[:], "RHash") + require.Equal( + t, rHtlc.Amt.Val.Int(), cHtlc.Amt.ToSatoshis(), "Amt", + ) + require.Equal( + t, rHtlc.RefundTimeout.Val, cHtlc.RefundTimeout, + "RefundTimeout", + ) + require.EqualValues( + t, rHtlc.OutputIndex.Val, cHtlc.OutputIndex, + "OutputIndex", + ) + require.Equal( + t, rHtlc.Incoming.Val, cHtlc.Incoming, "Incoming", + ) } } diff --git a/channeldb/revocation_log.go b/channeldb/revocation_log.go index f062ac0860..26e4b9644a 100644 --- a/channeldb/revocation_log.go +++ b/channeldb/revocation_log.go @@ -54,6 +54,74 @@ var ( ErrOutputIndexTooBig = errors.New("output index is over uint16") ) +// SparsePayHash is a type alias for a 32 byte array, which when serialized is +// able to save some space by not including an empty payment hash on disk. +type SparsePayHash [32]byte + +// NewSparsePayHash creates a new SparsePayHash from a 32 byte array. +func NewSparsePayHash(rHash [32]byte) SparsePayHash { + return SparsePayHash(rHash) +} + +// Record returns a tlv record for the SparsePayHash. +func (s *SparsePayHash) Record() tlv.Record { + // We use a zero for the type here, as this'll be used along with the + // RecordT type. + return tlv.MakeDynamicRecord( + 0, s, s.hashLen, + sparseHashEncoder, sparseHashDecoder, + ) +} + +// hashLen is used by MakeDynamicRecord to return the size of the RHash. +// +// NOTE: for zero hash, we return a length 0. +func (s *SparsePayHash) hashLen() uint64 { + if bytes.Equal(s[:], lntypes.ZeroHash[:]) { + return 0 + } + + return 32 +} + +// sparseHashEncoder is the customized encoder which skips encoding the empty +// hash. +func sparseHashEncoder(w io.Writer, val interface{}, buf *[8]byte) error { + v, ok := val.(*SparsePayHash) + if !ok { + return tlv.NewTypeForEncodingErr(val, "SparsePayHash") + } + + // If the value is an empty hash, we will skip encoding it. + if bytes.Equal(v[:], lntypes.ZeroHash[:]) { + return nil + } + + vArray := (*[32]byte)(v) + + return tlv.EBytes32(w, vArray, buf) +} + +// sparseHashDecoder is the customized decoder which skips decoding the empty +// hash. +func sparseHashDecoder(r io.Reader, val interface{}, buf *[8]byte, + l uint64) error { + + v, ok := val.(*SparsePayHash) + if !ok { + return tlv.NewTypeForEncodingErr(val, "SparsePayHash") + } + + // If the length is zero, we will skip encoding the empty hash. + if l == 0 { + return nil + } + + vArray := (*[32]byte)(v) + + return tlv.DBytes32(r, vArray, buf, 32) +} + // HTLCEntry specifies the minimal info needed to be stored on disk for ALL the // historical HTLCs, which is useful for constructing RevocationLog when a // breach is detected. @@ -72,116 +140,60 @@ var ( // made into tlv records without further conversion. type HTLCEntry struct { // RHash is the payment hash of the HTLC. - RHash [32]byte + RHash tlv.RecordT[tlv.TlvType0, SparsePayHash] // RefundTimeout is the absolute timeout on the HTLC that the sender // must wait before reclaiming the funds in limbo. - RefundTimeout uint32 + RefundTimeout tlv.RecordT[tlv.TlvType1, uint32] // OutputIndex is the output index for this particular HTLC output // within the commitment transaction. // // NOTE: we use uint16 instead of int32 here to save us 2 bytes, which // gives us a max number of HTLCs of 65K. - OutputIndex uint16 + OutputIndex tlv.RecordT[tlv.TlvType2, uint16] // Incoming denotes whether we're the receiver or the sender of this // HTLC. // // NOTE: this field is the memory representation of the field // incomingUint. - Incoming bool + Incoming tlv.RecordT[tlv.TlvType3, bool] // Amt is the amount of satoshis this HTLC escrows. // // NOTE: this field is the memory representation of the field amtUint. - Amt btcutil.Amount - - // amtTlv is the uint64 format of Amt. This field is created so we can - // easily make it into a tlv record and save it to disk. - // - // NOTE: we keep this field for accounting purpose only. If the disk - // space becomes an issue, we could delete this field to save us extra - // 8 bytes. - amtTlv uint64 - - // incomingTlv is the uint8 format of Incoming. This field is created - // so we can easily make it into a tlv record and save it to disk. - incomingTlv uint8 -} - -// RHashLen is used by MakeDynamicRecord to return the size of the RHash. -// -// NOTE: for zero hash, we return a length 0. -func (h *HTLCEntry) RHashLen() uint64 { - if h.RHash == lntypes.ZeroHash { - return 0 - } - return 32 -} - -// RHashEncoder is the customized encoder which skips encoding the empty hash. -func RHashEncoder(w io.Writer, val interface{}, buf *[8]byte) error { - v, ok := val.(*[32]byte) - if !ok { - return tlv.NewTypeForEncodingErr(val, "RHash") - } - - // If the value is an empty hash, we will skip encoding it. - if *v == lntypes.ZeroHash { - return nil - } - - return tlv.EBytes32(w, v, buf) -} - -// RHashDecoder is the customized decoder which skips decoding the empty hash. -func RHashDecoder(r io.Reader, val interface{}, buf *[8]byte, l uint64) error { - v, ok := val.(*[32]byte) - if !ok { - return tlv.NewTypeForEncodingErr(val, "RHash") - } - - // If the length is zero, we will skip encoding the empty hash. - if l == 0 { - return nil - } - - return tlv.DBytes32(r, v, buf, 32) + Amt tlv.RecordT[tlv.TlvType4, tlv.BigSizeT[btcutil.Amount]] } // toTlvStream converts an HTLCEntry record into a tlv representation. func (h *HTLCEntry) toTlvStream() (*tlv.Stream, error) { - const ( - // A set of tlv type definitions used to serialize htlc entries - // to the database. We define it here instead of the head of - // the file to avoid naming conflicts. - // - // NOTE: A migration should be added whenever this list - // changes. - rHashType tlv.Type = 0 - refundTimeoutType tlv.Type = 1 - outputIndexType tlv.Type = 2 - incomingType tlv.Type = 3 - amtType tlv.Type = 4 + return tlv.NewStream( + h.RHash.Record(), + h.RefundTimeout.Record(), + h.OutputIndex.Record(), + h.Incoming.Record(), + h.Amt.Record(), ) +} - return tlv.NewStream( - tlv.MakeDynamicRecord( - rHashType, &h.RHash, h.RHashLen, - RHashEncoder, RHashDecoder, +// NewHTLCEntryFromHTLC creates a new HTLCEntry from an HTLC. +func NewHTLCEntryFromHTLC(htlc HTLC) *HTLCEntry { + return &HTLCEntry{ + RHash: tlv.NewRecordT[tlv.TlvType0]( + NewSparsePayHash(htlc.RHash), ), - tlv.MakePrimitiveRecord( - refundTimeoutType, &h.RefundTimeout, + RefundTimeout: tlv.NewPrimitiveRecord[tlv.TlvType1]( + htlc.RefundTimeout, ), - tlv.MakePrimitiveRecord( - outputIndexType, &h.OutputIndex, + OutputIndex: tlv.NewPrimitiveRecord[tlv.TlvType2]( + uint16(htlc.OutputIndex), ), - tlv.MakePrimitiveRecord(incomingType, &h.incomingTlv), - // We will save 3 bytes if the amount is less or equal to - // 4,294,967,295 msat, or roughly 0.043 bitcoin. - tlv.MakeBigSizeRecord(amtType, &h.amtTlv), - ) + Incoming: tlv.NewPrimitiveRecord[tlv.TlvType3](htlc.Incoming), + Amt: tlv.NewRecordT[tlv.TlvType4]( + tlv.NewBigSizeT(htlc.Amt.ToSatoshis()), + ), + } } // RevocationLog stores the info needed to construct a breach retribution. Its @@ -265,13 +277,7 @@ func putRevocationLog(bucket kvdb.RwBucket, commit *ChannelCommitment, return ErrOutputIndexTooBig } - entry := &HTLCEntry{ - RHash: htlc.RHash, - RefundTimeout: htlc.RefundTimeout, - Incoming: htlc.Incoming, - OutputIndex: uint16(htlc.OutputIndex), - Amt: htlc.Amt.ToSatoshis(), - } + entry := NewHTLCEntryFromHTLC(htlc) rl.HTLCEntries = append(rl.HTLCEntries, entry) } @@ -351,14 +357,6 @@ func serializeRevocationLog(w io.Writer, rl *RevocationLog) error { // format. func serializeHTLCEntries(w io.Writer, htlcs []*HTLCEntry) error { for _, htlc := range htlcs { - // Patch the incomingTlv field. - if htlc.Incoming { - htlc.incomingTlv = 1 - } - - // Patch the amtTlv field. - htlc.amtTlv = uint64(htlc.Amt) - // Create the tlv stream. tlvStream, err := htlc.toTlvStream() if err != nil { @@ -447,14 +445,6 @@ func deserializeHTLCEntries(r io.Reader) ([]*HTLCEntry, error) { return nil, err } - // Patch the Incoming field. - if htlc.incomingTlv == 1 { - htlc.Incoming = true - } - - // Patch the Amt field. - htlc.Amt = btcutil.Amount(htlc.amtTlv) - // Append the entry. htlcs = append(htlcs, &htlc) } @@ -469,6 +459,7 @@ func writeTlvStream(w io.Writer, s *tlv.Stream) error { if err := s.Encode(&b); err != nil { return err } + // Write the stream's length as a varint. err := tlv.WriteVarInt(w, uint64(b.Len()), &[8]byte{}) if err != nil { diff --git a/channeldb/revocation_log_test.go b/channeldb/revocation_log_test.go index fc5303a48d..c9d2a16f70 100644 --- a/channeldb/revocation_log_test.go +++ b/channeldb/revocation_log_test.go @@ -34,12 +34,16 @@ var ( } testHTLCEntry = HTLCEntry{ - RefundTimeout: 740_000, - OutputIndex: 10, - Incoming: true, - Amt: 1000_000, - amtTlv: 1000_000, - incomingTlv: 1, + RefundTimeout: tlv.NewPrimitiveRecord[tlv.TlvType1, uint32]( + 740_000, + ), + OutputIndex: tlv.NewPrimitiveRecord[tlv.TlvType2, uint16]( + 10, + ), + Incoming: tlv.NewPrimitiveRecord[tlv.TlvType3](true), + Amt: tlv.NewRecordT[tlv.TlvType4]( + tlv.NewBigSizeT(btcutil.Amount(1_000_000)), + ), } testHTLCEntryBytes = []byte{ // Body length 23. @@ -56,6 +60,40 @@ var ( 0x4, 0x5, 0xfe, 0x0, 0xf, 0x42, 0x40, } + testHTLCEntryHash = HTLCEntry{ + RHash: tlv.NewPrimitiveRecord[tlv.TlvType0](NewSparsePayHash( + [32]byte{0x33, 0x44, 0x55}, + )), + RefundTimeout: tlv.NewPrimitiveRecord[tlv.TlvType1, uint32]( + 740_000, + ), + OutputIndex: tlv.NewPrimitiveRecord[tlv.TlvType2, uint16]( + 10, + ), + Incoming: tlv.NewPrimitiveRecord[tlv.TlvType3](true), + Amt: tlv.NewRecordT[tlv.TlvType4]( + tlv.NewBigSizeT(btcutil.Amount(1_000_000)), + ), + } + testHTLCEntryHashBytes = []byte{ + // Body length 54. + 0x36, + // Rhash tlv. + 0x0, 0x20, + 0x33, 0x44, 0x55, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + // RefundTimeout tlv. + 0x1, 0x4, 0x0, 0xb, 0x4a, 0xa0, + // OutputIndex tlv. + 0x2, 0x2, 0x0, 0xa, + // Incoming tlv. + 0x3, 0x1, 0x1, + // Amt tlv. + 0x4, 0x5, 0xfe, 0x0, 0xf, 0x42, 0x40, + } + localBalance = lnwire.MilliSatoshi(9000) remoteBalance = lnwire.MilliSatoshi(3000) @@ -68,11 +106,11 @@ var ( CommitTx: channels.TestFundingTx, CommitSig: bytes.Repeat([]byte{1}, 71), Htlcs: []HTLC{{ - RefundTimeout: testHTLCEntry.RefundTimeout, - OutputIndex: int32(testHTLCEntry.OutputIndex), - Incoming: testHTLCEntry.Incoming, + RefundTimeout: testHTLCEntry.RefundTimeout.Val, + OutputIndex: int32(testHTLCEntry.OutputIndex.Val), + Incoming: testHTLCEntry.Incoming.Val, Amt: lnwire.NewMSatFromSatoshis( - testHTLCEntry.Amt, + testHTLCEntry.Amt.Val.Int(), ), }}, } @@ -193,11 +231,6 @@ func TestSerializeHTLCEntriesEmptyRHash(t *testing.T) { // Copy the testHTLCEntry. entry := testHTLCEntry - // Set the internal fields to empty values so we can test the bytes are - // padded. - entry.incomingTlv = 0 - entry.amtTlv = 0 - // Write the tlv stream. buf := bytes.NewBuffer([]byte{}) err := serializeHTLCEntries(buf, []*HTLCEntry{&entry}) @@ -207,6 +240,21 @@ func TestSerializeHTLCEntriesEmptyRHash(t *testing.T) { require.Equal(t, testHTLCEntryBytes, buf.Bytes()) } +func TestSerializeHTLCEntriesWithRHash(t *testing.T) { + t.Parallel() + + // Copy the testHTLCEntry. + entry := testHTLCEntryHash + + // Write the tlv stream. + buf := bytes.NewBuffer([]byte{}) + err := serializeHTLCEntries(buf, []*HTLCEntry{&entry}) + require.NoError(t, err) + + // Check the bytes are read as expected. + require.Equal(t, testHTLCEntryHashBytes, buf.Bytes()) +} + func TestSerializeHTLCEntries(t *testing.T) { t.Parallel() @@ -215,7 +263,7 @@ func TestSerializeHTLCEntries(t *testing.T) { // Create a fake rHash. rHashBytes := bytes.Repeat([]byte{10}, 32) - copy(entry.RHash[:], rHashBytes) + copy(entry.RHash.Val[:], rHashBytes) // Construct the serialized bytes. // @@ -269,7 +317,7 @@ func TestSerializeAndDeserializeRevLog(t *testing.T) { t, &test.revLog, test.revLogBytes, ) - testDerializeRevocationLog( + testDeserializeRevocationLog( t, &test.revLog, test.revLogBytes, ) }) @@ -293,7 +341,7 @@ func testSerializeRevocationLog(t *testing.T, rl *RevocationLog, require.Equal(t, revLogBytes, buf.Bytes()[:bodyIndex]) } -func testDerializeRevocationLog(t *testing.T, revLog *RevocationLog, +func testDeserializeRevocationLog(t *testing.T, revLog *RevocationLog, revLogBytes []byte) { // Construct the full bytes. @@ -309,7 +357,7 @@ func testDerializeRevocationLog(t *testing.T, revLog *RevocationLog, require.Equal(t, *revLog, rl) } -func TestDerializeHTLCEntriesEmptyRHash(t *testing.T) { +func TestDeserializeHTLCEntriesEmptyRHash(t *testing.T) { t.Parallel() // Read the tlv stream. @@ -322,7 +370,7 @@ func TestDerializeHTLCEntriesEmptyRHash(t *testing.T) { require.Equal(t, &testHTLCEntry, htlcs[0]) } -func TestDerializeHTLCEntries(t *testing.T) { +func TestDeserializeHTLCEntries(t *testing.T) { t.Parallel() // Copy the testHTLCEntry. @@ -330,7 +378,7 @@ func TestDerializeHTLCEntries(t *testing.T) { // Create a fake rHash. rHashBytes := bytes.Repeat([]byte{10}, 32) - copy(entry.RHash[:], rHashBytes) + copy(entry.RHash.Val[:], rHashBytes) // Construct the serialized bytes. // @@ -398,11 +446,11 @@ func TestDeleteLogBucket(t *testing.T) { err = kvdb.Update(backend, func(tx kvdb.RwTx) error { // Create the buckets. - chanBucket, _, err := createTestRevocatoinLogBuckets(tx) + chanBucket, _, err := createTestRevocationLogBuckets(tx) require.NoError(t, err) // Create the buckets again should give us an error. - _, _, err = createTestRevocatoinLogBuckets(tx) + _, _, err = createTestRevocationLogBuckets(tx) require.ErrorIs(t, err, kvdb.ErrBucketExists) // Delete both buckets. @@ -410,7 +458,7 @@ func TestDeleteLogBucket(t *testing.T) { require.NoError(t, err) // Create the buckets again should give us NO error. - _, _, err = createTestRevocatoinLogBuckets(tx) + _, _, err = createTestRevocationLogBuckets(tx) return err }, func() {}) require.NoError(t, err) @@ -516,7 +564,7 @@ func TestPutRevocationLog(t *testing.T) { // Construct the testing db transaction. dbTx := func(tx kvdb.RwTx) (RevocationLog, error) { // Create the buckets. - _, bucket, err := createTestRevocatoinLogBuckets(tx) + _, bucket, err := createTestRevocationLogBuckets(tx) require.NoError(t, err) // Save the log. @@ -686,7 +734,7 @@ func TestFetchRevocationLogCompatible(t *testing.T) { } } -func createTestRevocatoinLogBuckets(tx kvdb.RwTx) (kvdb.RwBucket, +func createTestRevocationLogBuckets(tx kvdb.RwTx) (kvdb.RwBucket, kvdb.RwBucket, error) { chanBucket, err := tx.CreateTopLevelBucket(openChannelBucket) diff --git a/lnwallet/channel.go b/lnwallet/channel.go index 3c724f5576..071d728938 100644 --- a/lnwallet/channel.go +++ b/lnwallet/channel.go @@ -2202,8 +2202,8 @@ func createHtlcRetribution(chanState *channeldb.OpenChannel, // then from the PoV of the remote commitment state, they're the // receiver of this HTLC. scriptInfo, err := genHtlcScript( - chanState.ChanType, htlc.Incoming, lntypes.Remote, - htlc.RefundTimeout, htlc.RHash, keyRing, + chanState.ChanType, htlc.Incoming.Val, lntypes.Remote, + htlc.RefundTimeout.Val, htlc.RHash.Val, keyRing, ) if err != nil { return emptyRetribution, err @@ -2216,7 +2216,7 @@ func createHtlcRetribution(chanState *channeldb.OpenChannel, WitnessScript: scriptInfo.WitnessScriptToSign(), Output: &wire.TxOut{ PkScript: scriptInfo.PkScript(), - Value: int64(htlc.Amt), + Value: int64(htlc.Amt.Val.Int()), }, HashType: sweepSigHash(chanState.ChanType), } @@ -2249,10 +2249,10 @@ func createHtlcRetribution(chanState *channeldb.OpenChannel, SignDesc: signDesc, OutPoint: wire.OutPoint{ Hash: commitHash, - Index: uint32(htlc.OutputIndex), + Index: uint32(htlc.OutputIndex.Val), }, SecondLevelWitnessScript: secondLevelWitnessScript, - IsIncoming: htlc.Incoming, + IsIncoming: htlc.Incoming.Val, SecondLevelTapTweak: secondLevelTapTweak, }, nil } @@ -2414,13 +2414,7 @@ func createBreachRetributionLegacy(revokedLog *channeldb.ChannelCommitment, continue } - entry := &channeldb.HTLCEntry{ - RHash: htlc.RHash, - RefundTimeout: htlc.RefundTimeout, - OutputIndex: uint16(htlc.OutputIndex), - Incoming: htlc.Incoming, - Amt: htlc.Amt.ToSatoshis(), - } + entry := channeldb.NewHTLCEntryFromHTLC(htlc) hr, err := createHtlcRetribution( chanState, keyRing, commitHash, commitmentSecret, leaseExpiry, entry, diff --git a/lnwallet/channel_test.go b/lnwallet/channel_test.go index 07c10c8023..3d27712e14 100644 --- a/lnwallet/channel_test.go +++ b/lnwallet/channel_test.go @@ -10053,9 +10053,11 @@ func TestCreateHtlcRetribution(t *testing.T) { aliceChannel.channelState, ) htlc := &channeldb.HTLCEntry{ - Amt: testAmt, - Incoming: true, - OutputIndex: 1, + Amt: tlv.NewRecordT[tlv.TlvType4]( + tlv.NewBigSizeT(testAmt), + ), + Incoming: tlv.NewPrimitiveRecord[tlv.TlvType3](true), + OutputIndex: tlv.NewPrimitiveRecord[tlv.TlvType2, uint16](1), } // Create the htlc retribution. @@ -10069,8 +10071,8 @@ func TestCreateHtlcRetribution(t *testing.T) { // Check the fields have expected values. require.EqualValues(t, testAmt, hr.SignDesc.Output.Value) require.Equal(t, commitHash, hr.OutPoint.Hash) - require.EqualValues(t, htlc.OutputIndex, hr.OutPoint.Index) - require.Equal(t, htlc.Incoming, hr.IsIncoming) + require.EqualValues(t, htlc.OutputIndex.Val, hr.OutPoint.Index) + require.Equal(t, htlc.Incoming.Val, hr.IsIncoming) } // TestCreateBreachRetribution checks that `createBreachRetribution` behaves as @@ -10110,9 +10112,13 @@ func TestCreateBreachRetribution(t *testing.T) { aliceChannel.channelState, ) htlc := &channeldb.HTLCEntry{ - Amt: btcutil.Amount(testAmt), - Incoming: true, - OutputIndex: uint16(htlcIndex), + Amt: tlv.NewRecordT[tlv.TlvType4]( + tlv.NewBigSizeT(btcutil.Amount(testAmt)), + ), + Incoming: tlv.NewPrimitiveRecord[tlv.TlvType3](true), + OutputIndex: tlv.NewPrimitiveRecord[tlv.TlvType2]( + uint16(htlcIndex), + ), } // Create a dummy revocation log. @@ -10239,11 +10245,12 @@ func TestCreateBreachRetribution(t *testing.T) { require.Equal(t, remote, br.RemoteOutpoint) for _, hr := range br.HtlcRetributions { - require.EqualValues(t, testAmt, - hr.SignDesc.Output.Value) + require.EqualValues( + t, testAmt, hr.SignDesc.Output.Value, + ) require.Equal(t, commitHash, hr.OutPoint.Hash) require.EqualValues(t, htlcIndex, hr.OutPoint.Index) - require.Equal(t, htlc.Incoming, hr.IsIncoming) + require.Equal(t, htlc.Incoming.Val, hr.IsIncoming) } } From 1f785e897e4f2f1a22533f8c9c7d801ce3af7aa7 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Sat, 30 Mar 2024 18:13:57 -0700 Subject: [PATCH 040/218] channeldb: convert RevocationLog to use RecordT --- channeldb/channel_test.go | 12 ++- channeldb/revocation_log.go | 158 ++++++++++++++++++------------- channeldb/revocation_log_test.go | 25 +++-- lnwallet/channel.go | 36 ++++--- lnwallet/channel_test.go | 27 +++--- 5 files changed, 145 insertions(+), 113 deletions(-) diff --git a/channeldb/channel_test.go b/channeldb/channel_test.go index 4427579a79..e92692201d 100644 --- a/channeldb/channel_test.go +++ b/channeldb/channel_test.go @@ -584,9 +584,11 @@ func assertCommitmentEqual(t *testing.T, a, b *ChannelCommitment) { func assertRevocationLogEntryEqual(t *testing.T, c *ChannelCommitment, r *RevocationLog) { + t.Helper() + // Check the common fields. require.EqualValues( - t, r.CommitTxHash, c.CommitTx.TxHash(), "CommitTx mismatch", + t, r.CommitTxHash.Val, c.CommitTx.TxHash(), "CommitTx mismatch", ) // Now check the common fields from the HTLCs. @@ -820,10 +822,10 @@ func TestChannelStateTransition(t *testing.T) { // Check the output indexes are saved as expected. require.EqualValues( - t, dummyLocalOutputIndex, diskPrevCommit.OurOutputIndex, + t, dummyLocalOutputIndex, diskPrevCommit.OurOutputIndex.Val, ) require.EqualValues( - t, dummyRemoteOutIndex, diskPrevCommit.TheirOutputIndex, + t, dummyRemoteOutIndex, diskPrevCommit.TheirOutputIndex.Val, ) // The two deltas (the original vs the on-disk version) should @@ -865,10 +867,10 @@ func TestChannelStateTransition(t *testing.T) { // Check the output indexes are saved as expected. require.EqualValues( - t, dummyLocalOutputIndex, diskPrevCommit.OurOutputIndex, + t, dummyLocalOutputIndex, diskPrevCommit.OurOutputIndex.Val, ) require.EqualValues( - t, dummyRemoteOutIndex, diskPrevCommit.TheirOutputIndex, + t, dummyRemoteOutIndex, diskPrevCommit.TheirOutputIndex.Val, ) assertRevocationLogEntryEqual(t, &oldRemoteCommit, prevCommit) diff --git a/channeldb/revocation_log.go b/channeldb/revocation_log.go index 26e4b9644a..3fc4163c11 100644 --- a/channeldb/revocation_log.go +++ b/channeldb/revocation_log.go @@ -7,6 +7,7 @@ import ( "math" "github.com/btcsuite/btcd/btcutil" + "github.com/lightningnetwork/lnd/fn" "github.com/lightningnetwork/lnd/kvdb" "github.com/lightningnetwork/lnd/lntypes" "github.com/lightningnetwork/lnd/lnwire" @@ -16,16 +17,15 @@ import ( const ( // OutputIndexEmpty is used when the output index doesn't exist. OutputIndexEmpty = math.MaxUint16 +) - // A set of tlv type definitions used to serialize the body of - // revocation logs to the database. - // - // NOTE: A migration should be added whenever this list changes. - revLogOurOutputIndexType tlv.Type = 0 - revLogTheirOutputIndexType tlv.Type = 1 - revLogCommitTxHashType tlv.Type = 2 - revLogOurBalanceType tlv.Type = 3 - revLogTheirBalanceType tlv.Type = 4 +type ( + // BigSizeAmount is a type alias for a TLV record of a btcutil.Amount. + BigSizeAmount = tlv.BigSizeT[btcutil.Amount] + + // BigSizeMilliSatoshi is a type alias for a TLV record of a + // lnwire.MilliSatoshi. + BigSizeMilliSatoshi = tlv.BigSizeT[lnwire.MilliSatoshi] ) var ( @@ -203,15 +203,15 @@ func NewHTLCEntryFromHTLC(htlc HTLC) *HTLCEntry { type RevocationLog struct { // OurOutputIndex specifies our output index in this commitment. In a // remote commitment transaction, this is the to remote output index. - OurOutputIndex uint16 + OurOutputIndex tlv.RecordT[tlv.TlvType0, uint16] // TheirOutputIndex specifies their output index in this commitment. In // a remote commitment transaction, this is the to local output index. - TheirOutputIndex uint16 + TheirOutputIndex tlv.RecordT[tlv.TlvType1, uint16] // CommitTxHash is the hash of the latest version of the commitment // state, broadcast able by us. - CommitTxHash [32]byte + CommitTxHash tlv.RecordT[tlv.TlvType2, [32]byte] // HTLCEntries is the set of HTLCEntry's that are pending at this // particular commitment height. @@ -221,21 +221,53 @@ type RevocationLog struct { // directly spendable by us. In other words, it is the value of the // to_remote output on the remote parties' commitment transaction. // - // NOTE: this is a pointer so that it is clear if the value is zero or + // NOTE: this is an option so that it is clear if the value is zero or // nil. Since migration 30 of the channeldb initially did not include // this field, it could be the case that the field is not present for // all revocation logs. - OurBalance *lnwire.MilliSatoshi + OurBalance tlv.OptionalRecordT[tlv.TlvType3, BigSizeMilliSatoshi] // TheirBalance is the current available balance within the channel // directly spendable by the remote node. In other words, it is the // value of the to_local output on the remote parties' commitment. // - // NOTE: this is a pointer so that it is clear if the value is zero or + // NOTE: this is an option so that it is clear if the value is zero or // nil. Since migration 30 of the channeldb initially did not include // this field, it could be the case that the field is not present for // all revocation logs. - TheirBalance *lnwire.MilliSatoshi + TheirBalance tlv.OptionalRecordT[tlv.TlvType4, BigSizeMilliSatoshi] +} + +// NewRevocationLog creates a new RevocationLog from the given parameters. +func NewRevocationLog(ourOutputIndex uint16, theirOutputIndex uint16, + commitHash [32]byte, ourBalance, + theirBalance fn.Option[lnwire.MilliSatoshi], + htlcs []*HTLCEntry) RevocationLog { + + rl := RevocationLog{ + OurOutputIndex: tlv.NewPrimitiveRecord[tlv.TlvType0]( + ourOutputIndex, + ), + TheirOutputIndex: tlv.NewPrimitiveRecord[tlv.TlvType1]( + theirOutputIndex, + ), + CommitTxHash: tlv.NewPrimitiveRecord[tlv.TlvType2](commitHash), + HTLCEntries: htlcs, + } + + ourBalance.WhenSome(func(balance lnwire.MilliSatoshi) { + rl.OurBalance = tlv.SomeRecordT(tlv.NewRecordT[tlv.TlvType3]( + tlv.NewBigSizeT(balance), + )) + }) + + theirBalance.WhenSome(func(balance lnwire.MilliSatoshi) { + rl.TheirBalance = tlv.SomeRecordT(tlv.NewRecordT[tlv.TlvType4]( + tlv.NewBigSizeT(balance), + )) + }) + + return rl } // putRevocationLog uses the fields `CommitTx` and `Htlcs` from a @@ -254,15 +286,26 @@ func putRevocationLog(bucket kvdb.RwBucket, commit *ChannelCommitment, } rl := &RevocationLog{ - OurOutputIndex: uint16(ourOutputIndex), - TheirOutputIndex: uint16(theirOutputIndex), - CommitTxHash: commit.CommitTx.TxHash(), - HTLCEntries: make([]*HTLCEntry, 0, len(commit.Htlcs)), + OurOutputIndex: tlv.NewPrimitiveRecord[tlv.TlvType0]( + uint16(ourOutputIndex), + ), + TheirOutputIndex: tlv.NewPrimitiveRecord[tlv.TlvType1]( + uint16(theirOutputIndex), + ), + CommitTxHash: tlv.NewPrimitiveRecord[tlv.TlvType2, [32]byte]( + commit.CommitTx.TxHash(), + ), + HTLCEntries: make([]*HTLCEntry, 0, len(commit.Htlcs)), } if !noAmtData { - rl.OurBalance = &commit.LocalBalance - rl.TheirBalance = &commit.RemoteBalance + rl.OurBalance = tlv.SomeRecordT(tlv.NewRecordT[tlv.TlvType3]( + tlv.NewBigSizeT(commit.LocalBalance), + )) + + rl.TheirBalance = tlv.SomeRecordT(tlv.NewRecordT[tlv.TlvType4]( + tlv.NewBigSizeT(commit.RemoteBalance), + )) } for _, htlc := range commit.Htlcs { @@ -312,31 +355,23 @@ func fetchRevocationLog(log kvdb.RBucket, func serializeRevocationLog(w io.Writer, rl *RevocationLog) error { // Add the tlv records for all non-optional fields. records := []tlv.Record{ - tlv.MakePrimitiveRecord( - revLogOurOutputIndexType, &rl.OurOutputIndex, - ), - tlv.MakePrimitiveRecord( - revLogTheirOutputIndexType, &rl.TheirOutputIndex, - ), - tlv.MakePrimitiveRecord( - revLogCommitTxHashType, &rl.CommitTxHash, - ), + rl.OurOutputIndex.Record(), + rl.TheirOutputIndex.Record(), + rl.CommitTxHash.Record(), } // Now we add any optional fields that are non-nil. - if rl.OurBalance != nil { - lb := uint64(*rl.OurBalance) - records = append(records, tlv.MakeBigSizeRecord( - revLogOurBalanceType, &lb, - )) - } + rl.OurBalance.WhenSome( + func(r tlv.RecordT[tlv.TlvType3, BigSizeMilliSatoshi]) { + records = append(records, r.Record()) + }, + ) - if rl.TheirBalance != nil { - rb := uint64(*rl.TheirBalance) - records = append(records, tlv.MakeBigSizeRecord( - revLogTheirBalanceType, &rb, - )) - } + rl.TheirBalance.WhenSome( + func(r tlv.RecordT[tlv.TlvType4, BigSizeMilliSatoshi]) { + records = append(records, r.Record()) + }, + ) // Create the tlv stream. tlvStream, err := tlv.NewStream(records...) @@ -374,27 +409,18 @@ func serializeHTLCEntries(w io.Writer, htlcs []*HTLCEntry) error { // deserializeRevocationLog deserializes a RevocationLog based on tlv format. func deserializeRevocationLog(r io.Reader) (RevocationLog, error) { - var ( - rl RevocationLog - ourBalance uint64 - theirBalance uint64 - ) + var rl RevocationLog + + ourBalance := rl.OurBalance.Zero() + theirBalance := rl.TheirBalance.Zero() // Create the tlv stream. tlvStream, err := tlv.NewStream( - tlv.MakePrimitiveRecord( - revLogOurOutputIndexType, &rl.OurOutputIndex, - ), - tlv.MakePrimitiveRecord( - revLogTheirOutputIndexType, &rl.TheirOutputIndex, - ), - tlv.MakePrimitiveRecord( - revLogCommitTxHashType, &rl.CommitTxHash, - ), - tlv.MakeBigSizeRecord(revLogOurBalanceType, &ourBalance), - tlv.MakeBigSizeRecord( - revLogTheirBalanceType, &theirBalance, - ), + rl.OurOutputIndex.Record(), + rl.TheirOutputIndex.Record(), + rl.CommitTxHash.Record(), + ourBalance.Record(), + theirBalance.Record(), ) if err != nil { return rl, err @@ -406,14 +432,12 @@ func deserializeRevocationLog(r io.Reader) (RevocationLog, error) { return rl, err } - if t, ok := parsedTypes[revLogOurBalanceType]; ok && t == nil { - lb := lnwire.MilliSatoshi(ourBalance) - rl.OurBalance = &lb + if t, ok := parsedTypes[ourBalance.TlvType()]; ok && t == nil { + rl.OurBalance = tlv.SomeRecordT(ourBalance) } - if t, ok := parsedTypes[revLogTheirBalanceType]; ok && t == nil { - rb := lnwire.MilliSatoshi(theirBalance) - rl.TheirBalance = &rb + if t, ok := parsedTypes[theirBalance.TlvType()]; ok && t == nil { + rl.TheirBalance = tlv.SomeRecordT(theirBalance) } // Read the HTLC entries. diff --git a/channeldb/revocation_log_test.go b/channeldb/revocation_log_test.go index c9d2a16f70..813f70ac99 100644 --- a/channeldb/revocation_log_test.go +++ b/channeldb/revocation_log_test.go @@ -8,6 +8,7 @@ import ( "testing" "github.com/btcsuite/btcd/btcutil" + "github.com/lightningnetwork/lnd/fn" "github.com/lightningnetwork/lnd/kvdb" "github.com/lightningnetwork/lnd/lntest/channels" "github.com/lightningnetwork/lnd/lnwire" @@ -115,12 +116,11 @@ var ( }}, } - testRevocationLogNoAmts = RevocationLog{ - OurOutputIndex: 0, - TheirOutputIndex: 1, - CommitTxHash: testChannelCommit.CommitTx.TxHash(), - HTLCEntries: []*HTLCEntry{&testHTLCEntry}, - } + testRevocationLogNoAmts = NewRevocationLog( + 0, 1, testChannelCommit.CommitTx.TxHash(), + fn.None[lnwire.MilliSatoshi](), fn.None[lnwire.MilliSatoshi](), + []*HTLCEntry{&testHTLCEntry}, + ) testRevocationLogNoAmtsBytes = []byte{ // Body length 42. 0x2a, @@ -136,14 +136,11 @@ var ( 0xc8, 0x22, 0x51, 0xb1, 0x5b, 0xa0, 0xbf, 0xd, } - testRevocationLogWithAmts = RevocationLog{ - OurOutputIndex: 0, - TheirOutputIndex: 1, - CommitTxHash: testChannelCommit.CommitTx.TxHash(), - HTLCEntries: []*HTLCEntry{&testHTLCEntry}, - OurBalance: &localBalance, - TheirBalance: &remoteBalance, - } + testRevocationLogWithAmts = NewRevocationLog( + 0, 1, testChannelCommit.CommitTx.TxHash(), + fn.Some(localBalance), fn.Some(remoteBalance), + []*HTLCEntry{&testHTLCEntry}, + ) testRevocationLogWithAmtsBytes = []byte{ // Body length 52. 0x34, diff --git a/lnwallet/channel.go b/lnwallet/channel.go index 071d728938..7012c1b047 100644 --- a/lnwallet/channel.go +++ b/lnwallet/channel.go @@ -2275,7 +2275,7 @@ func createBreachRetribution(revokedLog *channeldb.RevocationLog, htlcRetributions := make([]HtlcRetribution, len(revokedLog.HTLCEntries)) for i, htlc := range revokedLog.HTLCEntries { hr, err := createHtlcRetribution( - chanState, keyRing, commitHash, + chanState, keyRing, commitHash.Val, commitmentSecret, leaseExpiry, htlc, ) if err != nil { @@ -2288,10 +2288,10 @@ func createBreachRetribution(revokedLog *channeldb.RevocationLog, // Construct the our outpoint. ourOutpoint := wire.OutPoint{ - Hash: commitHash, + Hash: commitHash.Val, } - if revokedLog.OurOutputIndex != channeldb.OutputIndexEmpty { - ourOutpoint.Index = uint32(revokedLog.OurOutputIndex) + if revokedLog.OurOutputIndex.Val != channeldb.OutputIndexEmpty { + ourOutpoint.Index = uint32(revokedLog.OurOutputIndex.Val) // If the spend transaction is provided, then we use it to get // the value of our output. @@ -2314,26 +2314,29 @@ func createBreachRetribution(revokedLog *channeldb.RevocationLog, // contains our output amount. Due to a previous // migration, this field may be empty in which case an // error will be returned. - if revokedLog.OurBalance == nil { - return nil, 0, 0, ErrRevLogDataMissing + b, err := revokedLog.OurBalance.ValOpt().UnwrapOrErr( + ErrRevLogDataMissing, + ) + if err != nil { + return nil, 0, 0, err } - ourAmt = int64(revokedLog.OurBalance.ToSatoshis()) + ourAmt = int64(b.Int().ToSatoshis()) } } // Construct the their outpoint. theirOutpoint := wire.OutPoint{ - Hash: commitHash, + Hash: commitHash.Val, } - if revokedLog.TheirOutputIndex != channeldb.OutputIndexEmpty { - theirOutpoint.Index = uint32(revokedLog.TheirOutputIndex) + if revokedLog.TheirOutputIndex.Val != channeldb.OutputIndexEmpty { + theirOutpoint.Index = uint32(revokedLog.TheirOutputIndex.Val) // If the spend transaction is provided, then we use it to get // the value of the remote parties' output. if spendTx != nil { // Sanity check that TheirOutputIndex is within range. - if int(revokedLog.TheirOutputIndex) >= + if int(revokedLog.TheirOutputIndex.Val) >= len(spendTx.TxOut) { return nil, 0, 0, fmt.Errorf("%w: theirs=%v, "+ @@ -2351,16 +2354,19 @@ func createBreachRetribution(revokedLog *channeldb.RevocationLog, // contains remote parties' output amount. Due to a // previous migration, this field may be empty in which // case an error will be returned. - if revokedLog.TheirBalance == nil { - return nil, 0, 0, ErrRevLogDataMissing + b, err := revokedLog.TheirBalance.ValOpt().UnwrapOrErr( + ErrRevLogDataMissing, + ) + if err != nil { + return nil, 0, 0, err } - theirAmt = int64(revokedLog.TheirBalance.ToSatoshis()) + theirAmt = int64(b.Int().ToSatoshis()) } } return &BreachRetribution{ - BreachTxHash: commitHash, + BreachTxHash: commitHash.Val, ChainHash: chanState.ChainHash, LocalOutpoint: ourOutpoint, RemoteOutpoint: theirOutpoint, diff --git a/lnwallet/channel_test.go b/lnwallet/channel_test.go index 3d27712e14..ac4b46b4fb 100644 --- a/lnwallet/channel_test.go +++ b/lnwallet/channel_test.go @@ -10124,22 +10124,19 @@ func TestCreateBreachRetribution(t *testing.T) { // Create a dummy revocation log. ourAmtMsat := lnwire.MilliSatoshi(ourAmt * 1000) theirAmtMsat := lnwire.MilliSatoshi(theirAmt * 1000) - revokedLog := channeldb.RevocationLog{ - CommitTxHash: commitHash, - OurOutputIndex: uint16(localIndex), - TheirOutputIndex: uint16(remoteIndex), - HTLCEntries: []*channeldb.HTLCEntry{htlc}, - TheirBalance: &theirAmtMsat, - OurBalance: &ourAmtMsat, - } + revokedLog := channeldb.NewRevocationLog( + uint16(localIndex), uint16(remoteIndex), commitHash, + fn.Some(ourAmtMsat), fn.Some(theirAmtMsat), + []*channeldb.HTLCEntry{htlc}, + ) // Create a log with an empty local output index. revokedLogNoLocal := revokedLog - revokedLogNoLocal.OurOutputIndex = channeldb.OutputIndexEmpty + revokedLogNoLocal.OurOutputIndex.Val = channeldb.OutputIndexEmpty // Create a log with an empty remote output index. revokedLogNoRemote := revokedLog - revokedLogNoRemote.TheirOutputIndex = channeldb.OutputIndexEmpty + revokedLogNoRemote.TheirOutputIndex.Val = channeldb.OutputIndexEmpty testCases := []struct { name string @@ -10169,14 +10166,20 @@ func TestCreateBreachRetribution(t *testing.T) { { name: "fail due to our index too big", revocationLog: &channeldb.RevocationLog{ - OurOutputIndex: uint16(htlcIndex + 1), + //nolint:lll + OurOutputIndex: tlv.NewPrimitiveRecord[tlv.TlvType0]( + uint16(htlcIndex + 1), + ), }, expectedErr: ErrOutputIndexOutOfRange, }, { name: "fail due to their index too big", revocationLog: &channeldb.RevocationLog{ - TheirOutputIndex: uint16(htlcIndex + 1), + //nolint:lll + TheirOutputIndex: tlv.NewPrimitiveRecord[tlv.TlvType1]( + uint16(htlcIndex + 1), + ), }, expectedErr: ErrOutputIndexOutOfRange, }, From eb600a9447447d6301c0a56f54f5f26cf835fe49 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Mon, 1 Apr 2024 17:00:55 -0700 Subject: [PATCH 041/218] channeldb: add custom blobs to RevocationLog+HTLCEntry This'll be useful for custom channel types that want to store extra information that'll be useful to help handle channel revocation cases. --- channeldb/revocation_log.go | 88 ++++++++++++++++++++++++++++---- channeldb/revocation_log_test.go | 45 ++++++++++++---- lnwallet/channel.go | 6 ++- lnwallet/channel_test.go | 2 +- 4 files changed, 120 insertions(+), 21 deletions(-) diff --git a/channeldb/revocation_log.go b/channeldb/revocation_log.go index 3fc4163c11..b7b73a35ab 100644 --- a/channeldb/revocation_log.go +++ b/channeldb/revocation_log.go @@ -164,22 +164,32 @@ type HTLCEntry struct { // // NOTE: this field is the memory representation of the field amtUint. Amt tlv.RecordT[tlv.TlvType4, tlv.BigSizeT[btcutil.Amount]] + + // CustomBlob is an optional blob that can be used to store information + // specific to revocation handling for a custom channel type. + CustomBlob tlv.OptionalRecordT[tlv.TlvType5, tlv.Blob] } // toTlvStream converts an HTLCEntry record into a tlv representation. func (h *HTLCEntry) toTlvStream() (*tlv.Stream, error) { - return tlv.NewStream( + records := []tlv.Record{ h.RHash.Record(), h.RefundTimeout.Record(), h.OutputIndex.Record(), h.Incoming.Record(), h.Amt.Record(), - ) + } + + h.CustomBlob.WhenSome(func(r tlv.RecordT[tlv.TlvType5, tlv.Blob]) { + records = append(records, r.Record()) + }) + + return tlv.NewStream(records...) } // NewHTLCEntryFromHTLC creates a new HTLCEntry from an HTLC. -func NewHTLCEntryFromHTLC(htlc HTLC) *HTLCEntry { - return &HTLCEntry{ +func NewHTLCEntryFromHTLC(htlc HTLC) (*HTLCEntry, error) { + h := &HTLCEntry{ RHash: tlv.NewRecordT[tlv.TlvType0]( NewSparsePayHash(htlc.RHash), ), @@ -194,6 +204,19 @@ func NewHTLCEntryFromHTLC(htlc HTLC) *HTLCEntry { tlv.NewBigSizeT(htlc.Amt.ToSatoshis()), ), } + + if len(htlc.CustomRecords) != 0 { + blob, err := htlc.CustomRecords.Serialize() + if err != nil { + return nil, err + } + + h.CustomBlob = tlv.SomeRecordT( + tlv.NewPrimitiveRecord[tlv.TlvType5, tlv.Blob](blob), + ) + } + + return h, nil } // RevocationLog stores the info needed to construct a breach retribution. Its @@ -236,13 +259,19 @@ type RevocationLog struct { // this field, it could be the case that the field is not present for // all revocation logs. TheirBalance tlv.OptionalRecordT[tlv.TlvType4, BigSizeMilliSatoshi] + + // CustomBlob is an optional blob that can be used to store information + // specific to a custom channel type. This information is only created + // at channel funding time, and after wards is to be considered + // immutable. + CustomBlob tlv.OptionalRecordT[tlv.TlvType5, tlv.Blob] } // NewRevocationLog creates a new RevocationLog from the given parameters. func NewRevocationLog(ourOutputIndex uint16, theirOutputIndex uint16, commitHash [32]byte, ourBalance, - theirBalance fn.Option[lnwire.MilliSatoshi], - htlcs []*HTLCEntry) RevocationLog { + theirBalance fn.Option[lnwire.MilliSatoshi], htlcs []*HTLCEntry, + customBlob fn.Option[tlv.Blob]) RevocationLog { rl := RevocationLog{ OurOutputIndex: tlv.NewPrimitiveRecord[tlv.TlvType0]( @@ -267,6 +296,12 @@ func NewRevocationLog(ourOutputIndex uint16, theirOutputIndex uint16, )) }) + customBlob.WhenSome(func(blob tlv.Blob) { + rl.CustomBlob = tlv.SomeRecordT( + tlv.NewPrimitiveRecord[tlv.TlvType5, tlv.Blob](blob), + ) + }) + return rl } @@ -298,6 +333,12 @@ func putRevocationLog(bucket kvdb.RwBucket, commit *ChannelCommitment, HTLCEntries: make([]*HTLCEntry, 0, len(commit.Htlcs)), } + commit.CustomBlob.WhenSome(func(blob tlv.Blob) { + rl.CustomBlob = tlv.SomeRecordT( + tlv.NewPrimitiveRecord[tlv.TlvType5, tlv.Blob](blob), + ) + }) + if !noAmtData { rl.OurBalance = tlv.SomeRecordT(tlv.NewRecordT[tlv.TlvType3]( tlv.NewBigSizeT(commit.LocalBalance), @@ -320,7 +361,10 @@ func putRevocationLog(bucket kvdb.RwBucket, commit *ChannelCommitment, return ErrOutputIndexTooBig } - entry := NewHTLCEntryFromHTLC(htlc) + entry, err := NewHTLCEntryFromHTLC(htlc) + if err != nil { + return err + } rl.HTLCEntries = append(rl.HTLCEntries, entry) } @@ -373,6 +417,10 @@ func serializeRevocationLog(w io.Writer, rl *RevocationLog) error { }, ) + rl.CustomBlob.WhenSome(func(r tlv.RecordT[tlv.TlvType5, tlv.Blob]) { + records = append(records, r.Record()) + }) + // Create the tlv stream. tlvStream, err := tlv.NewStream(records...) if err != nil { @@ -413,6 +461,7 @@ func deserializeRevocationLog(r io.Reader) (RevocationLog, error) { ourBalance := rl.OurBalance.Zero() theirBalance := rl.TheirBalance.Zero() + customBlob := rl.CustomBlob.Zero() // Create the tlv stream. tlvStream, err := tlv.NewStream( @@ -421,6 +470,7 @@ func deserializeRevocationLog(r io.Reader) (RevocationLog, error) { rl.CommitTxHash.Record(), ourBalance.Record(), theirBalance.Record(), + customBlob.Record(), ) if err != nil { return rl, err @@ -440,6 +490,10 @@ func deserializeRevocationLog(r io.Reader) (RevocationLog, error) { rl.TheirBalance = tlv.SomeRecordT(theirBalance) } + if t, ok := parsedTypes[customBlob.TlvType()]; ok && t == nil { + rl.CustomBlob = tlv.SomeRecordT(customBlob) + } + // Read the HTLC entries. rl.HTLCEntries, err = deserializeHTLCEntries(r) @@ -454,14 +508,26 @@ func deserializeHTLCEntries(r io.Reader) ([]*HTLCEntry, error) { for { var htlc HTLCEntry + customBlob := htlc.CustomBlob.Zero() + // Create the tlv stream. - tlvStream, err := htlc.toTlvStream() + records := []tlv.Record{ + htlc.RHash.Record(), + htlc.RefundTimeout.Record(), + htlc.OutputIndex.Record(), + htlc.Incoming.Record(), + htlc.Amt.Record(), + customBlob.Record(), + } + + tlvStream, err := tlv.NewStream(records...) if err != nil { return nil, err } // Read the HTLC entry. - if _, err := readTlvStream(r, tlvStream); err != nil { + parsedTypes, err := readTlvStream(r, tlvStream) + if err != nil { // We've reached the end when hitting an EOF. if err == io.ErrUnexpectedEOF { break @@ -469,6 +535,10 @@ func deserializeHTLCEntries(r io.Reader) ([]*HTLCEntry, error) { return nil, err } + if t, ok := parsedTypes[customBlob.TlvType()]; ok && t == nil { + htlc.CustomBlob = tlv.SomeRecordT(customBlob) + } + // Append the entry. htlcs = append(htlcs, &htlc) } diff --git a/channeldb/revocation_log_test.go b/channeldb/revocation_log_test.go index 813f70ac99..139a02d521 100644 --- a/channeldb/revocation_log_test.go +++ b/channeldb/revocation_log_test.go @@ -34,6 +34,17 @@ var ( 0xff, // value = 255 } + customRecords = lnwire.CustomRecords{ + lnwire.MinCustomRecordsTlvType + 1: []byte("custom data"), + } + + blobBytes = []byte{ + // Corresponds to the encoded version of the above custom + // records. + 0xfe, 0x00, 0x01, 0x00, 0x01, 0x0b, 0x63, 0x75, 0x73, 0x74, + 0x6f, 0x6d, 0x20, 0x64, 0x61, 0x74, 0x61, + } + testHTLCEntry = HTLCEntry{ RefundTimeout: tlv.NewPrimitiveRecord[tlv.TlvType1, uint32]( 740_000, @@ -45,10 +56,13 @@ var ( Amt: tlv.NewRecordT[tlv.TlvType4]( tlv.NewBigSizeT(btcutil.Amount(1_000_000)), ), + CustomBlob: tlv.SomeRecordT( + tlv.NewPrimitiveRecord[tlv.TlvType5](blobBytes), + ), } testHTLCEntryBytes = []byte{ - // Body length 23. - 0x16, + // Body length 41. + 0x29, // Rhash tlv. 0x0, 0x0, // RefundTimeout tlv. @@ -59,6 +73,9 @@ var ( 0x3, 0x1, 0x1, // Amt tlv. 0x4, 0x5, 0xfe, 0x0, 0xf, 0x42, 0x40, + // Custom blob tlv. + 0x5, 0x11, 0xfe, 0x00, 0x01, 0x00, 0x01, 0x0b, 0x63, 0x75, 0x73, + 0x74, 0x6f, 0x6d, 0x20, 0x64, 0x61, 0x74, 0x61, } testHTLCEntryHash = HTLCEntry{ @@ -113,17 +130,19 @@ var ( Amt: lnwire.NewMSatFromSatoshis( testHTLCEntry.Amt.Val.Int(), ), + CustomRecords: customRecords, }}, + CustomBlob: fn.Some(blobBytes), } testRevocationLogNoAmts = NewRevocationLog( 0, 1, testChannelCommit.CommitTx.TxHash(), fn.None[lnwire.MilliSatoshi](), fn.None[lnwire.MilliSatoshi](), - []*HTLCEntry{&testHTLCEntry}, + []*HTLCEntry{&testHTLCEntry}, fn.Some(blobBytes), ) testRevocationLogNoAmtsBytes = []byte{ - // Body length 42. - 0x2a, + // Body length 61. + 0x3d, // OurOutputIndex tlv. 0x0, 0x2, 0x0, 0x0, // TheirOutputIndex tlv. @@ -134,16 +153,19 @@ var ( 0x6e, 0x60, 0x29, 0x23, 0x1d, 0x5e, 0xc5, 0xe6, 0xbd, 0xf7, 0xd3, 0x9b, 0x16, 0x7d, 0x0, 0xff, 0xc8, 0x22, 0x51, 0xb1, 0x5b, 0xa0, 0xbf, 0xd, + // Custom blob tlv. + 0x5, 0x11, 0xfe, 0x00, 0x01, 0x00, 0x01, 0x0b, 0x63, 0x75, 0x73, + 0x74, 0x6f, 0x6d, 0x20, 0x64, 0x61, 0x74, 0x61, } testRevocationLogWithAmts = NewRevocationLog( 0, 1, testChannelCommit.CommitTx.TxHash(), fn.Some(localBalance), fn.Some(remoteBalance), - []*HTLCEntry{&testHTLCEntry}, + []*HTLCEntry{&testHTLCEntry}, fn.Some(blobBytes), ) testRevocationLogWithAmtsBytes = []byte{ - // Body length 52. - 0x34, + // Body length 71. + 0x47, // OurOutputIndex tlv. 0x0, 0x2, 0x0, 0x0, // TheirOutputIndex tlv. @@ -158,6 +180,9 @@ var ( 0x3, 0x3, 0xfd, 0x23, 0x28, // Remote Balance. 0x4, 0x3, 0xfd, 0x0b, 0xb8, + // Custom blob tlv. + 0x5, 0x11, 0xfe, 0x00, 0x01, 0x00, 0x01, 0x0b, 0x63, 0x75, 0x73, + 0x74, 0x6f, 0x6d, 0x20, 0x64, 0x61, 0x74, 0x61, } ) @@ -269,7 +294,7 @@ func TestSerializeHTLCEntries(t *testing.T) { partialBytes := testHTLCEntryBytes[3:] // Write the total length and RHash tlv. - expectedBytes := []byte{0x36, 0x0, 0x20} + expectedBytes := []byte{0x49, 0x0, 0x20} expectedBytes = append(expectedBytes, rHashBytes...) // Append the rest. @@ -384,7 +409,7 @@ func TestDeserializeHTLCEntries(t *testing.T) { partialBytes := testHTLCEntryBytes[3:] // Write the total length and RHash tlv. - testBytes := append([]byte{0x36, 0x0, 0x20}, rHashBytes...) + testBytes := append([]byte{0x4d, 0x0, 0x20}, rHashBytes...) // Append the rest. testBytes = append(testBytes, partialBytes...) diff --git a/lnwallet/channel.go b/lnwallet/channel.go index 7012c1b047..87a4702073 100644 --- a/lnwallet/channel.go +++ b/lnwallet/channel.go @@ -2420,7 +2420,11 @@ func createBreachRetributionLegacy(revokedLog *channeldb.ChannelCommitment, continue } - entry := channeldb.NewHTLCEntryFromHTLC(htlc) + entry, err := channeldb.NewHTLCEntryFromHTLC(htlc) + if err != nil { + return nil, 0, 0, err + } + hr, err := createHtlcRetribution( chanState, keyRing, commitHash, commitmentSecret, leaseExpiry, entry, diff --git a/lnwallet/channel_test.go b/lnwallet/channel_test.go index ac4b46b4fb..0be7be1b4f 100644 --- a/lnwallet/channel_test.go +++ b/lnwallet/channel_test.go @@ -10127,7 +10127,7 @@ func TestCreateBreachRetribution(t *testing.T) { revokedLog := channeldb.NewRevocationLog( uint16(localIndex), uint16(remoteIndex), commitHash, fn.Some(ourAmtMsat), fn.Some(theirAmtMsat), - []*channeldb.HTLCEntry{htlc}, + []*channeldb.HTLCEntry{htlc}, fn.None[tlv.Blob](), ) // Create a log with an empty local output index. From 180648072cee0a39842b7efc8814cfe881d424b6 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Mon, 1 Apr 2024 17:44:14 -0700 Subject: [PATCH 042/218] channeldb: add HtlcIndex to HTLCEntry This may be useful for custom channel types that base everything off the index (a global value) rather than the output index (can change with each state). --- channeldb/revocation_log.go | 23 ++++++++++++++++++----- channeldb/revocation_log_test.go | 17 +++++++++++++---- 2 files changed, 31 insertions(+), 9 deletions(-) diff --git a/channeldb/revocation_log.go b/channeldb/revocation_log.go index b7b73a35ab..3abc73f81e 100644 --- a/channeldb/revocation_log.go +++ b/channeldb/revocation_log.go @@ -155,19 +155,17 @@ type HTLCEntry struct { // Incoming denotes whether we're the receiver or the sender of this // HTLC. - // - // NOTE: this field is the memory representation of the field - // incomingUint. Incoming tlv.RecordT[tlv.TlvType3, bool] // Amt is the amount of satoshis this HTLC escrows. - // - // NOTE: this field is the memory representation of the field amtUint. Amt tlv.RecordT[tlv.TlvType4, tlv.BigSizeT[btcutil.Amount]] // CustomBlob is an optional blob that can be used to store information // specific to revocation handling for a custom channel type. CustomBlob tlv.OptionalRecordT[tlv.TlvType5, tlv.Blob] + + // HtlcIndex is the index of the HTLC in the channel. + HtlcIndex tlv.OptionalRecordT[tlv.TlvType6, uint16] } // toTlvStream converts an HTLCEntry record into a tlv representation. @@ -184,6 +182,12 @@ func (h *HTLCEntry) toTlvStream() (*tlv.Stream, error) { records = append(records, r.Record()) }) + h.HtlcIndex.WhenSome(func(r tlv.RecordT[tlv.TlvType6, uint16]) { + records = append(records, r.Record()) + }) + + tlv.SortRecords(records) + return tlv.NewStream(records...) } @@ -203,6 +207,9 @@ func NewHTLCEntryFromHTLC(htlc HTLC) (*HTLCEntry, error) { Amt: tlv.NewRecordT[tlv.TlvType4]( tlv.NewBigSizeT(htlc.Amt.ToSatoshis()), ), + HtlcIndex: tlv.SomeRecordT(tlv.NewPrimitiveRecord[tlv.TlvType6]( + uint16(htlc.HtlcIndex), + )), } if len(htlc.CustomRecords) != 0 { @@ -509,6 +516,7 @@ func deserializeHTLCEntries(r io.Reader) ([]*HTLCEntry, error) { var htlc HTLCEntry customBlob := htlc.CustomBlob.Zero() + htlcIndex := htlc.HtlcIndex.Zero() // Create the tlv stream. records := []tlv.Record{ @@ -518,6 +526,7 @@ func deserializeHTLCEntries(r io.Reader) ([]*HTLCEntry, error) { htlc.Incoming.Record(), htlc.Amt.Record(), customBlob.Record(), + htlcIndex.Record(), } tlvStream, err := tlv.NewStream(records...) @@ -539,6 +548,10 @@ func deserializeHTLCEntries(r io.Reader) ([]*HTLCEntry, error) { htlc.CustomBlob = tlv.SomeRecordT(customBlob) } + if t, ok := parsedTypes[htlcIndex.TlvType()]; ok && t == nil { + htlc.HtlcIndex = tlv.SomeRecordT(htlcIndex) + } + // Append the entry. htlcs = append(htlcs, &htlc) } diff --git a/channeldb/revocation_log_test.go b/channeldb/revocation_log_test.go index 139a02d521..4290552eee 100644 --- a/channeldb/revocation_log_test.go +++ b/channeldb/revocation_log_test.go @@ -59,10 +59,13 @@ var ( CustomBlob: tlv.SomeRecordT( tlv.NewPrimitiveRecord[tlv.TlvType5](blobBytes), ), + HtlcIndex: tlv.SomeRecordT( + tlv.NewPrimitiveRecord[tlv.TlvType6, uint16](0x33), + ), } testHTLCEntryBytes = []byte{ - // Body length 41. - 0x29, + // Body length 45. + 0x2d, // Rhash tlv. 0x0, 0x0, // RefundTimeout tlv. @@ -76,6 +79,8 @@ var ( // Custom blob tlv. 0x5, 0x11, 0xfe, 0x00, 0x01, 0x00, 0x01, 0x0b, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x20, 0x64, 0x61, 0x74, 0x61, + // HLTC index tlv. + 0x6, 0x2, 0x0, 0x33, } testHTLCEntryHash = HTLCEntry{ @@ -126,7 +131,11 @@ var ( Htlcs: []HTLC{{ RefundTimeout: testHTLCEntry.RefundTimeout.Val, OutputIndex: int32(testHTLCEntry.OutputIndex.Val), - Incoming: testHTLCEntry.Incoming.Val, + HtlcIndex: uint64( + testHTLCEntry.HtlcIndex.ValOpt(). + UnsafeFromSome(), + ), + Incoming: testHTLCEntry.Incoming.Val, Amt: lnwire.NewMSatFromSatoshis( testHTLCEntry.Amt.Val.Int(), ), @@ -294,7 +303,7 @@ func TestSerializeHTLCEntries(t *testing.T) { partialBytes := testHTLCEntryBytes[3:] // Write the total length and RHash tlv. - expectedBytes := []byte{0x49, 0x0, 0x20} + expectedBytes := []byte{0x4d, 0x0, 0x20} expectedBytes = append(expectedBytes, rHashBytes...) // Append the rest. From b1c8a836e3e52a9f4d345cd0c4a67d6c7656a5ef Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Thu, 25 Apr 2024 19:00:42 +0200 Subject: [PATCH 043/218] multi: thread thru the AuxLeafStore everywhere --- chainreg/chainregistry.go | 5 ++ config_builder.go | 36 +++++++++----- contractcourt/breach_arbitrator_test.go | 3 ++ contractcourt/chain_arbitrator.go | 18 ++++++- contractcourt/chain_watcher.go | 11 +++-- funding/manager.go | 12 ++++- funding/manager_test.go | 5 ++ lnd.go | 3 +- lnwallet/channel.go | 63 +++++++++++++++---------- lnwallet/channel_test.go | 10 ++++ lnwallet/config.go | 5 ++ lnwallet/mock.go | 44 +++++++++++++++++ lnwallet/wallet.go | 9 +++- peer/brontide.go | 14 +++++- server.go | 3 ++ 15 files changed, 194 insertions(+), 47 deletions(-) diff --git a/chainreg/chainregistry.go b/chainreg/chainregistry.go index 37e72fdee3..41a2fcbb7f 100644 --- a/chainreg/chainregistry.go +++ b/chainreg/chainregistry.go @@ -24,6 +24,7 @@ import ( "github.com/lightningnetwork/lnd/chainntnfs/neutrinonotify" "github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/channeldb/models" + "github.com/lightningnetwork/lnd/fn" "github.com/lightningnetwork/lnd/input" "github.com/lightningnetwork/lnd/keychain" "github.com/lightningnetwork/lnd/kvdb" @@ -63,6 +64,10 @@ type Config struct { // state. ChanStateDB *channeldb.ChannelStateDB + // AuxLeafStore is an optional store that can be used to store auxiliary + // leaves for certain custom channel types. + AuxLeafStore fn.Option[lnwallet.AuxLeafStore] + // BlockCache is the main cache for storing block information. BlockCache *blockcache.BlockCache diff --git a/config_builder.go b/config_builder.go index bef59b9a04..7c399297e7 100644 --- a/config_builder.go +++ b/config_builder.go @@ -105,7 +105,7 @@ type DatabaseBuilder interface { type WalletConfigBuilder interface { // BuildWalletConfig is responsible for creating or unlocking and then // fully initializing a wallet. - BuildWalletConfig(context.Context, *DatabaseInstances, + BuildWalletConfig(context.Context, *DatabaseInstances, *AuxComponents, *rpcperms.InterceptorChain, []*ListenerWithSignal) (*chainreg.PartialChainControl, *btcwallet.Config, func(), error) @@ -120,14 +120,6 @@ type ChainControlBuilder interface { *btcwallet.Config) (*chainreg.ChainControl, func(), error) } -// AuxComponents is a set of auxiliary components that can be used by lnd for -// certain custom channel types. -type AuxComponents struct { - // MsgRouter is an optional message router that if set will be used in - // place of a new blank default message router. - MsgRouter fn.Option[msgmux.Router] -} - // ImplementationCfg is a struct that holds all configuration items for // components that can be implemented outside lnd itself. type ImplementationCfg struct { @@ -160,6 +152,18 @@ type ImplementationCfg struct { AuxComponents } +// AuxComponents is a set of auxiliary components that can be used by lnd for +// certain custom channel types. +type AuxComponents struct { + // AuxLeafStore is an optional data source that can be used by custom + // channels to fetch+store various data. + AuxLeafStore fn.Option[lnwallet.AuxLeafStore] + + // MsgRouter is an optional message router that if set will be used in + // place of a new blank default message router. + MsgRouter fn.Option[msgmux.Router] +} + // DefaultWalletImpl is the default implementation of our normal, btcwallet // backed configuration. type DefaultWalletImpl struct { @@ -242,7 +246,8 @@ func (d *DefaultWalletImpl) Permissions() map[string][]bakery.Op { // // NOTE: This is part of the WalletConfigBuilder interface. func (d *DefaultWalletImpl) BuildWalletConfig(ctx context.Context, - dbs *DatabaseInstances, interceptorChain *rpcperms.InterceptorChain, + dbs *DatabaseInstances, aux *AuxComponents, + interceptorChain *rpcperms.InterceptorChain, grpcListeners []*ListenerWithSignal) (*chainreg.PartialChainControl, *btcwallet.Config, func(), error) { @@ -562,6 +567,7 @@ func (d *DefaultWalletImpl) BuildWalletConfig(ctx context.Context, HeightHintDB: dbs.HeightHintDB, ChanStateDB: dbs.ChanStateDB.ChannelStateDB(), NeutrinoCS: neutrinoCS, + AuxLeafStore: aux.AuxLeafStore, ActiveNetParams: d.cfg.ActiveNetParams, FeeURL: d.cfg.FeeURL, Fee: &lncfg.Fee{ @@ -625,8 +631,9 @@ func (d *DefaultWalletImpl) BuildWalletConfig(ctx context.Context, // proxyBlockEpoch proxies a block epoch subsections to the underlying neutrino // rebroadcaster client. -func proxyBlockEpoch(notifier chainntnfs.ChainNotifier, -) func() (*blockntfns.Subscription, error) { +func proxyBlockEpoch( + notifier chainntnfs.ChainNotifier) func() (*blockntfns.Subscription, + error) { return func() (*blockntfns.Subscription, error) { blockEpoch, err := notifier.RegisterBlockEpochNtfn( @@ -717,6 +724,7 @@ func (d *DefaultWalletImpl) BuildChainControl( ChainIO: walletController, NetParams: *walletConfig.NetParams, CoinSelectionStrategy: walletConfig.CoinSelectionStrategy, + AuxLeafStore: partialChainControl.Cfg.AuxLeafStore, } // The broadcast is already always active for neutrino nodes, so we @@ -899,6 +907,10 @@ type DatabaseInstances struct { // for native SQL queries for tables that already support it. This may // be nil if the use-native-sql flag was not set. NativeSQLStore *sqldb.BaseDB + + // AuxLeafStore is an optional data source that can be used by custom + // channels to fetch+store various data. + AuxLeafStore fn.Option[lnwallet.AuxLeafStore] } // DefaultDatabaseBuilder is a type that builds the default database backends diff --git a/contractcourt/breach_arbitrator_test.go b/contractcourt/breach_arbitrator_test.go index 6a1865444b..5940ee25bd 100644 --- a/contractcourt/breach_arbitrator_test.go +++ b/contractcourt/breach_arbitrator_test.go @@ -22,6 +22,7 @@ import ( "github.com/go-errors/errors" "github.com/lightningnetwork/lnd/chainntnfs" "github.com/lightningnetwork/lnd/channeldb" + "github.com/lightningnetwork/lnd/fn" "github.com/lightningnetwork/lnd/input" "github.com/lightningnetwork/lnd/keychain" "github.com/lightningnetwork/lnd/lntest/channels" @@ -1590,6 +1591,7 @@ func testBreachSpends(t *testing.T, test breachTest) { // Notify the breach arbiter about the breach. retribution, err := lnwallet.NewBreachRetribution( alice.State(), height, 1, forceCloseTx, + fn.Some[lnwallet.AuxLeafStore](&lnwallet.MockAuxLeafStore{}), ) require.NoError(t, err, "unable to create breach retribution") @@ -1799,6 +1801,7 @@ func TestBreachDelayedJusticeConfirmation(t *testing.T) { // Notify the breach arbiter about the breach. retribution, err := lnwallet.NewBreachRetribution( alice.State(), height, uint32(blockHeight), forceCloseTx, + fn.Some[lnwallet.AuxLeafStore](&lnwallet.MockAuxLeafStore{}), ) require.NoError(t, err, "unable to create breach retribution") diff --git a/contractcourt/chain_arbitrator.go b/contractcourt/chain_arbitrator.go index 0cc4b111a5..dbc97939a9 100644 --- a/contractcourt/chain_arbitrator.go +++ b/contractcourt/chain_arbitrator.go @@ -217,6 +217,10 @@ type ChainArbitratorConfig struct { // meanwhile, turn `PaymentCircuit` into an interface or bring it to a // lower package. QueryIncomingCircuit func(circuit models.CircuitKey) *models.CircuitKey + + // AuxLeafStore is an optional store that can be used to store auxiliary + // leaves for certain custom channel types. + AuxLeafStore fn.Option[lnwallet.AuxLeafStore] } // ChainArbitrator is a sub-system that oversees the on-chain resolution of all @@ -299,8 +303,13 @@ func (a *arbChannel) NewAnchorResolutions() (*lnwallet.AnchorResolutions, return nil, err } + var chanOpts []lnwallet.ChannelOpt + a.c.cfg.AuxLeafStore.WhenSome(func(s lnwallet.AuxLeafStore) { + chanOpts = append(chanOpts, lnwallet.WithLeafStore(s)) + }) + chanMachine, err := lnwallet.NewLightningChannel( - a.c.cfg.Signer, channel, nil, + a.c.cfg.Signer, channel, nil, chanOpts..., ) if err != nil { return nil, err @@ -344,10 +353,15 @@ func (a *arbChannel) ForceCloseChan() (*lnwallet.LocalForceCloseSummary, error) return nil, err } + var chanOpts []lnwallet.ChannelOpt + a.c.cfg.AuxLeafStore.WhenSome(func(s lnwallet.AuxLeafStore) { + chanOpts = append(chanOpts, lnwallet.WithLeafStore(s)) + }) + // Finally, we'll force close the channel completing // the force close workflow. chanMachine, err := lnwallet.NewLightningChannel( - a.c.cfg.Signer, channel, nil, + a.c.cfg.Signer, channel, nil, chanOpts..., ) if err != nil { return nil, err diff --git a/contractcourt/chain_watcher.go b/contractcourt/chain_watcher.go index 66a61fe3b2..4012f031d8 100644 --- a/contractcourt/chain_watcher.go +++ b/contractcourt/chain_watcher.go @@ -193,6 +193,9 @@ type chainWatcherConfig struct { // obfuscater. This is used by the chain watcher to identify which // state was broadcast and confirmed on-chain. extractStateNumHint func(*wire.MsgTx, [lnwallet.StateHintSize]byte) uint64 + + // auxLeafStore can be used to fetch information for custom channels. + auxLeafStore fn.Option[lnwallet.AuxLeafStore] } // chainWatcher is a system that's assigned to every active channel. The duty @@ -867,7 +870,7 @@ func (c *chainWatcher) handlePossibleBreach(commitSpend *chainntnfs.SpendDetail, spendHeight := uint32(commitSpend.SpendingHeight) retribution, err := lnwallet.NewBreachRetribution( c.cfg.chanState, broadcastStateNum, spendHeight, - commitSpend.SpendingTx, + commitSpend.SpendingTx, c.cfg.auxLeafStore, ) switch { @@ -1117,8 +1120,8 @@ func (c *chainWatcher) dispatchLocalForceClose( "detected", c.cfg.chanState.FundingOutpoint) forceClose, err := lnwallet.NewLocalForceCloseSummary( - c.cfg.chanState, c.cfg.signer, - commitSpend.SpendingTx, stateNum, + c.cfg.chanState, c.cfg.signer, commitSpend.SpendingTx, stateNum, + c.cfg.auxLeafStore, ) if err != nil { return err @@ -1211,7 +1214,7 @@ func (c *chainWatcher) dispatchRemoteForceClose( // channel on-chain. uniClose, err := lnwallet.NewUnilateralCloseSummary( c.cfg.chanState, c.cfg.signer, commitSpend, - remoteCommit, commitPoint, + remoteCommit, commitPoint, c.cfg.auxLeafStore, ) if err != nil { return err diff --git a/funding/manager.go b/funding/manager.go index b22f2e6919..6bb027725d 100644 --- a/funding/manager.go +++ b/funding/manager.go @@ -24,6 +24,7 @@ import ( "github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/channeldb/models" "github.com/lightningnetwork/lnd/discovery" + "github.com/lightningnetwork/lnd/fn" "github.com/lightningnetwork/lnd/graph" "github.com/lightningnetwork/lnd/input" "github.com/lightningnetwork/lnd/keychain" @@ -544,6 +545,10 @@ type Config struct { // backed funding flow to not use utxos still being swept by the sweeper // subsystem. IsSweeperOutpoint func(wire.OutPoint) bool + + // AuxLeafStore is an optional store that can be used to store auxiliary + // leaves for certain custom channel types. + AuxLeafStore fn.Option[lnwallet.AuxLeafStore] } // Manager acts as an orchestrator/bridge between the wallet's @@ -1069,9 +1074,14 @@ func (f *Manager) advanceFundingState(channel *channeldb.OpenChannel, } } + var chanOpts []lnwallet.ChannelOpt + f.cfg.AuxLeafStore.WhenSome(func(s lnwallet.AuxLeafStore) { + chanOpts = append(chanOpts, lnwallet.WithLeafStore(s)) + }) + // We create the state-machine object which wraps the database state. lnChannel, err := lnwallet.NewLightningChannel( - nil, channel, nil, + nil, channel, nil, chanOpts..., ) if err != nil { log.Errorf("Unable to create LightningChannel(%v): %v", diff --git a/funding/manager_test.go b/funding/manager_test.go index c4c8b4f36c..471d0209fb 100644 --- a/funding/manager_test.go +++ b/funding/manager_test.go @@ -28,6 +28,7 @@ import ( "github.com/lightningnetwork/lnd/channeldb/models" "github.com/lightningnetwork/lnd/channelnotifier" "github.com/lightningnetwork/lnd/discovery" + "github.com/lightningnetwork/lnd/fn" "github.com/lightningnetwork/lnd/input" "github.com/lightningnetwork/lnd/keychain" "github.com/lightningnetwork/lnd/lncfg" @@ -563,6 +564,9 @@ func createTestFundingManager(t *testing.T, privKey *btcec.PrivateKey, IsSweeperOutpoint: func(wire.OutPoint) bool { return false }, + AuxLeafStore: fn.Some[lnwallet.AuxLeafStore]( + &lnwallet.MockAuxLeafStore{}, + ), } for _, op := range options { @@ -672,6 +676,7 @@ func recreateAliceFundingManager(t *testing.T, alice *testNode) { OpenChannelPredicate: chainedAcceptor, DeleteAliasEdge: oldCfg.DeleteAliasEdge, AliasManager: oldCfg.AliasManager, + AuxLeafStore: oldCfg.AuxLeafStore, }) require.NoError(t, err, "failed recreating aliceFundingManager") diff --git a/lnd.go b/lnd.go index e483d5512f..da7747e91d 100644 --- a/lnd.go +++ b/lnd.go @@ -456,7 +456,8 @@ func Main(cfg *Config, lisCfg ListenerCfg, implCfg *ImplementationCfg, defer cleanUp() partialChainControl, walletConfig, cleanUp, err := implCfg.BuildWalletConfig( - ctx, dbs, interceptorChain, grpcListeners, + ctx, dbs, &implCfg.AuxComponents, interceptorChain, + grpcListeners, ) if err != nil { return mkErr("error creating wallet config: %v", err) diff --git a/lnwallet/channel.go b/lnwallet/channel.go index 87a4702073..b5ef71c154 100644 --- a/lnwallet/channel.go +++ b/lnwallet/channel.go @@ -1965,7 +1965,8 @@ type BreachRetribution struct { // required to construct the BreachRetribution. If the revocation log is missing // the required fields then ErrRevLogDataMissing will be returned. func NewBreachRetribution(chanState *channeldb.OpenChannel, stateNum uint64, - breachHeight uint32, spendTx *wire.MsgTx) (*BreachRetribution, error) { + breachHeight uint32, spendTx *wire.MsgTx, + leafStore fn.Option[AuxLeafStore]) (*BreachRetribution, error) { // Query the on-disk revocation log for the snapshot which was recorded // at this particular state num. Based on whether a legacy revocation @@ -3023,9 +3024,16 @@ func processFeeUpdate(feeUpdate *PaymentDescriptor, nextHeight uint64, // signature can be submitted to the sigPool to generate all the signatures // asynchronously and in parallel. func genRemoteHtlcSigJobs(keyRing *CommitmentKeyRing, - chanType channeldb.ChannelType, isRemoteInitiator bool, - leaseExpiry uint32, localChanCfg, remoteChanCfg *channeldb.ChannelConfig, - remoteCommitView *commitment) ([]SignJob, chan struct{}, error) { + chanState *channeldb.OpenChannel, leaseExpiry uint32, + remoteCommitView *commitment, + leafStore fn.Option[AuxLeafStore]) ([]SignJob, chan struct{}, error) { + + var ( + isRemoteInitiator = !chanState.IsInitiator + localChanCfg = chanState.LocalChanCfg + remoteChanCfg = chanState.RemoteChanCfg + chanType = chanState.ChanType + ) txHash := remoteCommitView.txn.TxHash() dustLimit := remoteChanCfg.DustLimit @@ -3191,9 +3199,9 @@ func genRemoteHtlcSigJobs(keyRing *CommitmentKeyRing, // validate this new state. This function is called right before sending the // new commitment to the remote party. The commit diff returned contains all // information necessary for retransmission. -func (lc *LightningChannel) createCommitDiff( - newCommit *commitment, commitSig lnwire.Sig, - htlcSigs []lnwire.Sig) (*channeldb.CommitDiff, error) { +func (lc *LightningChannel) createCommitDiff(newCommit *commitment, + commitSig lnwire.Sig, htlcSigs []lnwire.Sig) (*channeldb.CommitDiff, + error) { // First, we need to convert the funding outpoint into the ID that's // used on the wire to identify this channel. We'll use this shortly @@ -3892,9 +3900,8 @@ func (lc *LightningChannel) SignNextCommitment() (*NewCommitState, error) { leaseExpiry = lc.channelState.ThawHeight } sigBatch, cancelChan, err := genRemoteHtlcSigJobs( - keyRing, lc.channelState.ChanType, !lc.channelState.IsInitiator, - leaseExpiry, &lc.channelState.LocalChanCfg, - &lc.channelState.RemoteChanCfg, newCommitView, + keyRing, lc.channelState, leaseExpiry, newCommitView, + lc.leafStore, ) if err != nil { return nil, err @@ -4497,10 +4504,18 @@ func (lc *LightningChannel) computeView(view *HtlcView, // meant to verify all the signatures for HTLC's attached to a newly created // commitment state. The jobs generated are fully populated, and can be sent // directly into the pool of workers. -func genHtlcSigValidationJobs(localCommitmentView *commitment, - keyRing *CommitmentKeyRing, htlcSigs []lnwire.Sig, - chanType channeldb.ChannelType, isLocalInitiator bool, leaseExpiry uint32, - localChanCfg, remoteChanCfg *channeldb.ChannelConfig) ([]VerifyJob, error) { +// +//nolint:funlen +func genHtlcSigValidationJobs(chanState *channeldb.OpenChannel, + localCommitmentView *commitment, keyRing *CommitmentKeyRing, + htlcSigs []lnwire.Sig, leaseExpiry uint32, + leafStore fn.Option[AuxLeafStore]) ([]VerifyJob, error) { + + var ( + isLocalInitiator = chanState.IsInitiator + localChanCfg = chanState.LocalChanCfg + chanType = chanState.ChanType + ) txHash := localCommitmentView.txn.TxHash() feePerKw := localCommitmentView.feePerKw @@ -4890,10 +4905,8 @@ func (lc *LightningChannel) ReceiveNewCommitment(commitSigs *CommitSigs) error { leaseExpiry = lc.channelState.ThawHeight } verifyJobs, err := genHtlcSigValidationJobs( - localCommitmentView, keyRing, commitSigs.HtlcSigs, - lc.channelState.ChanType, lc.channelState.IsInitiator, - leaseExpiry, &lc.channelState.LocalChanCfg, - &lc.channelState.RemoteChanCfg, + lc.channelState, localCommitmentView, keyRing, + commitSigs.HtlcSigs, leaseExpiry, lc.leafStore, ) if err != nil { return err @@ -6340,10 +6353,10 @@ type UnilateralCloseSummary struct { // happen in case we have lost state) it should be set to an empty struct, in // which case we will attempt to sweep the non-HTLC output using the passed // commitPoint. -func NewUnilateralCloseSummary(chanState *channeldb.OpenChannel, signer input.Signer, - commitSpend *chainntnfs.SpendDetail, - remoteCommit channeldb.ChannelCommitment, - commitPoint *btcec.PublicKey) (*UnilateralCloseSummary, error) { +func NewUnilateralCloseSummary(chanState *channeldb.OpenChannel, + signer input.Signer, commitSpend *chainntnfs.SpendDetail, + remoteCommit channeldb.ChannelCommitment, commitPoint *btcec.PublicKey, + leafStore fn.Option[AuxLeafStore]) (*UnilateralCloseSummary, error) { // First, we'll generate the commitment point and the revocation point // so we can re-construct the HTLC state and also our payment key. @@ -7286,7 +7299,7 @@ func (lc *LightningChannel) ForceClose() (*LocalForceCloseSummary, error) { localCommitment := lc.channelState.LocalCommitment summary, err := NewLocalForceCloseSummary( lc.channelState, lc.Signer, commitTx, - localCommitment.CommitHeight, + localCommitment.CommitHeight, lc.leafStore, ) if err != nil { return nil, fmt.Errorf("unable to gen force close "+ @@ -7303,8 +7316,8 @@ func (lc *LightningChannel) ForceClose() (*LocalForceCloseSummary, error) { // channel state. The passed commitTx must be a fully signed commitment // transaction corresponding to localCommit. func NewLocalForceCloseSummary(chanState *channeldb.OpenChannel, - signer input.Signer, commitTx *wire.MsgTx, stateNum uint64) ( - *LocalForceCloseSummary, error) { + signer input.Signer, commitTx *wire.MsgTx, stateNum uint64, + leafStore fn.Option[AuxLeafStore]) (*LocalForceCloseSummary, error) { // Re-derive the original pkScript for to-self output within the // commitment transaction. We'll need this to find the corresponding diff --git a/lnwallet/channel_test.go b/lnwallet/channel_test.go index 0be7be1b4f..8bdf45aa8e 100644 --- a/lnwallet/channel_test.go +++ b/lnwallet/channel_test.go @@ -5793,6 +5793,7 @@ func TestChannelUnilateralCloseHtlcResolution(t *testing.T) { spendDetail, aliceChannel.channelState.RemoteCommitment, aliceChannel.channelState.RemoteCurrentRevocation, + fn.Some[AuxLeafStore](&MockAuxLeafStore{}), ) require.NoError(t, err, "unable to create alice close summary") @@ -5942,6 +5943,7 @@ func TestChannelUnilateralClosePendingCommit(t *testing.T) { spendDetail, aliceChannel.channelState.RemoteCommitment, aliceChannel.channelState.RemoteCurrentRevocation, + fn.Some[AuxLeafStore](&MockAuxLeafStore{}), ) require.NoError(t, err, "unable to create alice close summary") @@ -5959,6 +5961,7 @@ func TestChannelUnilateralClosePendingCommit(t *testing.T) { spendDetail, aliceRemoteChainTip.Commitment, aliceChannel.channelState.RemoteNextRevocation, + fn.Some[AuxLeafStore](&MockAuxLeafStore{}), ) require.NoError(t, err, "unable to create alice close summary") @@ -6839,6 +6842,7 @@ func TestNewBreachRetributionSkipsDustHtlcs(t *testing.T) { breachTx := aliceChannel.channelState.RemoteCommitment.CommitTx breachRet, err := NewBreachRetribution( aliceChannel.channelState, revokedStateNum, 100, breachTx, + fn.Some[AuxLeafStore](&MockAuxLeafStore{}), ) require.NoError(t, err, "unable to create breach retribution") @@ -10387,6 +10391,7 @@ func testNewBreachRetribution(t *testing.T, chanType channeldb.ChannelType) { // error as there are no past delta state saved as revocation logs yet. _, err = NewBreachRetribution( aliceChannel.channelState, stateNum, breachHeight, breachTx, + fn.None[AuxLeafStore](), ) require.ErrorIs(t, err, channeldb.ErrNoPastDeltas) @@ -10394,6 +10399,7 @@ func testNewBreachRetribution(t *testing.T, chanType channeldb.ChannelType) { // provided. _, err = NewBreachRetribution( aliceChannel.channelState, stateNum, breachHeight, nil, + fn.None[AuxLeafStore](), ) require.ErrorIs(t, err, channeldb.ErrNoPastDeltas) @@ -10439,6 +10445,7 @@ func testNewBreachRetribution(t *testing.T, chanType channeldb.ChannelType) { // successfully. br, err := NewBreachRetribution( aliceChannel.channelState, stateNum, breachHeight, breachTx, + fn.Some[AuxLeafStore](&MockAuxLeafStore{}), ) require.NoError(t, err) @@ -10450,6 +10457,7 @@ func testNewBreachRetribution(t *testing.T, chanType channeldb.ChannelType) { // since the necessary info should now be found in the revocation log. br, err = NewBreachRetribution( aliceChannel.channelState, stateNum, breachHeight, nil, + fn.Some[AuxLeafStore](&MockAuxLeafStore{}), ) require.NoError(t, err) assertRetribution(br, 1, 0) @@ -10458,6 +10466,7 @@ func testNewBreachRetribution(t *testing.T, chanType channeldb.ChannelType) { // error. _, err = NewBreachRetribution( aliceChannel.channelState, stateNum+1, breachHeight, breachTx, + fn.Some[AuxLeafStore](&MockAuxLeafStore{}), ) require.ErrorIs(t, err, channeldb.ErrLogEntryNotFound) @@ -10465,6 +10474,7 @@ func testNewBreachRetribution(t *testing.T, chanType channeldb.ChannelType) { // provided. _, err = NewBreachRetribution( aliceChannel.channelState, stateNum+1, breachHeight, nil, + fn.Some[AuxLeafStore](&MockAuxLeafStore{}), ) require.ErrorIs(t, err, channeldb.ErrLogEntryNotFound) } diff --git a/lnwallet/config.go b/lnwallet/config.go index 7eeacb6ea2..24961f38ed 100644 --- a/lnwallet/config.go +++ b/lnwallet/config.go @@ -5,6 +5,7 @@ import ( "github.com/btcsuite/btcwallet/wallet" "github.com/lightningnetwork/lnd/chainntnfs" "github.com/lightningnetwork/lnd/channeldb" + "github.com/lightningnetwork/lnd/fn" "github.com/lightningnetwork/lnd/input" "github.com/lightningnetwork/lnd/keychain" "github.com/lightningnetwork/lnd/lnwallet/chainfee" @@ -62,4 +63,8 @@ type Config struct { // CoinSelectionStrategy is the strategy that is used for selecting // coins when funding a transaction. CoinSelectionStrategy wallet.CoinSelectionStrategy + + // AuxLeafStore is an optional store that can be used to store auxiliary + // leaves for certain custom channel types. + AuxLeafStore fn.Option[AuxLeafStore] } diff --git a/lnwallet/mock.go b/lnwallet/mock.go index e6bd0b2e41..591ec285ab 100644 --- a/lnwallet/mock.go +++ b/lnwallet/mock.go @@ -17,8 +17,10 @@ import ( "github.com/btcsuite/btcwallet/wallet/txauthor" "github.com/btcsuite/btcwallet/wtxmgr" "github.com/lightningnetwork/lnd/chainntnfs" + "github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/fn" "github.com/lightningnetwork/lnd/lnwallet/chainfee" + "github.com/lightningnetwork/lnd/tlv" ) var ( @@ -389,3 +391,45 @@ func (*mockChainIO) GetBlockHeader( return nil, nil } + +type MockAuxLeafStore struct{} + +// A compile time check to ensure that MockAuxLeafStore implements the +// AuxLeafStore interface. +var _ AuxLeafStore = (*MockAuxLeafStore)(nil) + +// FetchLeavesFromView attempts to fetch the auxiliary leaves that +// correspond to the passed aux blob, and pending original (unfiltered) +// HTLC view. +func (*MockAuxLeafStore) FetchLeavesFromView( + _ CommitDiffAuxInput) fn.Result[CommitDiffAuxResult] { + + return fn.Ok(CommitDiffAuxResult{}) +} + +// FetchLeavesFromCommit attempts to fetch the auxiliary leaves that +// correspond to the passed aux blob, and an existing channel +// commitment. +func (*MockAuxLeafStore) FetchLeavesFromCommit(_ AuxChanState, + _ channeldb.ChannelCommitment, + _ CommitmentKeyRing) fn.Result[CommitDiffAuxResult] { + + return fn.Ok(CommitDiffAuxResult{}) +} + +// FetchLeavesFromRevocation attempts to fetch the auxiliary leaves +// from a channel revocation that stores balance + blob information. +func (*MockAuxLeafStore) FetchLeavesFromRevocation( + _ *channeldb.RevocationLog) fn.Result[CommitDiffAuxResult] { + + return fn.Ok(CommitDiffAuxResult{}) +} + +// ApplyHtlcView serves as the state transition function for the custom +// channel's blob. Given the old blob, and an HTLC view, then a new +// blob should be returned that reflects the pending updates. +func (*MockAuxLeafStore) ApplyHtlcView( + _ CommitDiffAuxInput) fn.Result[fn.Option[tlv.Blob]] { + + return fn.Ok(fn.None[tlv.Blob]()) +} diff --git a/lnwallet/wallet.go b/lnwallet/wallet.go index 39748e7e81..648c5b9ce4 100644 --- a/lnwallet/wallet.go +++ b/lnwallet/wallet.go @@ -2496,9 +2496,16 @@ func initStateHints(commit1, commit2 *wire.MsgTx, func (l *LightningWallet) ValidateChannel(channelState *channeldb.OpenChannel, fundingTx *wire.MsgTx) error { + var chanOpts []ChannelOpt + l.Cfg.AuxLeafStore.WhenSome(func(s AuxLeafStore) { + chanOpts = append(chanOpts, WithLeafStore(s)) + }) + // First, we'll obtain a fully signed commitment transaction so we can // pass into it on the chanvalidate package for verification. - channel, err := NewLightningChannel(l.Cfg.Signer, channelState, nil) + channel, err := NewLightningChannel( + l.Cfg.Signer, channelState, nil, chanOpts..., + ) if err != nil { return err } diff --git a/peer/brontide.go b/peer/brontide.go index 25c7cea6f2..1324044da3 100644 --- a/peer/brontide.go +++ b/peer/brontide.go @@ -372,6 +372,10 @@ type Config struct { AddLocalAlias func(alias, base lnwire.ShortChannelID, gossip bool) error + // AuxLeafStore is an optional store that can be used to store auxiliary + // leaves for certain custom channel types. + AuxLeafStore fn.Option[lnwallet.AuxLeafStore] + // PongBuf is a slice we'll reuse instead of allocating memory on the // heap. Since only reads will occur and no writes, there is no need // for any synchronization primitives. As a result, it's safe to share @@ -943,8 +947,12 @@ func (p *Brontide) loadActiveChannels(chans []*channeldb.OpenChannel) ( } } + var chanOpts []lnwallet.ChannelOpt + p.cfg.AuxLeafStore.WhenSome(func(s lnwallet.AuxLeafStore) { + chanOpts = append(chanOpts, lnwallet.WithLeafStore(s)) + }) lnChan, err := lnwallet.NewLightningChannel( - p.cfg.Signer, dbChan, p.cfg.SigPool, + p.cfg.Signer, dbChan, p.cfg.SigPool, chanOpts..., ) if err != nil { return nil, fmt.Errorf("unable to create channel "+ @@ -4151,6 +4159,10 @@ func (p *Brontide) addActiveChannel(c *lnpeer.NewChannel) error { chanOpts = append(chanOpts, lnwallet.WithSkipNonceInit()) } + p.cfg.AuxLeafStore.WhenSome(func(s lnwallet.AuxLeafStore) { + chanOpts = append(chanOpts, lnwallet.WithLeafStore(s)) + }) + // If not already active, we'll add this channel to the set of active // channels, so we can look it up later easily according to its channel // ID. diff --git a/server.go b/server.go index cc4c45e832..21c65399e2 100644 --- a/server.go +++ b/server.go @@ -1272,6 +1272,7 @@ func newServer(cfg *Config, listenAddrs []net.Addr, return &pc.Incoming }, + AuxLeafStore: implCfg.AuxLeafStore, }, dbs.ChanStateDB) // Select the configuration and funding parameters for Bitcoin. @@ -1606,6 +1607,7 @@ func newServer(cfg *Config, listenAddrs []net.Addr, br, err := lnwallet.NewBreachRetribution( channel, commitHeight, 0, nil, + implCfg.AuxLeafStore, ) if err != nil { return nil, 0, err @@ -4043,6 +4045,7 @@ func (s *server) peerConnected(conn net.Conn, connReq *connmgr.ConnReq, DisallowRouteBlinding: s.cfg.ProtocolOptions.NoRouteBlinding(), MaxFeeExposure: thresholdMSats, Quit: s.quit, + AuxLeafStore: s.implCfg.AuxLeafStore, MsgRouter: s.implCfg.MsgRouter, } From 860cacb70a8faf0bee6e05f43937cee229c07653 Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Fri, 23 Aug 2024 15:13:19 +0200 Subject: [PATCH 044/218] lnwallet: refactor commit keys to use lntypes.Dual --- lnwallet/channel.go | 30 ++++++++++++++---------------- lnwallet/channel_test.go | 2 +- 2 files changed, 15 insertions(+), 17 deletions(-) diff --git a/lnwallet/channel.go b/lnwallet/channel.go index b5ef71c154..875903e929 100644 --- a/lnwallet/channel.go +++ b/lnwallet/channel.go @@ -575,9 +575,8 @@ func (c *commitment) toDiskCommit( // restore commitment state written to disk back into memory once we need to // restart a channel session. func (lc *LightningChannel) diskHtlcToPayDesc(feeRate chainfee.SatPerKWeight, - htlc *channeldb.HTLC, localCommitKeys *CommitmentKeyRing, - remoteCommitKeys *CommitmentKeyRing, whoseCommit lntypes.ChannelParty, -) (PaymentDescriptor, error) { + htlc *channeldb.HTLC, commitKeys lntypes.Dual[*CommitmentKeyRing], + whoseCommit lntypes.ChannelParty) (PaymentDescriptor, error) { // The proper pkScripts for this PaymentDescriptor must be // generated so we can easily locate them within the commitment @@ -598,6 +597,7 @@ func (lc *LightningChannel) diskHtlcToPayDesc(feeRate chainfee.SatPerKWeight, chanType, htlc.Incoming, lntypes.Local, feeRate, htlc.Amt.ToSatoshis(), lc.channelState.LocalChanCfg.DustLimit, ) + localCommitKeys := commitKeys.GetForParty(lntypes.Local) if !isDustLocal && localCommitKeys != nil { scriptInfo, err := genHtlcScript( chanType, htlc.Incoming, lntypes.Local, @@ -613,6 +613,7 @@ func (lc *LightningChannel) diskHtlcToPayDesc(feeRate chainfee.SatPerKWeight, chanType, htlc.Incoming, lntypes.Remote, feeRate, htlc.Amt.ToSatoshis(), lc.channelState.RemoteChanCfg.DustLimit, ) + remoteCommitKeys := commitKeys.GetForParty(lntypes.Remote) if !isDustRemote && remoteCommitKeys != nil { scriptInfo, err := genHtlcScript( chanType, htlc.Incoming, lntypes.Remote, @@ -665,9 +666,9 @@ func (lc *LightningChannel) diskHtlcToPayDesc(feeRate chainfee.SatPerKWeight, // these payment descriptors can be re-inserted into the in-memory updateLog // for each side. func (lc *LightningChannel) extractPayDescs(feeRate chainfee.SatPerKWeight, - htlcs []channeldb.HTLC, localCommitKeys *CommitmentKeyRing, - remoteCommitKeys *CommitmentKeyRing, whoseCommit lntypes.ChannelParty, -) ([]PaymentDescriptor, []PaymentDescriptor, error) { + htlcs []channeldb.HTLC, commitKeys lntypes.Dual[*CommitmentKeyRing], + whoseCommit lntypes.ChannelParty) ([]PaymentDescriptor, + []PaymentDescriptor, error) { var ( incomingHtlcs []PaymentDescriptor @@ -685,9 +686,7 @@ func (lc *LightningChannel) extractPayDescs(feeRate chainfee.SatPerKWeight, htlc := htlc payDesc, err := lc.diskHtlcToPayDesc( - feeRate, &htlc, - localCommitKeys, remoteCommitKeys, - whoseCommit, + feeRate, &htlc, commitKeys, whoseCommit, ) if err != nil { return incomingHtlcs, outgoingHtlcs, err @@ -716,22 +715,22 @@ func (lc *LightningChannel) diskCommitToMemCommit( // (we extended but weren't able to complete the commitment dance // before shutdown), then the localCommitPoint won't be set as we // haven't yet received a responding commitment from the remote party. - var localCommitKeys, remoteCommitKeys *CommitmentKeyRing + var commitKeys lntypes.Dual[*CommitmentKeyRing] if localCommitPoint != nil { - localCommitKeys = DeriveCommitmentKeys( + commitKeys.SetForParty(lntypes.Local, DeriveCommitmentKeys( localCommitPoint, lntypes.Local, lc.channelState.ChanType, &lc.channelState.LocalChanCfg, &lc.channelState.RemoteChanCfg, - ) + )) } if remoteCommitPoint != nil { - remoteCommitKeys = DeriveCommitmentKeys( + commitKeys.SetForParty(lntypes.Remote, DeriveCommitmentKeys( remoteCommitPoint, lntypes.Remote, lc.channelState.ChanType, &lc.channelState.LocalChanCfg, &lc.channelState.RemoteChanCfg, - ) + )) } // With the key rings re-created, we'll now convert all the on-disk @@ -739,8 +738,7 @@ func (lc *LightningChannel) diskCommitToMemCommit( // update log. incomingHtlcs, outgoingHtlcs, err := lc.extractPayDescs( chainfee.SatPerKWeight(diskCommit.FeePerKw), - diskCommit.Htlcs, localCommitKeys, remoteCommitKeys, - whoseCommit, + diskCommit.Htlcs, commitKeys, whoseCommit, ) if err != nil { return nil, err diff --git a/lnwallet/channel_test.go b/lnwallet/channel_test.go index 8bdf45aa8e..a09f97bfe8 100644 --- a/lnwallet/channel_test.go +++ b/lnwallet/channel_test.go @@ -10512,7 +10512,7 @@ func TestExtractPayDescs(t *testing.T) { // NOTE: we use nil commitment key rings to avoid checking the htlc // scripts(`genHtlcScript`) as it should be tested independently. incomingPDs, outgoingPDs, err := lnChan.extractPayDescs( - 0, htlcs, nil, nil, lntypes.Local, + 0, htlcs, lntypes.Dual[*CommitmentKeyRing]{}, lntypes.Local, ) require.NoError(t, err) From b0728647c9a57af1e0e1b8a37f8c7c34d98c4f30 Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Thu, 25 Apr 2024 19:01:37 +0200 Subject: [PATCH 045/218] lnwallet: thread thru input.AuxTapleaf to all relevant areas In this commit, we start to thread thru the new aux tap leaf structures to all relevant areas. This includes: commitment outputs, resolution creation, breach handling, and also HTLC scripts. --- contractcourt/chain_watcher.go | 30 +++- input/size_test.go | 4 +- lnwallet/channel.go | 304 ++++++++++++++++++++++++++++----- lnwallet/channel_test.go | 8 +- lnwallet/commitment.go | 63 ++++--- lnwallet/transactions.go | 12 +- 6 files changed, 345 insertions(+), 76 deletions(-) diff --git a/contractcourt/chain_watcher.go b/contractcourt/chain_watcher.go index 4012f031d8..b1e3fc1c28 100644 --- a/contractcourt/chain_watcher.go +++ b/contractcourt/chain_watcher.go @@ -426,15 +426,36 @@ func (c *chainWatcher) handleUnknownLocalState( &c.cfg.chanState.LocalChanCfg, &c.cfg.chanState.RemoteChanCfg, ) + auxResult, err := fn.MapOptionZ( + c.cfg.auxLeafStore, + //nolint:lll + func(s lnwallet.AuxLeafStore) fn.Result[lnwallet.CommitDiffAuxResult] { + return s.FetchLeavesFromCommit( + lnwallet.NewAuxChanState(c.cfg.chanState), + c.cfg.chanState.LocalCommitment, *commitKeyRing, + ) + }, + ).Unpack() + if err != nil { + return false, fmt.Errorf("unable to fetch aux leaves: %w", err) + } + // With the keys derived, we'll construct the remote script that'll be // present if they have a non-dust balance on the commitment. var leaseExpiry uint32 if c.cfg.chanState.ChanType.HasLeaseExpiration() { leaseExpiry = c.cfg.chanState.ThawHeight } + + remoteAuxLeaf := fn.ChainOption( + func(l lnwallet.CommitAuxLeaves) input.AuxTapLeaf { + return l.RemoteAuxLeaf + }, + )(auxResult.AuxLeaves) remoteScript, _, err := lnwallet.CommitScriptToRemote( c.cfg.chanState.ChanType, c.cfg.chanState.IsInitiator, - commitKeyRing.ToRemoteKey, leaseExpiry, input.NoneTapLeaf(), + commitKeyRing.ToRemoteKey, leaseExpiry, + remoteAuxLeaf, ) if err != nil { return false, err @@ -443,11 +464,16 @@ func (c *chainWatcher) handleUnknownLocalState( // Next, we'll derive our script that includes the revocation base for // the remote party allowing them to claim this output before the CSV // delay if we breach. + localAuxLeaf := fn.ChainOption( + func(l lnwallet.CommitAuxLeaves) input.AuxTapLeaf { + return l.LocalAuxLeaf + }, + )(auxResult.AuxLeaves) localScript, err := lnwallet.CommitScriptToSelf( c.cfg.chanState.ChanType, c.cfg.chanState.IsInitiator, commitKeyRing.ToLocalKey, commitKeyRing.RevocationKey, uint32(c.cfg.chanState.LocalChanCfg.CsvDelay), leaseExpiry, - input.NoneTapLeaf(), + localAuxLeaf, ) if err != nil { return false, err diff --git a/input/size_test.go b/input/size_test.go index 21e2c06ae4..33f9ff5397 100644 --- a/input/size_test.go +++ b/input/size_test.go @@ -1388,7 +1388,7 @@ func genTimeoutTx(t *testing.T, // Create the unsigned timeout tx. timeoutTx, err := lnwallet.CreateHtlcTimeoutTx( chanType, false, testOutPoint, testAmt, testCLTVExpiry, - testCSVDelay, 0, testPubkey, testPubkey, + testCSVDelay, 0, testPubkey, testPubkey, input.NoneTapLeaf(), ) require.NoError(t, err) @@ -1457,7 +1457,7 @@ func genSuccessTx(t *testing.T, chanType channeldb.ChannelType) *wire.MsgTx { // Create the unsigned success tx. successTx, err := lnwallet.CreateHtlcSuccessTx( chanType, false, testOutPoint, testAmt, testCSVDelay, 0, - testPubkey, testPubkey, + testPubkey, testPubkey, input.NoneTapLeaf(), ) require.NoError(t, err) diff --git a/lnwallet/channel.go b/lnwallet/channel.go index 875903e929..d76c97294b 100644 --- a/lnwallet/channel.go +++ b/lnwallet/channel.go @@ -576,7 +576,8 @@ func (c *commitment) toDiskCommit( // restart a channel session. func (lc *LightningChannel) diskHtlcToPayDesc(feeRate chainfee.SatPerKWeight, htlc *channeldb.HTLC, commitKeys lntypes.Dual[*CommitmentKeyRing], - whoseCommit lntypes.ChannelParty) (PaymentDescriptor, error) { + whoseCommit lntypes.ChannelParty, + auxLeaf input.AuxTapLeaf) (PaymentDescriptor, error) { // The proper pkScripts for this PaymentDescriptor must be // generated so we can easily locate them within the commitment @@ -602,6 +603,7 @@ func (lc *LightningChannel) diskHtlcToPayDesc(feeRate chainfee.SatPerKWeight, scriptInfo, err := genHtlcScript( chanType, htlc.Incoming, lntypes.Local, htlc.RefundTimeout, htlc.RHash, localCommitKeys, + auxLeaf, ) if err != nil { return pd, err @@ -618,6 +620,7 @@ func (lc *LightningChannel) diskHtlcToPayDesc(feeRate chainfee.SatPerKWeight, scriptInfo, err := genHtlcScript( chanType, htlc.Incoming, lntypes.Remote, htlc.RefundTimeout, htlc.RHash, remoteCommitKeys, + auxLeaf, ) if err != nil { return pd, err @@ -667,7 +670,8 @@ func (lc *LightningChannel) diskHtlcToPayDesc(feeRate chainfee.SatPerKWeight, // for each side. func (lc *LightningChannel) extractPayDescs(feeRate chainfee.SatPerKWeight, htlcs []channeldb.HTLC, commitKeys lntypes.Dual[*CommitmentKeyRing], - whoseCommit lntypes.ChannelParty) ([]PaymentDescriptor, + whoseCommit lntypes.ChannelParty, + auxLeaves fn.Option[CommitAuxLeaves]) ([]PaymentDescriptor, []PaymentDescriptor, error) { var ( @@ -685,8 +689,19 @@ func (lc *LightningChannel) extractPayDescs(feeRate chainfee.SatPerKWeight, htlc := htlc + auxLeaf := fn.ChainOption( + func(l CommitAuxLeaves) input.AuxTapLeaf { + leaves := l.OutgoingHtlcLeaves + if htlc.Incoming { + leaves = l.IncomingHtlcLeaves + } + + return leaves[htlc.HtlcIndex].AuxTapLeaf + }, + )(auxLeaves) + payDesc, err := lc.diskHtlcToPayDesc( - feeRate, &htlc, commitKeys, whoseCommit, + feeRate, &htlc, commitKeys, whoseCommit, auxLeaf, ) if err != nil { return incomingHtlcs, outgoingHtlcs, err @@ -733,12 +748,25 @@ func (lc *LightningChannel) diskCommitToMemCommit( )) } + auxResult, err := fn.MapOptionZ( + lc.leafStore, + func(s AuxLeafStore) fn.Result[CommitDiffAuxResult] { + return s.FetchLeavesFromCommit( + NewAuxChanState(lc.channelState), *diskCommit, + *commitKeys.GetForParty(whoseCommit), + ) + }, + ).Unpack() + if err != nil { + return nil, fmt.Errorf("unable to fetch aux leaves: %w", err) + } + // With the key rings re-created, we'll now convert all the on-disk // HTLC"s into PaymentDescriptor's so we can re-insert them into our // update log. incomingHtlcs, outgoingHtlcs, err := lc.extractPayDescs( chainfee.SatPerKWeight(diskCommit.FeePerKw), - diskCommit.Htlcs, commitKeys, whoseCommit, + diskCommit.Htlcs, commitKeys, whoseCommit, auxResult.AuxLeaves, ) if err != nil { return nil, err @@ -1091,7 +1119,8 @@ func (lc *LightningChannel) ResetState() { func (lc *LightningChannel) logUpdateToPayDesc(logUpdate *channeldb.LogUpdate, remoteUpdateLog *updateLog, commitHeight uint64, feeRate chainfee.SatPerKWeight, remoteCommitKeys *CommitmentKeyRing, - remoteDustLimit btcutil.Amount) (*PaymentDescriptor, error) { + remoteDustLimit btcutil.Amount, + auxLeaves fn.Option[CommitAuxLeaves]) (*PaymentDescriptor, error) { // Depending on the type of update message we'll map that to a distinct // PaymentDescriptor instance. @@ -1127,10 +1156,17 @@ func (lc *LightningChannel) logUpdateToPayDesc(logUpdate *channeldb.LogUpdate, feeRate, wireMsg.Amount.ToSatoshis(), remoteDustLimit, ) if !isDustRemote { + auxLeaf := fn.ChainOption( + func(l CommitAuxLeaves) input.AuxTapLeaf { + leaves := l.OutgoingHtlcLeaves + return leaves[pd.HtlcIndex].AuxTapLeaf + }, + )(auxLeaves) + scriptInfo, err := genHtlcScript( lc.channelState.ChanType, false, lntypes.Remote, wireMsg.Expiry, wireMsg.PaymentHash, - remoteCommitKeys, + remoteCommitKeys, auxLeaf, ) if err != nil { return nil, err @@ -1791,6 +1827,19 @@ func (lc *LightningChannel) restorePendingLocalUpdates( pendingCommit := pendingRemoteCommitDiff.Commitment pendingHeight := pendingCommit.CommitHeight + auxResult, err := fn.MapOptionZ( + lc.leafStore, + func(s AuxLeafStore) fn.Result[CommitDiffAuxResult] { + return s.FetchLeavesFromCommit( + NewAuxChanState(lc.channelState), pendingCommit, + *pendingRemoteKeys, + ) + }, + ).Unpack() + if err != nil { + return fmt.Errorf("unable to fetch aux leaves: %w", err) + } + // If we did have a dangling commit, then we'll examine which updates // we included in that state and re-insert them into our update log. for _, logUpdate := range pendingRemoteCommitDiff.LogUpdates { @@ -1801,6 +1850,7 @@ func (lc *LightningChannel) restorePendingLocalUpdates( chainfee.SatPerKWeight(pendingCommit.FeePerKw), pendingRemoteKeys, lc.channelState.RemoteChanCfg.DustLimit, + auxResult.AuxLeaves, ) if err != nil { return err @@ -2007,24 +2057,40 @@ func NewBreachRetribution(chanState *channeldb.OpenChannel, stateNum uint64, leaseExpiry = chanState.ThawHeight } - // TODO(roasbeef): Actually fetch aux leaves (later commits in this PR). + auxResult, err := fn.MapOptionZ( + leafStore, func(s AuxLeafStore) fn.Result[CommitDiffAuxResult] { + return s.FetchLeavesFromRevocation(revokedLog) + }, + ).Unpack() + if err != nil { + return nil, fmt.Errorf("unable to fetch aux leaves: %w", err) + } // Since it is the remote breach we are reconstructing, the output // going to us will be a to-remote script with our local params. + remoteAuxLeaf := fn.ChainOption( + func(l CommitAuxLeaves) input.AuxTapLeaf { + return l.RemoteAuxLeaf + }, + )(auxResult.AuxLeaves) isRemoteInitiator := !chanState.IsInitiator ourScript, ourDelay, err := CommitScriptToRemote( chanState.ChanType, isRemoteInitiator, keyRing.ToRemoteKey, - leaseExpiry, input.NoneTapLeaf(), + leaseExpiry, remoteAuxLeaf, ) if err != nil { return nil, err } + localAuxLeaf := fn.ChainOption( + func(l CommitAuxLeaves) input.AuxTapLeaf { + return l.LocalAuxLeaf + }, + )(auxResult.AuxLeaves) theirDelay := uint32(chanState.RemoteChanCfg.CsvDelay) theirScript, err := CommitScriptToSelf( chanState.ChanType, isRemoteInitiator, keyRing.ToLocalKey, - keyRing.RevocationKey, theirDelay, leaseExpiry, - input.NoneTapLeaf(), + keyRing.RevocationKey, theirDelay, leaseExpiry, localAuxLeaf, ) if err != nil { return nil, err @@ -2042,7 +2108,7 @@ func NewBreachRetribution(chanState *channeldb.OpenChannel, stateNum uint64, if revokedLog != nil { br, ourAmt, theirAmt, err = createBreachRetribution( revokedLog, spendTx, chanState, keyRing, - commitmentSecret, leaseExpiry, + commitmentSecret, leaseExpiry, auxResult.AuxLeaves, ) if err != nil { return nil, err @@ -2176,7 +2242,8 @@ func NewBreachRetribution(chanState *channeldb.OpenChannel, stateNum uint64, func createHtlcRetribution(chanState *channeldb.OpenChannel, keyRing *CommitmentKeyRing, commitHash chainhash.Hash, commitmentSecret *btcec.PrivateKey, leaseExpiry uint32, - htlc *channeldb.HTLCEntry) (HtlcRetribution, error) { + htlc *channeldb.HTLCEntry, + auxLeaves fn.Option[CommitAuxLeaves]) (HtlcRetribution, error) { var emptyRetribution HtlcRetribution @@ -2186,10 +2253,24 @@ func createHtlcRetribution(chanState *channeldb.OpenChannel, // We'll generate the original second level witness script now, as // we'll need it if we're revoking an HTLC output on the remote // commitment transaction, and *they* go to the second level. + secondLevelAuxLeaf := fn.ChainOption( + func(l CommitAuxLeaves) fn.Option[input.AuxTapLeaf] { + return fn.MapOption(func(val uint16) input.AuxTapLeaf { + idx := input.HtlcIndex(val) + + if htlc.Incoming.Val { + leaves := l.IncomingHtlcLeaves[idx] + return leaves.SecondLevelLeaf + } + + return l.OutgoingHtlcLeaves[idx].SecondLevelLeaf + })(htlc.HtlcIndex.ValOpt()) + }, + )(auxLeaves) secondLevelScript, err := SecondLevelHtlcScript( chanState.ChanType, isRemoteInitiator, keyRing.RevocationKey, keyRing.ToLocalKey, theirDelay, - leaseExpiry, + leaseExpiry, fn.FlattenOption(secondLevelAuxLeaf), ) if err != nil { return emptyRetribution, err @@ -2200,9 +2281,24 @@ func createHtlcRetribution(chanState *channeldb.OpenChannel, // HTLC script. Otherwise, is this was an outgoing HTLC that we sent, // then from the PoV of the remote commitment state, they're the // receiver of this HTLC. + htlcLeaf := fn.ChainOption( + func(l CommitAuxLeaves) fn.Option[input.AuxTapLeaf] { + return fn.MapOption(func(val uint16) input.AuxTapLeaf { + idx := input.HtlcIndex(val) + + if htlc.Incoming.Val { + leaves := l.IncomingHtlcLeaves[idx] + return leaves.AuxTapLeaf + } + + return l.OutgoingHtlcLeaves[idx].AuxTapLeaf + })(htlc.HtlcIndex.ValOpt()) + }, + )(auxLeaves) scriptInfo, err := genHtlcScript( chanState.ChanType, htlc.Incoming.Val, lntypes.Remote, htlc.RefundTimeout.Val, htlc.RHash.Val, keyRing, + fn.FlattenOption(htlcLeaf), ) if err != nil { return emptyRetribution, err @@ -2266,7 +2362,9 @@ func createHtlcRetribution(chanState *channeldb.OpenChannel, func createBreachRetribution(revokedLog *channeldb.RevocationLog, spendTx *wire.MsgTx, chanState *channeldb.OpenChannel, keyRing *CommitmentKeyRing, commitmentSecret *btcec.PrivateKey, - leaseExpiry uint32) (*BreachRetribution, int64, int64, error) { + leaseExpiry uint32, + auxLeaves fn.Option[CommitAuxLeaves]) (*BreachRetribution, int64, int64, + error) { commitHash := revokedLog.CommitTxHash @@ -2275,7 +2373,7 @@ func createBreachRetribution(revokedLog *channeldb.RevocationLog, for i, htlc := range revokedLog.HTLCEntries { hr, err := createHtlcRetribution( chanState, keyRing, commitHash.Val, - commitmentSecret, leaseExpiry, htlc, + commitmentSecret, leaseExpiry, htlc, auxLeaves, ) if err != nil { return nil, 0, 0, err @@ -2427,6 +2525,7 @@ func createBreachRetributionLegacy(revokedLog *channeldb.ChannelCommitment, hr, err := createHtlcRetribution( chanState, keyRing, commitHash, commitmentSecret, leaseExpiry, entry, + fn.None[CommitAuxLeaves](), ) if err != nil { return nil, 0, 0, err @@ -3041,13 +3140,27 @@ func genRemoteHtlcSigJobs(keyRing *CommitmentKeyRing, // With the keys generated, we'll make a slice with enough capacity to // hold potentially all the HTLCs. The actual slice may be a bit // smaller (than its total capacity) and some HTLCs may be dust. - numSigs := (len(remoteCommitView.incomingHTLCs) + - len(remoteCommitView.outgoingHTLCs)) + numSigs := len(remoteCommitView.incomingHTLCs) + + len(remoteCommitView.outgoingHTLCs) sigBatch := make([]SignJob, 0, numSigs) var err error cancelChan := make(chan struct{}) + auxResult, err := fn.MapOptionZ( + leafStore, func(s AuxLeafStore) fn.Result[CommitDiffAuxResult] { + return s.FetchLeavesFromCommit( + NewAuxChanState(chanState), + *remoteCommitView.toDiskCommit(lntypes.Remote), + *keyRing, + ) + }, + ).Unpack() + if err != nil { + return nil, nil, fmt.Errorf("unable to fetch aux leaves: %w", + err) + } + // For each outgoing and incoming HTLC, if the HTLC isn't considered a // dust output after taking into account second-level HTLC fees, then a // sigJob will be generated and appended to the current batch. @@ -3074,6 +3187,13 @@ func genRemoteHtlcSigJobs(keyRing *CommitmentKeyRing, htlcFee := HtlcTimeoutFee(chanType, feePerKw) outputAmt := htlc.Amount.ToSatoshis() - htlcFee + auxLeaf := fn.ChainOption( + func(l CommitAuxLeaves) input.AuxTapLeaf { + leaves := l.IncomingHtlcLeaves + return leaves[htlc.HtlcIndex].SecondLevelLeaf + }, + )(auxResult.AuxLeaves) + // With the fee calculate, we can properly create the HTLC // timeout transaction using the HTLC amount minus the fee. op := wire.OutPoint{ @@ -3084,11 +3204,15 @@ func genRemoteHtlcSigJobs(keyRing *CommitmentKeyRing, chanType, isRemoteInitiator, op, outputAmt, htlc.Timeout, uint32(remoteChanCfg.CsvDelay), leaseExpiry, keyRing.RevocationKey, keyRing.ToLocalKey, + auxLeaf, ) if err != nil { return nil, nil, err } + // TODO(roasbeef): hook up signer interface here (later commit + // in this PR). + // Construct a full hash cache as we may be signing a segwit v1 // sighash. txOut := remoteCommitView.txn.TxOut[htlc.remoteOutputIndex] @@ -3115,7 +3239,8 @@ func genRemoteHtlcSigJobs(keyRing *CommitmentKeyRing, // If this is a taproot channel, then we'll need to set the // method type to ensure we generate a valid signature. if chanType.IsTaproot() { - sigJob.SignDesc.SignMethod = input.TaprootScriptSpendSignMethod //nolint:lll + //nolint:lll + sigJob.SignDesc.SignMethod = input.TaprootScriptSpendSignMethod } sigBatch = append(sigBatch, sigJob) @@ -3141,6 +3266,13 @@ func genRemoteHtlcSigJobs(keyRing *CommitmentKeyRing, htlcFee := HtlcSuccessFee(chanType, feePerKw) outputAmt := htlc.Amount.ToSatoshis() - htlcFee + auxLeaf := fn.ChainOption( + func(l CommitAuxLeaves) input.AuxTapLeaf { + leaves := l.OutgoingHtlcLeaves + return leaves[htlc.HtlcIndex].SecondLevelLeaf + }, + )(auxResult.AuxLeaves) + // With the proper output amount calculated, we can now // generate the success transaction using the remote party's // CSV delay. @@ -3152,6 +3284,7 @@ func genRemoteHtlcSigJobs(keyRing *CommitmentKeyRing, chanType, isRemoteInitiator, op, outputAmt, uint32(remoteChanCfg.CsvDelay), leaseExpiry, keyRing.RevocationKey, keyRing.ToLocalKey, + auxLeaf, ) if err != nil { return nil, nil, err @@ -4523,10 +4656,24 @@ func genHtlcSigValidationJobs(chanState *channeldb.OpenChannel, // enough capacity to hold verification jobs for all HTLC's in this // view. In the case that we have some dust outputs, then the actual // length will be smaller than the total capacity. - numHtlcs := (len(localCommitmentView.incomingHTLCs) + - len(localCommitmentView.outgoingHTLCs)) + numHtlcs := len(localCommitmentView.incomingHTLCs) + + len(localCommitmentView.outgoingHTLCs) verifyJobs := make([]VerifyJob, 0, numHtlcs) + auxResult, err := fn.MapOptionZ( + leafStore, func(s AuxLeafStore) fn.Result[CommitDiffAuxResult] { + return s.FetchLeavesFromCommit( + NewAuxChanState(chanState), + *localCommitmentView.toDiskCommit( + lntypes.Local, + ), *keyRing, + ) + }, + ).Unpack() + if err != nil { + return nil, fmt.Errorf("unable to fetch aux leaves: %w", err) + } + // We'll iterate through each output in the commitment transaction, // populating the sigHash closure function if it's detected to be an // HLTC output. Given the sighash, and the signing key, we'll be able @@ -4560,11 +4707,19 @@ func genHtlcSigValidationJobs(chanState *channeldb.OpenChannel, htlcFee := HtlcSuccessFee(chanType, feePerKw) outputAmt := htlc.Amount.ToSatoshis() - htlcFee + auxLeaf := fn.ChainOption(func( + l CommitAuxLeaves) input.AuxTapLeaf { + + leaves := l.IncomingHtlcLeaves + idx := htlc.HtlcIndex + return leaves[idx].SecondLevelLeaf + })(auxResult.AuxLeaves) + successTx, err := CreateHtlcSuccessTx( chanType, isLocalInitiator, op, outputAmt, uint32(localChanCfg.CsvDelay), leaseExpiry, keyRing.RevocationKey, - keyRing.ToLocalKey, + keyRing.ToLocalKey, auxLeaf, ) if err != nil { return nil, err @@ -4644,12 +4799,20 @@ func genHtlcSigValidationJobs(chanState *channeldb.OpenChannel, htlcFee := HtlcTimeoutFee(chanType, feePerKw) outputAmt := htlc.Amount.ToSatoshis() - htlcFee + auxLeaf := fn.ChainOption(func( + l CommitAuxLeaves) input.AuxTapLeaf { + + leaves := l.OutgoingHtlcLeaves + idx := htlc.HtlcIndex + return leaves[idx].SecondLevelLeaf + })(auxResult.AuxLeaves) + timeoutTx, err := CreateHtlcTimeoutTx( chanType, isLocalInitiator, op, outputAmt, htlc.Timeout, uint32(localChanCfg.CsvDelay), leaseExpiry, keyRing.RevocationKey, - keyRing.ToLocalKey, + keyRing.ToLocalKey, auxLeaf, ) if err != nil { return nil, err @@ -6364,6 +6527,18 @@ func NewUnilateralCloseSummary(chanState *channeldb.OpenChannel, &chanState.LocalChanCfg, &chanState.RemoteChanCfg, ) + auxResult, err := fn.MapOptionZ( + leafStore, func(s AuxLeafStore) fn.Result[CommitDiffAuxResult] { + return s.FetchLeavesFromCommit( + NewAuxChanState(chanState), remoteCommit, + *keyRing, + ) + }, + ).Unpack() + if err != nil { + return nil, fmt.Errorf("unable to fetch aux leaves: %w", err) + } + // Next, we'll obtain HTLC resolutions for all the outgoing HTLC's we // had on their commitment transaction. var leaseExpiry uint32 @@ -6376,6 +6551,7 @@ func NewUnilateralCloseSummary(chanState *channeldb.OpenChannel, signer, remoteCommit.Htlcs, keyRing, &chanState.LocalChanCfg, &chanState.RemoteChanCfg, commitSpend.SpendingTx, chanState.ChanType, isRemoteInitiator, leaseExpiry, + auxResult.AuxLeaves, ) if err != nil { return nil, fmt.Errorf("unable to create htlc "+ @@ -6384,14 +6560,17 @@ func NewUnilateralCloseSummary(chanState *channeldb.OpenChannel, commitTxBroadcast := commitSpend.SpendingTx - // TODO(roasbeef): Actually fetch aux leaves (later commits in this PR). - // Before we can generate the proper sign descriptor, we'll need to // locate the output index of our non-delayed output on the commitment // transaction. + remoteAuxLeaf := fn.ChainOption( + func(l CommitAuxLeaves) input.AuxTapLeaf { + return l.RemoteAuxLeaf + }, + )(auxResult.AuxLeaves) selfScript, maturityDelay, err := CommitScriptToRemote( chanState.ChanType, isRemoteInitiator, keyRing.ToRemoteKey, - leaseExpiry, input.NoneTapLeaf(), + leaseExpiry, remoteAuxLeaf, ) if err != nil { return nil, fmt.Errorf("unable to create self commit "+ @@ -6631,7 +6810,8 @@ func newOutgoingHtlcResolution(signer input.Signer, htlc *channeldb.HTLC, keyRing *CommitmentKeyRing, feePerKw chainfee.SatPerKWeight, csvDelay, leaseExpiry uint32, whoseCommit lntypes.ChannelParty, isCommitFromInitiator bool, - chanType channeldb.ChannelType) (*OutgoingHtlcResolution, error) { + chanType channeldb.ChannelType, + auxLeaves fn.Option[CommitAuxLeaves]) (*OutgoingHtlcResolution, error) { op := wire.OutPoint{ Hash: commitTx.TxHash(), @@ -6640,9 +6820,12 @@ func newOutgoingHtlcResolution(signer input.Signer, // First, we'll re-generate the script used to send the HTLC to the // remote party within their commitment transaction. + auxLeaf := fn.ChainOption(func(l CommitAuxLeaves) input.AuxTapLeaf { + return l.OutgoingHtlcLeaves[htlc.HtlcIndex].AuxTapLeaf + })(auxLeaves) htlcScriptInfo, err := genHtlcScript( chanType, false, whoseCommit, htlc.RefundTimeout, htlc.RHash, - keyRing, + keyRing, auxLeaf, ) if err != nil { return nil, err @@ -6715,10 +6898,16 @@ func newOutgoingHtlcResolution(signer input.Signer, // With the fee calculated, re-construct the second level timeout // transaction. + secondLevelAuxLeaf := fn.ChainOption( + func(l CommitAuxLeaves) input.AuxTapLeaf { + leaves := l.OutgoingHtlcLeaves + return leaves[htlc.HtlcIndex].SecondLevelLeaf + }, + )(auxLeaves) timeoutTx, err := CreateHtlcTimeoutTx( chanType, isCommitFromInitiator, op, secondLevelOutputAmt, - htlc.RefundTimeout, csvDelay, leaseExpiry, keyRing.RevocationKey, - keyRing.ToLocalKey, + htlc.RefundTimeout, csvDelay, leaseExpiry, + keyRing.RevocationKey, keyRing.ToLocalKey, secondLevelAuxLeaf, ) if err != nil { return nil, err @@ -6801,6 +6990,7 @@ func newOutgoingHtlcResolution(signer input.Signer, htlcSweepScript, err = SecondLevelHtlcScript( chanType, isCommitFromInitiator, keyRing.RevocationKey, keyRing.ToLocalKey, csvDelay, leaseExpiry, + secondLevelAuxLeaf, ) if err != nil { return nil, err @@ -6809,7 +6999,7 @@ func newOutgoingHtlcResolution(signer input.Signer, //nolint:lll secondLevelScriptTree, err := input.TaprootSecondLevelScriptTree( keyRing.RevocationKey, keyRing.ToLocalKey, csvDelay, - fn.None[txscript.TapLeaf](), + secondLevelAuxLeaf, ) if err != nil { return nil, err @@ -6884,8 +7074,8 @@ func newIncomingHtlcResolution(signer input.Signer, htlc *channeldb.HTLC, keyRing *CommitmentKeyRing, feePerKw chainfee.SatPerKWeight, csvDelay, leaseExpiry uint32, whoseCommit lntypes.ChannelParty, isCommitFromInitiator bool, - chanType channeldb.ChannelType) ( - *IncomingHtlcResolution, error) { + chanType channeldb.ChannelType, + auxLeaves fn.Option[CommitAuxLeaves]) (*IncomingHtlcResolution, error) { op := wire.OutPoint{ Hash: commitTx.TxHash(), @@ -6894,9 +7084,12 @@ func newIncomingHtlcResolution(signer input.Signer, // First, we'll re-generate the script the remote party used to // send the HTLC to us in their commitment transaction. + auxLeaf := fn.ChainOption(func(l CommitAuxLeaves) input.AuxTapLeaf { + return l.IncomingHtlcLeaves[htlc.HtlcIndex].AuxTapLeaf + })(auxLeaves) scriptInfo, err := genHtlcScript( chanType, true, whoseCommit, htlc.RefundTimeout, htlc.RHash, - keyRing, + keyRing, auxLeaf, ) if err != nil { return nil, err @@ -6956,6 +7149,13 @@ func newIncomingHtlcResolution(signer input.Signer, }, nil } + secondLevelAuxLeaf := fn.ChainOption( + func(l CommitAuxLeaves) input.AuxTapLeaf { + leaves := l.IncomingHtlcLeaves + return leaves[htlc.HtlcIndex].SecondLevelLeaf + }, + )(auxLeaves) + // Otherwise, we'll need to go to the second level to sweep this HTLC. // // First, we'll reconstruct the original HTLC success transaction, @@ -6965,7 +7165,7 @@ func newIncomingHtlcResolution(signer input.Signer, successTx, err := CreateHtlcSuccessTx( chanType, isCommitFromInitiator, op, secondLevelOutputAmt, csvDelay, leaseExpiry, keyRing.RevocationKey, - keyRing.ToLocalKey, + keyRing.ToLocalKey, secondLevelAuxLeaf, ) if err != nil { return nil, err @@ -7048,6 +7248,7 @@ func newIncomingHtlcResolution(signer input.Signer, htlcSweepScript, err = SecondLevelHtlcScript( chanType, isCommitFromInitiator, keyRing.RevocationKey, keyRing.ToLocalKey, csvDelay, leaseExpiry, + secondLevelAuxLeaf, ) if err != nil { return nil, err @@ -7056,7 +7257,7 @@ func newIncomingHtlcResolution(signer input.Signer, //nolint:lll secondLevelScriptTree, err := input.TaprootSecondLevelScriptTree( keyRing.RevocationKey, keyRing.ToLocalKey, csvDelay, - fn.None[txscript.TapLeaf](), + secondLevelAuxLeaf, ) if err != nil { return nil, err @@ -7149,7 +7350,8 @@ func extractHtlcResolutions(feePerKw chainfee.SatPerKWeight, htlcs []channeldb.HTLC, keyRing *CommitmentKeyRing, localChanCfg, remoteChanCfg *channeldb.ChannelConfig, commitTx *wire.MsgTx, chanType channeldb.ChannelType, - isCommitFromInitiator bool, leaseExpiry uint32) (*HtlcResolutions, error) { + isCommitFromInitiator bool, leaseExpiry uint32, + auxLeaves fn.Option[CommitAuxLeaves]) (*HtlcResolutions, error) { // TODO(roasbeef): don't need to swap csv delay? dustLimit := remoteChanCfg.DustLimit @@ -7182,8 +7384,9 @@ func extractHtlcResolutions(feePerKw chainfee.SatPerKWeight, // as we can satisfy the contract. ihr, err := newIncomingHtlcResolution( signer, localChanCfg, commitTx, &htlc, - keyRing, feePerKw, uint32(csvDelay), leaseExpiry, - whoseCommit, isCommitFromInitiator, chanType, + keyRing, feePerKw, uint32(csvDelay), + leaseExpiry, whoseCommit, isCommitFromInitiator, + chanType, auxLeaves, ) if err != nil { return nil, fmt.Errorf("incoming resolution "+ @@ -7197,7 +7400,7 @@ func extractHtlcResolutions(feePerKw chainfee.SatPerKWeight, ohr, err := newOutgoingHtlcResolution( signer, localChanCfg, commitTx, &htlc, keyRing, feePerKw, uint32(csvDelay), leaseExpiry, whoseCommit, - isCommitFromInitiator, chanType, + isCommitFromInitiator, chanType, auxLeaves, ) if err != nil { return nil, fmt.Errorf("outgoing resolution "+ @@ -7336,16 +7539,31 @@ func NewLocalForceCloseSummary(chanState *channeldb.OpenChannel, &chanState.LocalChanCfg, &chanState.RemoteChanCfg, ) - // TODO(roasbeef): Actually fetch aux leaves (later commits in this PR). + auxResult, err := fn.MapOptionZ( + leafStore, func(s AuxLeafStore) fn.Result[CommitDiffAuxResult] { + return s.FetchLeavesFromCommit( + NewAuxChanState(chanState), + chanState.LocalCommitment, *keyRing, + ) + }, + ).Unpack() + if err != nil { + return nil, fmt.Errorf("unable to fetch aux leaves: %w", err) + } var leaseExpiry uint32 if chanState.ChanType.HasLeaseExpiration() { leaseExpiry = chanState.ThawHeight } + + localAuxLeaf := fn.ChainOption( + func(l CommitAuxLeaves) input.AuxTapLeaf { + return l.LocalAuxLeaf + }, + )(auxResult.AuxLeaves) toLocalScript, err := CommitScriptToSelf( chanState.ChanType, chanState.IsInitiator, keyRing.ToLocalKey, - keyRing.RevocationKey, csvTimeout, leaseExpiry, - input.NoneTapLeaf(), + keyRing.RevocationKey, csvTimeout, leaseExpiry, localAuxLeaf, ) if err != nil { return nil, err @@ -7436,7 +7654,7 @@ func NewLocalForceCloseSummary(chanState *channeldb.OpenChannel, chainfee.SatPerKWeight(localCommit.FeePerKw), lntypes.Local, signer, localCommit.Htlcs, keyRing, &chanState.LocalChanCfg, &chanState.RemoteChanCfg, commitTx, chanState.ChanType, - chanState.IsInitiator, leaseExpiry, + chanState.IsInitiator, leaseExpiry, auxResult.AuxLeaves, ) if err != nil { return nil, fmt.Errorf("unable to gen htlc resolution: %w", err) diff --git a/lnwallet/channel_test.go b/lnwallet/channel_test.go index a09f97bfe8..d7ca8cf3f1 100644 --- a/lnwallet/channel_test.go +++ b/lnwallet/channel_test.go @@ -10067,7 +10067,7 @@ func TestCreateHtlcRetribution(t *testing.T) { // Create the htlc retribution. hr, err := createHtlcRetribution( aliceChannel.channelState, keyRing, commitHash, - dummyPrivate, leaseExpiry, htlc, + dummyPrivate, leaseExpiry, htlc, fn.None[CommitAuxLeaves](), ) // Expect no error. require.NoError(t, err) @@ -10273,6 +10273,7 @@ func TestCreateBreachRetribution(t *testing.T) { tc.revocationLog, tx, aliceChannel.channelState, keyRing, dummyPrivate, leaseExpiry, + fn.None[CommitAuxLeaves](), ) // Check the error if expected. @@ -10391,7 +10392,7 @@ func testNewBreachRetribution(t *testing.T, chanType channeldb.ChannelType) { // error as there are no past delta state saved as revocation logs yet. _, err = NewBreachRetribution( aliceChannel.channelState, stateNum, breachHeight, breachTx, - fn.None[AuxLeafStore](), + fn.Some[AuxLeafStore](&MockAuxLeafStore{}), ) require.ErrorIs(t, err, channeldb.ErrNoPastDeltas) @@ -10399,7 +10400,7 @@ func testNewBreachRetribution(t *testing.T, chanType channeldb.ChannelType) { // provided. _, err = NewBreachRetribution( aliceChannel.channelState, stateNum, breachHeight, nil, - fn.None[AuxLeafStore](), + fn.Some[AuxLeafStore](&MockAuxLeafStore{}), ) require.ErrorIs(t, err, channeldb.ErrNoPastDeltas) @@ -10513,6 +10514,7 @@ func TestExtractPayDescs(t *testing.T) { // scripts(`genHtlcScript`) as it should be tested independently. incomingPDs, outgoingPDs, err := lnChan.extractPayDescs( 0, htlcs, lntypes.Dual[*CommitmentKeyRing]{}, lntypes.Local, + fn.None[CommitAuxLeaves](), ) require.NoError(t, err) diff --git a/lnwallet/commitment.go b/lnwallet/commitment.go index 7f2a9ce33c..2ff23ab635 100644 --- a/lnwallet/commitment.go +++ b/lnwallet/commitment.go @@ -420,14 +420,14 @@ func sweepSigHash(chanType channeldb.ChannelType) txscript.SigHashType { // argument should correspond to the owner of the commitment transaction which // we are generating the to_local script for. func SecondLevelHtlcScript(chanType channeldb.ChannelType, initiator bool, - revocationKey, delayKey *btcec.PublicKey, - csvDelay, leaseExpiry uint32) (input.ScriptDescriptor, error) { + revocationKey, delayKey *btcec.PublicKey, csvDelay, leaseExpiry uint32, + auxLeaf input.AuxTapLeaf) (input.ScriptDescriptor, error) { switch { // For taproot channels, the pkScript is a segwit v1 p2tr output. case chanType.IsTaproot(): return input.TaprootSecondLevelScriptTree( - revocationKey, delayKey, csvDelay, input.NoneTapLeaf(), + revocationKey, delayKey, csvDelay, auxLeaf, ) // If we are the initiator of a leased channel, then we have an @@ -755,10 +755,7 @@ func (cb *CommitmentBuilder) createUnsignedCommitmentTx(ourBalance, theirBalance -= commitFeeMSat } - var ( - commitTx *wire.MsgTx - err error - ) + var commitTx *wire.MsgTx // Before we create the commitment transaction below, we'll try to see // if there're any aux leaves that need to be a part of the tapscript @@ -806,6 +803,19 @@ func (cb *CommitmentBuilder) createUnsignedCommitmentTx(ourBalance, return nil, err } + // Similarly, we'll now attempt to extract the set of aux leaves for + // the set of incoming and outgoing HTLCs. + incomingAuxLeaves := fn.MapOption( + func(leaves CommitAuxLeaves) input.HtlcAuxLeaves { + return leaves.IncomingHtlcLeaves + }, + )(auxResult.AuxLeaves) + outgoingAuxLeaves := fn.MapOption( + func(leaves CommitAuxLeaves) input.HtlcAuxLeaves { + return leaves.OutgoingHtlcLeaves + }, + )(auxResult.AuxLeaves) + // We'll now add all the HTLC outputs to the commitment transaction. // Each output includes an off-chain 2-of-2 covenant clause, so we'll // need the objective local/remote keys for this particular commitment @@ -826,9 +836,15 @@ func (cb *CommitmentBuilder) createUnsignedCommitmentTx(ourBalance, continue } + auxLeaf := fn.ChainOption( + func(leaves input.HtlcAuxLeaves) input.AuxTapLeaf { + return leaves[htlc.HtlcIndex].AuxTapLeaf + }, + )(outgoingAuxLeaves) + err := addHTLC( commitTx, whoseCommit, false, htlc, keyRing, - cb.chanState.ChanType, + cb.chanState.ChanType, auxLeaf, ) if err != nil { return nil, err @@ -848,9 +864,15 @@ func (cb *CommitmentBuilder) createUnsignedCommitmentTx(ourBalance, continue } + auxLeaf := fn.ChainOption( + func(leaves input.HtlcAuxLeaves) input.AuxTapLeaf { + return leaves[htlc.HtlcIndex].AuxTapLeaf + }, + )(incomingAuxLeaves) + err := addHTLC( commitTx, whoseCommit, true, htlc, keyRing, - cb.chanState.ChanType, + cb.chanState.ChanType, auxLeaf, ) if err != nil { return nil, err @@ -1128,7 +1150,7 @@ func genSegwitV0HtlcScript(chanType channeldb.ChannelType, // channel. func GenTaprootHtlcScript(isIncoming bool, whoseCommit lntypes.ChannelParty, timeout uint32, rHash [32]byte, keyRing *CommitmentKeyRing, -) (*input.HtlcScriptTree, error) { + auxLeaf input.AuxTapLeaf) (*input.HtlcScriptTree, error) { var ( htlcScriptTree *input.HtlcScriptTree @@ -1145,8 +1167,7 @@ func GenTaprootHtlcScript(isIncoming bool, whoseCommit lntypes.ChannelParty, case isIncoming && whoseCommit.IsLocal(): htlcScriptTree, err = input.ReceiverHTLCScriptTaproot( timeout, keyRing.RemoteHtlcKey, keyRing.LocalHtlcKey, - keyRing.RevocationKey, rHash[:], whoseCommit, - input.NoneTapLeaf(), + keyRing.RevocationKey, rHash[:], whoseCommit, auxLeaf, ) // We're being paid via an HTLC by the remote party, and the HTLC is @@ -1155,8 +1176,7 @@ func GenTaprootHtlcScript(isIncoming bool, whoseCommit lntypes.ChannelParty, case isIncoming && whoseCommit.IsRemote(): htlcScriptTree, err = input.SenderHTLCScriptTaproot( keyRing.RemoteHtlcKey, keyRing.LocalHtlcKey, - keyRing.RevocationKey, rHash[:], whoseCommit, - input.NoneTapLeaf(), + keyRing.RevocationKey, rHash[:], whoseCommit, auxLeaf, ) // We're sending an HTLC which is being added to our commitment @@ -1165,8 +1185,7 @@ func GenTaprootHtlcScript(isIncoming bool, whoseCommit lntypes.ChannelParty, case !isIncoming && whoseCommit.IsLocal(): htlcScriptTree, err = input.SenderHTLCScriptTaproot( keyRing.LocalHtlcKey, keyRing.RemoteHtlcKey, - keyRing.RevocationKey, rHash[:], whoseCommit, - input.NoneTapLeaf(), + keyRing.RevocationKey, rHash[:], whoseCommit, auxLeaf, ) // Finally, we're paying the remote party via an HTLC, which is being @@ -1175,8 +1194,7 @@ func GenTaprootHtlcScript(isIncoming bool, whoseCommit lntypes.ChannelParty, case !isIncoming && whoseCommit.IsRemote(): htlcScriptTree, err = input.ReceiverHTLCScriptTaproot( timeout, keyRing.LocalHtlcKey, keyRing.RemoteHtlcKey, - keyRing.RevocationKey, rHash[:], whoseCommit, - input.NoneTapLeaf(), + keyRing.RevocationKey, rHash[:], whoseCommit, auxLeaf, ) } @@ -1191,7 +1209,8 @@ func GenTaprootHtlcScript(isIncoming bool, whoseCommit lntypes.ChannelParty, // along side the multiplexer. func genHtlcScript(chanType channeldb.ChannelType, isIncoming bool, whoseCommit lntypes.ChannelParty, timeout uint32, rHash [32]byte, - keyRing *CommitmentKeyRing) (input.ScriptDescriptor, error) { + keyRing *CommitmentKeyRing, + auxLeaf input.AuxTapLeaf) (input.ScriptDescriptor, error) { if !chanType.IsTaproot() { return genSegwitV0HtlcScript( @@ -1201,7 +1220,7 @@ func genHtlcScript(chanType channeldb.ChannelType, isIncoming bool, } return GenTaprootHtlcScript( - isIncoming, whoseCommit, timeout, rHash, keyRing, + isIncoming, whoseCommit, timeout, rHash, keyRing, auxLeaf, ) } @@ -1214,13 +1233,15 @@ func genHtlcScript(chanType channeldb.ChannelType, isIncoming bool, // the descriptor itself. func addHTLC(commitTx *wire.MsgTx, whoseCommit lntypes.ChannelParty, isIncoming bool, paymentDesc *PaymentDescriptor, - keyRing *CommitmentKeyRing, chanType channeldb.ChannelType) error { + keyRing *CommitmentKeyRing, chanType channeldb.ChannelType, + auxLeaf input.AuxTapLeaf) error { timeout := paymentDesc.Timeout rHash := paymentDesc.RHash scriptInfo, err := genHtlcScript( chanType, isIncoming, whoseCommit, timeout, rHash, keyRing, + auxLeaf, ) if err != nil { return err diff --git a/lnwallet/transactions.go b/lnwallet/transactions.go index 1cf954d3cb..da86650bc6 100644 --- a/lnwallet/transactions.go +++ b/lnwallet/transactions.go @@ -8,6 +8,7 @@ import ( "github.com/btcsuite/btcd/btcutil" "github.com/btcsuite/btcd/wire" "github.com/lightningnetwork/lnd/channeldb" + "github.com/lightningnetwork/lnd/input" ) const ( @@ -50,8 +51,8 @@ var ( // - func CreateHtlcSuccessTx(chanType channeldb.ChannelType, initiator bool, htlcOutput wire.OutPoint, htlcAmt btcutil.Amount, csvDelay, - leaseExpiry uint32, revocationKey, delayKey *btcec.PublicKey) ( - *wire.MsgTx, error) { + leaseExpiry uint32, revocationKey, delayKey *btcec.PublicKey, + auxLeaf input.AuxTapLeaf) (*wire.MsgTx, error) { // Create a version two transaction (as the success version of this // spends an output with a CSV timeout). @@ -71,7 +72,7 @@ func CreateHtlcSuccessTx(chanType channeldb.ChannelType, initiator bool, // HTLC outputs. scriptInfo, err := SecondLevelHtlcScript( chanType, initiator, revocationKey, delayKey, csvDelay, - leaseExpiry, + leaseExpiry, auxLeaf, ) if err != nil { return nil, err @@ -110,7 +111,8 @@ func CreateHtlcSuccessTx(chanType channeldb.ChannelType, initiator bool, func CreateHtlcTimeoutTx(chanType channeldb.ChannelType, initiator bool, htlcOutput wire.OutPoint, htlcAmt btcutil.Amount, cltvExpiry, csvDelay, leaseExpiry uint32, - revocationKey, delayKey *btcec.PublicKey) (*wire.MsgTx, error) { + revocationKey, delayKey *btcec.PublicKey, + auxLeaf input.AuxTapLeaf) (*wire.MsgTx, error) { // Create a version two transaction (as the success version of this // spends an output with a CSV timeout), and set the lock-time to the @@ -134,7 +136,7 @@ func CreateHtlcTimeoutTx(chanType channeldb.ChannelType, initiator bool, // HTLC outputs. scriptInfo, err := SecondLevelHtlcScript( chanType, initiator, revocationKey, delayKey, csvDelay, - leaseExpiry, + leaseExpiry, auxLeaf, ) if err != nil { return nil, err From 89a94e9ac887f8faeefa2e815339b9272f0951da Mon Sep 17 00:00:00 2001 From: Keagan McClelland Date: Fri, 5 Apr 2024 16:48:27 -0700 Subject: [PATCH 046/218] htlcswitch: extract error handling for syncChanStates --- htlcswitch/link.go | 161 ++++++++++++++++++++++----------------------- 1 file changed, 78 insertions(+), 83 deletions(-) diff --git a/htlcswitch/link.go b/htlcswitch/link.go index ff140352c0..c459ef9974 100644 --- a/htlcswitch/link.go +++ b/htlcswitch/link.go @@ -1086,6 +1086,83 @@ func (l *channelLink) loadAndRemove() error { return l.channel.RemoveFwdPkgs(removeHeights...) } +// handleChanSyncErr performs the error handling logic in the case where we +// could not successfully syncChanStates with our channel peer. +func (l *channelLink) handleChanSyncErr(err error) { + l.log.Warnf("error when syncing channel states: %v", err) + + var errDataLoss *lnwallet.ErrCommitSyncLocalDataLoss + + switch { + case errors.Is(err, ErrLinkShuttingDown): + l.log.Debugf("unable to sync channel states, link is " + + "shutting down") + return + + // We failed syncing the commit chains, probably because the remote has + // lost state. We should force close the channel. + case errors.Is(err, lnwallet.ErrCommitSyncRemoteDataLoss): + fallthrough + + // The remote sent us an invalid last commit secret, we should force + // close the channel. + // TODO(halseth): and permanently ban the peer? + case errors.Is(err, lnwallet.ErrInvalidLastCommitSecret): + fallthrough + + // The remote sent us a commit point different from what they sent us + // before. + // TODO(halseth): ban peer? + case errors.Is(err, lnwallet.ErrInvalidLocalUnrevokedCommitPoint): + // We'll fail the link and tell the peer to force close the + // channel. Note that the database state is not updated here, + // but will be updated when the close transaction is ready to + // avoid that we go down before storing the transaction in the + // db. + l.failf( + LinkFailureError{ + code: ErrSyncError, + FailureAction: LinkFailureForceClose, + }, + "unable to synchronize channel states: %v", err, + ) + + // We have lost state and cannot safely force close the channel. Fail + // the channel and wait for the remote to hopefully force close it. The + // remote has sent us its latest unrevoked commitment point, and we'll + // store it in the database, such that we can attempt to recover the + // funds if the remote force closes the channel. + case errors.As(err, &errDataLoss): + err := l.channel.MarkDataLoss( + errDataLoss.CommitPoint, + ) + if err != nil { + l.log.Errorf("unable to mark channel data loss: %v", + err) + } + + // We determined the commit chains were not possible to sync. We + // cautiously fail the channel, but don't force close. + // TODO(halseth): can we safely force close in any cases where this + // error is returned? + case errors.Is(err, lnwallet.ErrCannotSyncCommitChains): + if err := l.channel.MarkBorked(); err != nil { + l.log.Errorf("unable to mark channel borked: %v", err) + } + + // Other, unspecified error. + default: + } + + l.failf( + LinkFailureError{ + code: ErrRecoveryError, + FailureAction: LinkFailureForceNone, + }, + "unable to synchronize channel states: %v", err, + ) +} + // htlcManager is the primary goroutine which drives a channel's commitment // update state-machine in response to messages received via several channels. // This goroutine reads messages from the upstream (remote) peer, and also from @@ -1121,89 +1198,7 @@ func (l *channelLink) htlcManager() { if l.cfg.SyncStates { err := l.syncChanStates() if err != nil { - l.log.Warnf("error when syncing channel states: %v", err) - - errDataLoss, localDataLoss := - err.(*lnwallet.ErrCommitSyncLocalDataLoss) - - switch { - case err == ErrLinkShuttingDown: - l.log.Debugf("unable to sync channel states, " + - "link is shutting down") - return - - // We failed syncing the commit chains, probably - // because the remote has lost state. We should force - // close the channel. - case err == lnwallet.ErrCommitSyncRemoteDataLoss: - fallthrough - - // The remote sent us an invalid last commit secret, we - // should force close the channel. - // TODO(halseth): and permanently ban the peer? - case err == lnwallet.ErrInvalidLastCommitSecret: - fallthrough - - // The remote sent us a commit point different from - // what they sent us before. - // TODO(halseth): ban peer? - case err == lnwallet.ErrInvalidLocalUnrevokedCommitPoint: - // We'll fail the link and tell the peer to - // force close the channel. Note that the - // database state is not updated here, but will - // be updated when the close transaction is - // ready to avoid that we go down before - // storing the transaction in the db. - l.failf( - //nolint:lll - LinkFailureError{ - code: ErrSyncError, - FailureAction: LinkFailureForceClose, - }, - "unable to synchronize channel "+ - "states: %v", err, - ) - return - - // We have lost state and cannot safely force close the - // channel. Fail the channel and wait for the remote to - // hopefully force close it. The remote has sent us its - // latest unrevoked commitment point, and we'll store - // it in the database, such that we can attempt to - // recover the funds if the remote force closes the - // channel. - case localDataLoss: - err := l.channel.MarkDataLoss( - errDataLoss.CommitPoint, - ) - if err != nil { - l.log.Errorf("unable to mark channel "+ - "data loss: %v", err) - } - - // We determined the commit chains were not possible to - // sync. We cautiously fail the channel, but don't - // force close. - // TODO(halseth): can we safely force close in any - // cases where this error is returned? - case err == lnwallet.ErrCannotSyncCommitChains: - if err := l.channel.MarkBorked(); err != nil { - l.log.Errorf("unable to mark channel "+ - "borked: %v", err) - } - - // Other, unspecified error. - default: - } - - l.failf( - LinkFailureError{ - code: ErrRecoveryError, - FailureAction: LinkFailureForceNone, - }, - "unable to synchronize channel "+ - "states: %v", err, - ) + l.handleChanSyncErr(err) return } } From 61383f1014506c4a938a5275e544a35b7679c604 Mon Sep 17 00:00:00 2001 From: Keagan McClelland Date: Fri, 9 Aug 2024 12:47:58 -0700 Subject: [PATCH 047/218] lnwallet: pack commit chains into Dual This commit packs the LightningChannel's localCommitmentChain and remoteCommitmentChain into a Dual structure for better symmetric access. This will be leveraged by an upcoming commit where we want to more concisely express how we compute the number of pending updates. --- lnwallet/channel.go | 122 ++++++++++++++++++++------------------- lnwallet/channel_test.go | 56 ++++++++++-------- 2 files changed, 93 insertions(+), 85 deletions(-) diff --git a/lnwallet/channel.go b/lnwallet/channel.go index d76c97294b..fa453afce9 100644 --- a/lnwallet/channel.go +++ b/lnwallet/channel.go @@ -858,15 +858,13 @@ type LightningChannel struct { // accepted. currentHeight uint64 - // remoteCommitChain is the remote node's commitment chain. Any new - // commitments we initiate are added to the tip of this chain. - remoteCommitChain *commitmentChain - - // localCommitChain is our local commitment chain. Any new commitments - // received are added to the tip of this chain. The tail (or lowest - // height) in this chain is our current accepted state, which we are - // able to broadcast safely. - localCommitChain *commitmentChain + // commitChains is a Dual of the local and remote node's commitment + // chains. Any new commitments we initiate are added to Remote chain's + // tip. The Local portion of this field is our local commitment chain. + // Any new commitments received are added to the tip of this chain. + // The tail (or lowest height) in this chain is our current accepted + // state, which we are able to broadcast safely. + commitChains lntypes.Dual[*commitmentChain] channelState *channeldb.OpenChannel @@ -992,14 +990,18 @@ func NewLightningChannel(signer input.Signer, return nil, fmt.Errorf("unable to derive shachain: %w", err) } + commitChains := lntypes.Dual[*commitmentChain]{ + Local: newCommitmentChain(), + Remote: newCommitmentChain(), + } + lc := &LightningChannel{ - Signer: signer, - leafStore: opts.leafStore, - sigPool: sigPool, - currentHeight: localCommit.CommitHeight, - remoteCommitChain: newCommitmentChain(), - localCommitChain: newCommitmentChain(), - channelState: state, + Signer: signer, + leafStore: opts.leafStore, + sigPool: sigPool, + currentHeight: localCommit.CommitHeight, + commitChains: commitChains, + channelState: state, commitBuilder: NewCommitmentBuilder( state, opts.leafStore, ), @@ -1463,10 +1465,10 @@ func (lc *LightningChannel) restoreCommitState( if err != nil { return err } - lc.localCommitChain.addCommitment(localCommit) + lc.commitChains.Local.addCommitment(localCommit) lc.log.Tracef("starting local commitment: %v", - lnutils.SpewLogClosure(lc.localCommitChain.tail())) + lnutils.SpewLogClosure(lc.commitChains.Local.tail())) // We'll also do the same for the remote commitment chain. remoteCommit, err := lc.diskCommitToMemCommit( @@ -1476,10 +1478,10 @@ func (lc *LightningChannel) restoreCommitState( if err != nil { return err } - lc.remoteCommitChain.addCommitment(remoteCommit) + lc.commitChains.Remote.addCommitment(remoteCommit) lc.log.Tracef("starting remote commitment: %v", - lnutils.SpewLogClosure(lc.remoteCommitChain.tail())) + lnutils.SpewLogClosure(lc.commitChains.Remote.tail())) var ( pendingRemoteCommit *commitment @@ -1508,10 +1510,10 @@ func (lc *LightningChannel) restoreCommitState( if err != nil { return err } - lc.remoteCommitChain.addCommitment(pendingRemoteCommit) + lc.commitChains.Remote.addCommitment(pendingRemoteCommit) lc.log.Debugf("pending remote commitment: %v", - lnutils.SpewLogClosure(lc.remoteCommitChain.tip())) + lnutils.SpewLogClosure(lc.commitChains.Remote.tip())) // We'll also re-create the set of commitment keys needed to // fully re-derive the state. @@ -2655,10 +2657,10 @@ func (lc *LightningChannel) fetchCommitmentView( ourLogIndex, ourHtlcIndex, theirLogIndex, theirHtlcIndex uint64, keyRing *CommitmentKeyRing) (*commitment, error) { - commitChain := lc.localCommitChain + commitChain := lc.commitChains.Local dustLimit := lc.channelState.LocalChanCfg.DustLimit if whoseCommitChain.IsRemote() { - commitChain = lc.remoteCommitChain + commitChain = lc.commitChains.Remote dustLimit = lc.channelState.RemoteChanCfg.DustLimit } @@ -3480,10 +3482,10 @@ func (lc *LightningChannel) getUnsignedAckedUpdates() []channeldb.LogUpdate { chanID := lnwire.NewChanIDFromOutPoint(lc.channelState.FundingOutpoint) // Fetch the last remote update that we have signed for. - lastRemoteCommitted := lc.remoteCommitChain.tail().theirMessageIndex + lastRemoteCommitted := lc.commitChains.Remote.tail().theirMessageIndex // Fetch the last remote update that we have acked. - lastLocalCommitted := lc.localCommitChain.tail().theirMessageIndex + lastLocalCommitted := lc.commitChains.Local.tail().theirMessageIndex // We'll now run through the remote update log to locate the items that // we haven't signed for yet. This will be the set of items we need to @@ -3709,9 +3711,9 @@ func (lc *LightningChannel) validateCommitmentSanity(theirLogCounter, ) error { // First fetch the initial balance before applying any updates. - commitChain := lc.localCommitChain + commitChain := lc.commitChains.Local if whoseCommitChain.IsRemote() { - commitChain = lc.remoteCommitChain + commitChain = lc.commitChains.Remote } ourInitialBalance := commitChain.tip().ourBalance theirInitialBalance := commitChain.tip().theirBalance @@ -3961,7 +3963,7 @@ func (lc *LightningChannel) SignNextCommitment() (*NewCommitState, error) { // party, then we're unable to create new states. Each time we create a // new state, we consume a prior revocation point. commitPoint := lc.channelState.RemoteNextRevocation - unacked := lc.remoteCommitChain.hasUnackedCommitment() + unacked := lc.commitChains.Remote.hasUnackedCommitment() if unacked || commitPoint == nil { lc.log.Tracef("waiting for remote ack=%v, nil "+ "RemoteNextRevocation: %v", unacked, commitPoint == nil) @@ -3969,8 +3971,8 @@ func (lc *LightningChannel) SignNextCommitment() (*NewCommitState, error) { } // Determine the last update on the remote log that has been locked in. - remoteACKedIndex := lc.localCommitChain.tail().theirMessageIndex - remoteHtlcIndex := lc.localCommitChain.tail().theirHtlcIndex + remoteACKedIndex := lc.commitChains.Local.tail().theirMessageIndex + remoteHtlcIndex := lc.commitChains.Local.tail().theirHtlcIndex // Before we extend this new commitment to the remote commitment chain, // ensure that we aren't violating any of the constraints the remote @@ -4117,7 +4119,7 @@ func (lc *LightningChannel) SignNextCommitment() (*NewCommitState, error) { // Extend the remote commitment chain by one with the addition of our // latest commitment update. - lc.remoteCommitChain.addCommitment(newCommitView) + lc.commitChains.Remote.addCommitment(newCommitView) return &NewCommitState{ CommitSigs: &CommitSigs{ @@ -4254,9 +4256,9 @@ func (lc *LightningChannel) ProcessChanSyncMsg( // Take note of our current commit chain heights before we begin adding // more to them. var ( - localTailHeight = lc.localCommitChain.tail().height - remoteTailHeight = lc.remoteCommitChain.tail().height - remoteTipHeight = lc.remoteCommitChain.tip().height + localTailHeight = lc.commitChains.Local.tail().height + remoteTailHeight = lc.commitChains.Remote.tail().height + remoteTipHeight = lc.commitChains.Remote.tip().height ) // We'll now check that their view of our local chain is up-to-date. @@ -4535,10 +4537,10 @@ func (lc *LightningChannel) computeView(view *HtlcView, dryRunFee fn.Option[chainfee.SatPerKWeight]) (lnwire.MilliSatoshi, lnwire.MilliSatoshi, lntypes.WeightUnit, *HtlcView, error) { - commitChain := lc.localCommitChain + commitChain := lc.commitChains.Local dustLimit := lc.channelState.LocalChanCfg.DustLimit if whoseCommitChain.IsRemote() { - commitChain = lc.remoteCommitChain + commitChain = lc.commitChains.Remote dustLimit = lc.channelState.RemoteChanCfg.DustLimit } @@ -5001,8 +5003,8 @@ func (lc *LightningChannel) ReceiveNewCommitment(commitSigs *CommitSigs) error { } // Determine the last update on the local log that has been locked in. - localACKedIndex := lc.remoteCommitChain.tail().ourMessageIndex - localHtlcIndex := lc.remoteCommitChain.tail().ourHtlcIndex + localACKedIndex := lc.commitChains.Remote.tail().ourMessageIndex + localHtlcIndex := lc.commitChains.Remote.tail().ourHtlcIndex // Ensure that this new local update from the remote node respects all // the constraints we specified during initial channel setup. If not, @@ -5239,7 +5241,7 @@ func (lc *LightningChannel) ReceiveNewCommitment(commitSigs *CommitSigs) error { localCommitmentView.sig = commitSigs.CommitSig.ToSignatureBytes() //nolint:lll } - lc.localCommitChain.addCommitment(localCommitmentView) + lc.commitChains.Local.addCommitment(localCommitmentView) return nil } @@ -5257,13 +5259,13 @@ func (lc *LightningChannel) IsChannelClean() bool { defer lc.RUnlock() // Check whether we have a pending commitment for our local state. - if lc.localCommitChain.hasUnackedCommitment() { + if lc.commitChains.Local.hasUnackedCommitment() { return false } // Check whether our counterparty has a pending commitment for their // state. - if lc.remoteCommitChain.hasUnackedCommitment() { + if lc.commitChains.Remote.hasUnackedCommitment() { return false } @@ -5316,8 +5318,8 @@ func (lc *LightningChannel) oweCommitment(issuer lntypes.ChannelParty) bool { var ( remoteUpdatesPending, localUpdatesPending bool - lastLocalCommit = lc.localCommitChain.tip() - lastRemoteCommit = lc.remoteCommitChain.tip() + lastLocalCommit = lc.commitChains.Local.tip() + lastRemoteCommit = lc.commitChains.Remote.tip() perspective string ) @@ -5369,7 +5371,7 @@ func (lc *LightningChannel) PendingLocalUpdateCount() uint64 { lc.RLock() defer lc.RUnlock() - lastRemoteCommit := lc.remoteCommitChain.tip() + lastRemoteCommit := lc.commitChains.Remote.tip() return lc.localUpdateLog.logIndex - lastRemoteCommit.ourMessageIndex } @@ -5394,16 +5396,16 @@ func (lc *LightningChannel) RevokeCurrentCommitment() (*lnwire.RevokeAndAck, } lc.log.Tracef("revoking height=%v, now at height=%v", - lc.localCommitChain.tail().height, + lc.commitChains.Local.tail().height, lc.currentHeight+1) // Advance our tail, as we've revoked our previous state. - lc.localCommitChain.advanceTail() + lc.commitChains.Local.advanceTail() lc.currentHeight++ // Additionally, generate a channel delta for this state transition for // persistent storage. - chainTail := lc.localCommitChain.tail() + chainTail := lc.commitChains.Local.tail() newCommitment := chainTail.toDiskCommit(lntypes.Local) // Get the unsigned acked remotes updates that are currently in memory. @@ -5483,13 +5485,13 @@ func (lc *LightningChannel) ReceiveRevocation(revMsg *lnwire.RevokeAndAck) ( lc.log.Tracef("remote party accepted state transition, revoked height "+ "%v, now at %v", - lc.remoteCommitChain.tail().height, - lc.remoteCommitChain.tail().height+1) + lc.commitChains.Remote.tail().height, + lc.commitChains.Remote.tail().height+1) // Add one to the remote tail since this will be height *after* we write // the revocation to disk, the local height will remain unchanged. - remoteChainTail := lc.remoteCommitChain.tail().height + 1 - localChainTail := lc.localCommitChain.tail().height + remoteChainTail := lc.commitChains.Remote.tail().height + 1 + localChainTail := lc.commitChains.Local.tail().height source := lc.ShortChanID() chanID := lnwire.NewChanIDFromOutPoint(lc.channelState.FundingOutpoint) @@ -5630,8 +5632,8 @@ func (lc *LightningChannel) ReceiveRevocation(revMsg *lnwire.RevokeAndAck) ( // We use the remote commitment chain's tip as it will soon become the tail // once advanceTail is called. - remoteMessageIndex := lc.remoteCommitChain.tip().ourMessageIndex - localMessageIndex := lc.localCommitChain.tail().ourMessageIndex + remoteMessageIndex := lc.commitChains.Remote.tip().ourMessageIndex + localMessageIndex := lc.commitChains.Local.tail().ourMessageIndex localPeerUpdates := lc.unsignedLocalUpdates( remoteMessageIndex, localMessageIndex, chanID, @@ -5692,7 +5694,7 @@ func (lc *LightningChannel) ReceiveRevocation(revMsg *lnwire.RevokeAndAck) ( // Since they revoked the current lowest height in their commitment // chain, we can advance their chain by a single commitment. - lc.remoteCommitChain.advanceTail() + lc.commitChains.Remote.advanceTail() // As we've just completed a new state transition, attempt to see if we // can remove any entries from the update log which have been removed @@ -5953,7 +5955,7 @@ func (lc *LightningChannel) validateAddHtlc(pd *PaymentDescriptor, buffer BufferType) error { // Make sure adding this HTLC won't violate any of the constraints we // must keep on the commitment transactions. - remoteACKedIndex := lc.localCommitChain.tail().theirMessageIndex + remoteACKedIndex := lc.commitChains.Local.tail().theirMessageIndex // First we'll check whether this HTLC can be added to the remote // commitment transaction without violation any of the constraints. @@ -6009,7 +6011,7 @@ func (lc *LightningChannel) ReceiveHTLC(htlc *lnwire.UpdateAddHTLC) (uint64, // PR). } - localACKedIndex := lc.remoteCommitChain.tail().ourMessageIndex + localACKedIndex := lc.commitChains.Remote.tail().ourMessageIndex // Clamp down on the number of HTLC's we can receive by checking the // commitment sanity. @@ -8155,7 +8157,7 @@ func (lc *LightningChannel) availableBalance( // We'll grab the current set of log updates that the remote has // ACKed. - remoteACKedIndex := lc.localCommitChain.tip().theirMessageIndex + remoteACKedIndex := lc.commitChains.Local.tip().theirMessageIndex htlcView := lc.fetchHTLCView(remoteACKedIndex, lc.localUpdateLog.logIndex) @@ -8340,7 +8342,7 @@ func (lc *LightningChannel) validateFeeRate(feePerKw chainfee.SatPerKWeight) err availableBalance, txWeight := lc.availableBalance(AdditionalHtlc) oldFee := lnwire.NewMSatFromSatoshis( - lc.localCommitChain.tip().feePerKw.FeeForWeight(txWeight), + lc.commitChains.Local.tip().feePerKw.FeeForWeight(txWeight), ) // Our base balance is the total amount of satoshis we can commit @@ -8673,7 +8675,7 @@ func (lc *LightningChannel) MaxFeeRate( // exactly why it was introduced to react for sharp fee changes. availableBalance, weight := lc.availableBalance(AdditionalHtlc) - currentFee := lc.localCommitChain.tip().feePerKw.FeeForWeight(weight) + currentFee := lc.commitChains.Local.tip().feePerKw.FeeForWeight(weight) // baseBalance is the maximum amount available for us to spend on fees. baseBalance := availableBalance.ToSatoshis() + currentFee diff --git a/lnwallet/channel_test.go b/lnwallet/channel_test.go index d7ca8cf3f1..1e40a8bac3 100644 --- a/lnwallet/channel_test.go +++ b/lnwallet/channel_test.go @@ -1434,12 +1434,12 @@ func TestHTLCDustLimit(t *testing.T) { // while Bob's should not, because the value falls beneath his dust // limit. The amount of the HTLC should be applied to fees in Bob's // commitment transaction. - aliceCommitment := aliceChannel.localCommitChain.tip() + aliceCommitment := aliceChannel.commitChains.Local.tip() if len(aliceCommitment.txn.TxOut) != 3 { t.Fatalf("incorrect # of outputs: expected %v, got %v", 3, len(aliceCommitment.txn.TxOut)) } - bobCommitment := bobChannel.localCommitChain.tip() + bobCommitment := bobChannel.commitChains.Local.tip() if len(bobCommitment.txn.TxOut) != 2 { t.Fatalf("incorrect # of outputs: expected %v, got %v", 2, len(bobCommitment.txn.TxOut)) @@ -1464,7 +1464,7 @@ func TestHTLCDustLimit(t *testing.T) { // At this point, for Alice's commitment chains, the value of the HTLC // should have been added to Alice's balance and TotalSatoshisSent. - commitment := aliceChannel.localCommitChain.tip() + commitment := aliceChannel.commitChains.Local.tip() if len(commitment.txn.TxOut) != 2 { t.Fatalf("incorrect # of outputs: expected %v, got %v", 2, len(commitment.txn.TxOut)) @@ -1697,7 +1697,7 @@ func TestChannelBalanceDustLimit(t *testing.T) { // output for Alice's balance should have been removed as dust, leaving // only a single output that will send the remaining funds in the // channel to Bob. - commitment := bobChannel.localCommitChain.tip() + commitment := bobChannel.commitChains.Local.tip() if len(commitment.txn.TxOut) != 1 { t.Fatalf("incorrect # of outputs: expected %v, got %v", 1, len(commitment.txn.TxOut)) @@ -1815,25 +1815,25 @@ func TestStateUpdatePersistence(t *testing.T) { // After the state transition the fee update is fully locked in, and // should've been removed from both channels' update logs. - if aliceChannel.localCommitChain.tail().feePerKw != fee { + if aliceChannel.commitChains.Local.tail().feePerKw != fee { t.Fatalf("fee not locked in") } - if bobChannel.localCommitChain.tail().feePerKw != fee { + if bobChannel.commitChains.Local.tail().feePerKw != fee { t.Fatalf("fee not locked in") } assertNumLogUpdates(3, 1) // The latest commitment from both sides should have all the HTLCs. - numAliceOutgoing := aliceChannel.localCommitChain.tail().outgoingHTLCs - numAliceIncoming := aliceChannel.localCommitChain.tail().incomingHTLCs + numAliceOutgoing := aliceChannel.commitChains.Local.tail().outgoingHTLCs + numAliceIncoming := aliceChannel.commitChains.Local.tail().incomingHTLCs if len(numAliceOutgoing) != 3 { t.Fatalf("expected %v htlcs, instead got %v", 3, numAliceOutgoing) } if len(numAliceIncoming) != 1 { t.Fatalf("expected %v htlcs, instead got %v", 1, numAliceIncoming) } - numBobOutgoing := bobChannel.localCommitChain.tail().outgoingHTLCs - numBobIncoming := bobChannel.localCommitChain.tail().incomingHTLCs + numBobOutgoing := bobChannel.commitChains.Local.tail().outgoingHTLCs + numBobIncoming := bobChannel.commitChains.Local.tail().incomingHTLCs if len(numBobOutgoing) != 1 { t.Fatalf("expected %v htlcs, instead got %v", 1, numBobOutgoing) } @@ -2089,20 +2089,24 @@ func TestCancelHTLC(t *testing.T) { // Now HTLCs should be present on the commitment transaction for either // side. - if len(aliceChannel.localCommitChain.tip().outgoingHTLCs) != 0 || - len(aliceChannel.remoteCommitChain.tip().outgoingHTLCs) != 0 { + if len(aliceChannel.commitChains.Local.tip().outgoingHTLCs) != 0 || + len(aliceChannel.commitChains.Remote.tip().outgoingHTLCs) != 0 { + t.Fatalf("htlc's still active from alice's POV") } - if len(aliceChannel.localCommitChain.tip().incomingHTLCs) != 0 || - len(aliceChannel.remoteCommitChain.tip().incomingHTLCs) != 0 { + if len(aliceChannel.commitChains.Local.tip().incomingHTLCs) != 0 || + len(aliceChannel.commitChains.Remote.tip().incomingHTLCs) != 0 { + t.Fatalf("htlc's still active from alice's POV") } - if len(bobChannel.localCommitChain.tip().outgoingHTLCs) != 0 || - len(bobChannel.remoteCommitChain.tip().outgoingHTLCs) != 0 { + if len(bobChannel.commitChains.Local.tip().outgoingHTLCs) != 0 || + len(bobChannel.commitChains.Remote.tip().outgoingHTLCs) != 0 { + t.Fatalf("htlc's still active from bob's POV") } - if len(bobChannel.localCommitChain.tip().incomingHTLCs) != 0 || - len(bobChannel.remoteCommitChain.tip().incomingHTLCs) != 0 { + if len(bobChannel.commitChains.Local.tip().incomingHTLCs) != 0 || + len(bobChannel.commitChains.Remote.tip().incomingHTLCs) != 0 { + t.Fatalf("htlc's still active from bob's POV") } @@ -5305,7 +5309,9 @@ func TestChanCommitWeightDustHtlcs(t *testing.T) { // When sending htlcs we enforce the feebuffer on the commitment // transaction. remoteCommitWeight := func(lc *LightningChannel) lntypes.WeightUnit { - remoteACKedIndex := lc.localCommitChain.tip().theirMessageIndex + remoteACKedIndex := + lc.commitChains.Local.tip().theirMessageIndex + htlcView := lc.fetchHTLCView(remoteACKedIndex, lc.localUpdateLog.logIndex) @@ -5928,7 +5934,7 @@ func TestChannelUnilateralClosePendingCommit(t *testing.T) { // At this point, Alice's commitment chain should have a new pending // commit for Bob. We'll extract it so we can simulate Bob broadcasting // the commitment due to an issue. - bobCommit := aliceChannel.remoteCommitChain.tip().txn + bobCommit := aliceChannel.commitChains.Remote.tip().txn bobTxHash := bobCommit.TxHash() spendDetail := &chainntnfs.SpendDetail{ SpenderTxHash: &bobTxHash, @@ -7622,7 +7628,7 @@ func TestForceCloseBorkedState(t *testing.T) { // We manually advance the commitment tail here since the above // ReceiveRevocation call will fail before it's actually advanced. - aliceChannel.remoteCommitChain.advanceTail() + aliceChannel.commitChains.Remote.advanceTail() _, err = aliceChannel.SignNextCommitment() if err != channeldb.ErrChanBorked { t.Fatalf("sign commitment should have failed: %v", err) @@ -7837,7 +7843,7 @@ func TestIdealCommitFeeRate(t *testing.T) { maxFeeAlloc float64, ) chainfee.SatPerKWeight { balance, weight := c.availableBalance(AdditionalHtlc) - feeRate := c.localCommitChain.tip().feePerKw + feeRate := c.commitChains.Local.tip().feePerKw currentFee := feeRate.FeeForWeight(weight) maxBalance := balance.ToSatoshis() + currentFee @@ -7854,7 +7860,7 @@ func TestIdealCommitFeeRate(t *testing.T) { // currentFeeRate calculates the current fee rate of the channel. The // ideal fee rate is floored at the current fee rate of the channel. currentFeeRate := func(c *LightningChannel) chainfee.SatPerKWeight { - return c.localCommitChain.tip().feePerKw + return c.commitChains.Local.tip().feePerKw } // testCase definies the test cases when calculating the ideal fee rate @@ -11195,7 +11201,7 @@ func TestBlindingPointPersistence(t *testing.T) { require.NoError(t, err, "unable to restart alice") // Assert that the blinding point is restored from disk. - remoteCommit := aliceChannel.remoteCommitChain.tip() + remoteCommit := aliceChannel.commitChains.Remote.tip() require.Len(t, remoteCommit.outgoingHTLCs, 1) require.Equal(t, blinding, remoteCommit.outgoingHTLCs[0].BlindingPoint.UnwrapOrFailV(t)) @@ -11212,7 +11218,7 @@ func TestBlindingPointPersistence(t *testing.T) { require.NoError(t, err, "unable to restart bob's channel") // Assert that Bob is able to recover the blinding point from disk. - bobCommit := bobChannel.localCommitChain.tip() + bobCommit := bobChannel.commitChains.Local.tip() require.Len(t, bobCommit.incomingHTLCs, 1) require.Equal(t, blinding, bobCommit.incomingHTLCs[0].BlindingPoint.UnwrapOrFailV(t)) From 214dac0c4569b5716a1c77b43aeb6d31fcfe9da7 Mon Sep 17 00:00:00 2001 From: Keagan McClelland Date: Fri, 9 Aug 2024 13:00:59 -0700 Subject: [PATCH 048/218] lnwallet: pack update logs into Dual This commit, like the last one packs the update logs into a symmetric Dual structure. This will allow us to index into them more concisely in higher order logic. --- lnwallet/channel.go | 195 +++++++++++++++++--------------- lnwallet/channel_test.go | 236 ++++++++++++++++++++++----------------- 2 files changed, 236 insertions(+), 195 deletions(-) diff --git a/lnwallet/channel.go b/lnwallet/channel.go index fa453afce9..8b41ff3eda 100644 --- a/lnwallet/channel.go +++ b/lnwallet/channel.go @@ -874,8 +874,7 @@ type LightningChannel struct { // updates to this channel. The log is walked backwards as HTLC updates // are applied in order to re-construct a commitment transaction from a // commitment. The log is compacted once a revocation is received. - localUpdateLog *updateLog - remoteUpdateLog *updateLog + updateLogs lntypes.Dual[*updateLog] // log is a channel-specific logging instance. log btclog.Logger @@ -980,6 +979,10 @@ func NewLightningChannel(signer input.Signer, remoteUpdateLog := newUpdateLog( localCommit.RemoteLogIndex, localCommit.RemoteHtlcIndex, ) + updateLogs := lntypes.Dual[*updateLog]{ + Local: localUpdateLog, + Remote: remoteUpdateLog, + } logPrefix := fmt.Sprintf("ChannelPoint(%v):", state.FundingOutpoint) @@ -1005,8 +1008,7 @@ func NewLightningChannel(signer input.Signer, commitBuilder: NewCommitmentBuilder( state, opts.leafStore, ), - localUpdateLog: localUpdateLog, - remoteUpdateLog: remoteUpdateLog, + updateLogs: updateLogs, Capacity: state.Capacity, taprootNonceProducer: taprootNonceProducer, log: build.NewPrefixLog(logPrefix, walletLog), @@ -1662,7 +1664,7 @@ func (lc *LightningChannel) restoreStateLogs( htlc.addCommitHeightRemote = incomingRemoteAddHeights[htlc.HtlcIndex] // Restore the htlc back to the remote log. - lc.remoteUpdateLog.restoreHtlc(&htlc) + lc.updateLogs.Remote.restoreHtlc(&htlc) } // Similarly, we'll do the same for the outgoing HTLCs within the @@ -1677,7 +1679,7 @@ func (lc *LightningChannel) restoreStateLogs( htlc.addCommitHeightLocal = outgoingLocalAddHeights[htlc.HtlcIndex] // Restore the htlc back to the local log. - lc.localUpdateLog.restoreHtlc(&htlc) + lc.updateLogs.Local.restoreHtlc(&htlc) } // If we have a dangling (un-acked) commit for the remote party, then we @@ -1722,7 +1724,7 @@ func (lc *LightningChannel) restorePendingRemoteUpdates( logUpdate := logUpdate payDesc, err := lc.remoteLogUpdateToPayDesc( - &logUpdate, lc.localUpdateLog, localCommitmentHeight, + &logUpdate, lc.updateLogs.Local, localCommitmentHeight, ) if err != nil { return err @@ -1732,7 +1734,7 @@ func (lc *LightningChannel) restorePendingRemoteUpdates( // Sanity check that we are not restoring a remote log update // that we haven't received a sig for. - if logIdx >= lc.remoteUpdateLog.logIndex { + if logIdx >= lc.updateLogs.Remote.logIndex { return fmt.Errorf("attempted to restore an "+ "unsigned remote update: log_index=%v", logIdx) @@ -1773,15 +1775,17 @@ func (lc *LightningChannel) restorePendingRemoteUpdates( payDesc.removeCommitHeightRemote = height } - lc.remoteUpdateLog.restoreUpdate(payDesc) + lc.updateLogs.Remote.restoreUpdate(payDesc) default: if heightSet { payDesc.removeCommitHeightRemote = height } - lc.remoteUpdateLog.restoreUpdate(payDesc) - lc.localUpdateLog.markHtlcModified(payDesc.ParentIndex) + lc.updateLogs.Remote.restoreUpdate(payDesc) + lc.updateLogs.Local.markHtlcModified( + payDesc.ParentIndex, + ) } } @@ -1800,20 +1804,23 @@ func (lc *LightningChannel) restorePeerLocalUpdates(updates []channeldb.LogUpdat logUpdate := logUpdate payDesc, err := lc.localLogUpdateToPayDesc( - &logUpdate, lc.remoteUpdateLog, remoteCommitmentHeight, + &logUpdate, lc.updateLogs.Remote, + remoteCommitmentHeight, ) if err != nil { return err } - lc.localUpdateLog.restoreUpdate(payDesc) + lc.updateLogs.Local.restoreUpdate(payDesc) // Since Add updates are not stored and FeeUpdates don't have a // corresponding entry in the remote update log, we only need to // mark the htlc as modified if the update was Settle, Fail, or // MalformedFail. if payDesc.EntryType != FeeUpdate { - lc.remoteUpdateLog.markHtlcModified(payDesc.ParentIndex) + lc.updateLogs.Remote.markHtlcModified( + payDesc.ParentIndex, + ) } } @@ -1848,7 +1855,7 @@ func (lc *LightningChannel) restorePendingLocalUpdates( logUpdate := logUpdate payDesc, err := lc.logUpdateToPayDesc( - &logUpdate, lc.remoteUpdateLog, pendingHeight, + &logUpdate, lc.updateLogs.Remote, pendingHeight, chainfee.SatPerKWeight(pendingCommit.FeePerKw), pendingRemoteKeys, lc.channelState.RemoteChanCfg.DustLimit, @@ -1862,9 +1869,9 @@ func (lc *LightningChannel) restorePendingLocalUpdates( // updates, so they will be unset. To account for this we set // them to to current update log index. if payDesc.EntryType == FeeUpdate && payDesc.LogIndex == 0 && - lc.localUpdateLog.logIndex > 0 { + lc.updateLogs.Local.logIndex > 0 { - payDesc.LogIndex = lc.localUpdateLog.logIndex + payDesc.LogIndex = lc.updateLogs.Local.logIndex lc.log.Debugf("Found FeeUpdate on "+ "pendingRemoteCommitDiff without logIndex, "+ "using %v", payDesc.LogIndex) @@ -1872,10 +1879,10 @@ func (lc *LightningChannel) restorePendingLocalUpdates( // At this point the restored update's logIndex must be equal // to the update log, otherwise something is horribly wrong. - if payDesc.LogIndex != lc.localUpdateLog.logIndex { + if payDesc.LogIndex != lc.updateLogs.Local.logIndex { panic(fmt.Sprintf("log index mismatch: "+ "%v vs %v", payDesc.LogIndex, - lc.localUpdateLog.logIndex)) + lc.updateLogs.Local.logIndex)) } switch payDesc.EntryType { @@ -1885,21 +1892,25 @@ func (lc *LightningChannel) restorePendingLocalUpdates( // panic to catch this. // TODO(halseth): remove when cause of htlc entry bug // is found. - if payDesc.HtlcIndex != lc.localUpdateLog.htlcCounter { + if payDesc.HtlcIndex != + lc.updateLogs.Local.htlcCounter { + panic(fmt.Sprintf("htlc index mismatch: "+ "%v vs %v", payDesc.HtlcIndex, - lc.localUpdateLog.htlcCounter)) + lc.updateLogs.Local.htlcCounter)) } - lc.localUpdateLog.appendHtlc(payDesc) + lc.updateLogs.Local.appendHtlc(payDesc) case FeeUpdate: - lc.localUpdateLog.appendUpdate(payDesc) + lc.updateLogs.Local.appendUpdate(payDesc) default: - lc.localUpdateLog.appendUpdate(payDesc) + lc.updateLogs.Local.appendUpdate(payDesc) - lc.remoteUpdateLog.markHtlcModified(payDesc.ParentIndex) + lc.updateLogs.Remote.markHtlcModified( + payDesc.ParentIndex, + ) } } @@ -2617,7 +2628,7 @@ func (lc *LightningChannel) fetchHTLCView(theirLogIndex, ourLogIndex uint64) *HtlcView { var ourHTLCs []*PaymentDescriptor - for e := lc.localUpdateLog.Front(); e != nil; e = e.Next() { + for e := lc.updateLogs.Local.Front(); e != nil; e = e.Next() { htlc := e.Value // This HTLC is active from this point-of-view iff the log @@ -2629,7 +2640,7 @@ func (lc *LightningChannel) fetchHTLCView(theirLogIndex, } var theirHTLCs []*PaymentDescriptor - for e := lc.remoteUpdateLog.Front(); e != nil; e = e.Next() { + for e := lc.updateLogs.Remote.Front(); e != nil; e = e.Next() { htlc := e.Value // If this is an incoming HTLC, then it is only active from @@ -2953,10 +2964,10 @@ func (lc *LightningChannel) fetchParent(entry *PaymentDescriptor, ) if whoseUpdateLog.IsRemote() { - updateLog = lc.remoteUpdateLog + updateLog = lc.updateLogs.Remote logName = "remote" } else { - updateLog = lc.localUpdateLog + updateLog = lc.updateLogs.Local logName = "local" } @@ -3354,7 +3365,7 @@ func (lc *LightningChannel) createCommitDiff(newCommit *commitment, // were only just committed within this pending state. This will be the // set of items we need to retransmit if we reconnect and find that // they didn't process this new state fully. - for e := lc.localUpdateLog.Front(); e != nil; e = e.Next() { + for e := lc.updateLogs.Local.Front(); e != nil; e = e.Next() { pd := e.Value // If this entry wasn't committed at the exact height of this @@ -3492,7 +3503,7 @@ func (lc *LightningChannel) getUnsignedAckedUpdates() []channeldb.LogUpdate { // restore if we reconnect in order to produce the signature that the // remote party expects. var logUpdates []channeldb.LogUpdate - for e := lc.remoteUpdateLog.Front(); e != nil; e = e.Next() { + for e := lc.updateLogs.Remote.Front(); e != nil; e = e.Next() { pd := e.Value // Skip all remote updates that we have already included in our @@ -3982,7 +3993,7 @@ func (lc *LightningChannel) SignNextCommitment() (*NewCommitState, error) { // point all updates will have to get locked-in so we enforce the // minimum requirement. err := lc.validateCommitmentSanity( - remoteACKedIndex, lc.localUpdateLog.logIndex, lntypes.Remote, + remoteACKedIndex, lc.updateLogs.Local.logIndex, lntypes.Remote, NoBuffer, nil, nil, ) if err != nil { @@ -4005,8 +4016,8 @@ func (lc *LightningChannel) SignNextCommitment() (*NewCommitState, error) { // _all_ of our changes (pending or committed) but only the remote // node's changes up to the last change we've ACK'd. newCommitView, err := lc.fetchCommitmentView( - lntypes.Remote, lc.localUpdateLog.logIndex, - lc.localUpdateLog.htlcCounter, remoteACKedIndex, + lntypes.Remote, lc.updateLogs.Local.logIndex, + lc.updateLogs.Local.htlcCounter, remoteACKedIndex, remoteHtlcIndex, keyRing, ) if err != nil { @@ -4016,7 +4027,7 @@ func (lc *LightningChannel) SignNextCommitment() (*NewCommitState, error) { lc.log.Tracef("extending remote chain to height %v, "+ "local_log=%v, remote_log=%v", newCommitView.height, - lc.localUpdateLog.logIndex, remoteACKedIndex) + lc.updateLogs.Local.logIndex, remoteACKedIndex) lc.log.Tracef("remote chain: our_balance=%v, "+ "their_balance=%v, commit_tx: %v", @@ -5015,7 +5026,7 @@ func (lc *LightningChannel) ReceiveNewCommitment(commitSigs *CommitSigs) error { // the UpdateAddHTLC msg from our peer prior to receiving the // commit-sig). err := lc.validateCommitmentSanity( - lc.remoteUpdateLog.logIndex, localACKedIndex, lntypes.Local, + lc.updateLogs.Remote.logIndex, localACKedIndex, lntypes.Local, NoBuffer, nil, nil, ) if err != nil { @@ -5043,7 +5054,7 @@ func (lc *LightningChannel) ReceiveNewCommitment(commitSigs *CommitSigs) error { // up to the last change the remote node has ACK'd. localCommitmentView, err := lc.fetchCommitmentView( lntypes.Local, localACKedIndex, localHtlcIndex, - lc.remoteUpdateLog.logIndex, lc.remoteUpdateLog.htlcCounter, + lc.updateLogs.Remote.logIndex, lc.updateLogs.Remote.htlcCounter, keyRing, ) if err != nil { @@ -5053,7 +5064,7 @@ func (lc *LightningChannel) ReceiveNewCommitment(commitSigs *CommitSigs) error { lc.log.Tracef("extending local chain to height %v, "+ "local_log=%v, remote_log=%v", localCommitmentView.height, - localACKedIndex, lc.remoteUpdateLog.logIndex) + localACKedIndex, lc.updateLogs.Remote.logIndex) lc.log.Tracef("local chain: our_balance=%v, "+ "their_balance=%v, commit_tx: %v", @@ -5207,7 +5218,11 @@ func (lc *LightningChannel) ReceiveNewCommitment(commitSigs *CommitSigs) error { } var txBytes bytes.Buffer - localCommitTx.Serialize(&txBytes) + err = localCommitTx.Serialize(&txBytes) + if err != nil { + return err + } + return &InvalidHtlcSigError{ commitHeight: nextHeight, htlcSig: sig.ToSignatureBytes(), @@ -5329,7 +5344,7 @@ func (lc *LightningChannel) oweCommitment(issuer lntypes.ChannelParty) bool { // There are local updates pending if our local update log is // not in sync with our remote commitment tx. - localUpdatesPending = lc.localUpdateLog.logIndex != + localUpdatesPending = lc.updateLogs.Local.logIndex != lastRemoteCommit.ourMessageIndex // There are remote updates pending if their remote commitment @@ -5344,7 +5359,7 @@ func (lc *LightningChannel) oweCommitment(issuer lntypes.ChannelParty) bool { // perspective of the remote party) if the remote party has // updates to their remote tx pending for which they haven't // signed yet. - localUpdatesPending = lc.remoteUpdateLog.logIndex != + localUpdatesPending = lc.updateLogs.Remote.logIndex != lastLocalCommit.theirMessageIndex // There are remote updates pending (remote updates from the @@ -5373,7 +5388,7 @@ func (lc *LightningChannel) PendingLocalUpdateCount() uint64 { lastRemoteCommit := lc.commitChains.Remote.tip() - return lc.localUpdateLog.logIndex - lastRemoteCommit.ourMessageIndex + return lc.updateLogs.Local.logIndex - lastRemoteCommit.ourMessageIndex } // RevokeCurrentCommitment revokes the next lowest unrevoked commitment @@ -5509,7 +5524,7 @@ func (lc *LightningChannel) ReceiveRevocation(revMsg *lnwire.RevokeAndAck) ( ) var addIndex, settleFailIndex uint16 - for e := lc.remoteUpdateLog.Front(); e != nil; e = e.Next() { + for e := lc.updateLogs.Remote.Front(); e != nil; e = e.Next() { pd := e.Value // Fee updates are local to this particular channel, and should @@ -5700,7 +5715,7 @@ func (lc *LightningChannel) ReceiveRevocation(revMsg *lnwire.RevokeAndAck) ( // can remove any entries from the update log which have been removed // from the PoV of both commitment chains. compactLogs( - lc.localUpdateLog, lc.remoteUpdateLog, localChainTail, + lc.updateLogs.Local, lc.updateLogs.Remote, localChainTail, remoteChainTail, ) @@ -5806,7 +5821,7 @@ func (lc *LightningChannel) addHTLC(htlc *lnwire.UpdateAddHTLC, return 0, err } - lc.localUpdateLog.appendHtlc(pd) + lc.updateLogs.Local.appendHtlc(pd) return pd.HtlcIndex, nil } @@ -5839,7 +5854,7 @@ func (lc *LightningChannel) GetDustSum(whoseCommit lntypes.ChannelParty, feeRate = dryRunFee.UnwrapOr(feeRate) // Grab all of our HTLCs and evaluate against the dust limit. - for e := lc.localUpdateLog.Front(); e != nil; e = e.Next() { + for e := lc.updateLogs.Local.Front(); e != nil; e = e.Next() { pd := e.Value if pd.EntryType != Add { continue @@ -5858,7 +5873,7 @@ func (lc *LightningChannel) GetDustSum(whoseCommit lntypes.ChannelParty, } // Grab all of their HTLCs and evaluate against the dust limit. - for e := lc.remoteUpdateLog.Front(); e != nil; e = e.Next() { + for e := lc.updateLogs.Remote.Front(); e != nil; e = e.Next() { pd := e.Value if pd.EntryType != Add { continue @@ -5938,8 +5953,8 @@ func (lc *LightningChannel) htlcAddDescriptor(htlc *lnwire.UpdateAddHTLC, RHash: PaymentHash(htlc.PaymentHash), Timeout: htlc.Expiry, Amount: htlc.Amount, - LogIndex: lc.localUpdateLog.logIndex, - HtlcIndex: lc.localUpdateLog.htlcCounter, + LogIndex: lc.updateLogs.Local.logIndex, + HtlcIndex: lc.updateLogs.Local.htlcCounter, OnionBlob: htlc.OnionBlob[:], OpenCircuitKey: openKey, BlindingPoint: htlc.BlindingPoint, @@ -5960,7 +5975,7 @@ func (lc *LightningChannel) validateAddHtlc(pd *PaymentDescriptor, // First we'll check whether this HTLC can be added to the remote // commitment transaction without violation any of the constraints. err := lc.validateCommitmentSanity( - remoteACKedIndex, lc.localUpdateLog.logIndex, lntypes.Remote, + remoteACKedIndex, lc.updateLogs.Local.logIndex, lntypes.Remote, buffer, pd, nil, ) if err != nil { @@ -5973,7 +5988,7 @@ func (lc *LightningChannel) validateAddHtlc(pd *PaymentDescriptor, // concurrently, but if we fail this check there is for sure not // possible for us to add the HTLC. err = lc.validateCommitmentSanity( - lc.remoteUpdateLog.logIndex, lc.localUpdateLog.logIndex, + lc.updateLogs.Remote.logIndex, lc.updateLogs.Local.logIndex, lntypes.Local, buffer, pd, nil, ) if err != nil { @@ -5992,9 +6007,9 @@ func (lc *LightningChannel) ReceiveHTLC(htlc *lnwire.UpdateAddHTLC) (uint64, lc.Lock() defer lc.Unlock() - if htlc.ID != lc.remoteUpdateLog.htlcCounter { + if htlc.ID != lc.updateLogs.Remote.htlcCounter { return 0, fmt.Errorf("ID %d on HTLC add does not match expected next "+ - "ID %d", htlc.ID, lc.remoteUpdateLog.htlcCounter) + "ID %d", htlc.ID, lc.updateLogs.Remote.htlcCounter) } pd := &PaymentDescriptor{ @@ -6002,8 +6017,8 @@ func (lc *LightningChannel) ReceiveHTLC(htlc *lnwire.UpdateAddHTLC) (uint64, RHash: PaymentHash(htlc.PaymentHash), Timeout: htlc.Expiry, Amount: htlc.Amount, - LogIndex: lc.remoteUpdateLog.logIndex, - HtlcIndex: lc.remoteUpdateLog.htlcCounter, + LogIndex: lc.updateLogs.Remote.logIndex, + HtlcIndex: lc.updateLogs.Remote.htlcCounter, OnionBlob: htlc.OnionBlob[:], BlindingPoint: htlc.BlindingPoint, // TODO(guggero): Add custom records from HTLC here once we have @@ -6020,14 +6035,14 @@ func (lc *LightningChannel) ReceiveHTLC(htlc *lnwire.UpdateAddHTLC) (uint64, // we use it here. The current lightning protocol does not allow to // reject ADDs already sent by the peer. err := lc.validateCommitmentSanity( - lc.remoteUpdateLog.logIndex, localACKedIndex, lntypes.Local, + lc.updateLogs.Remote.logIndex, localACKedIndex, lntypes.Local, NoBuffer, nil, pd, ) if err != nil { return 0, err } - lc.remoteUpdateLog.appendHtlc(pd) + lc.updateLogs.Remote.appendHtlc(pd) return pd.HtlcIndex, nil } @@ -6063,7 +6078,7 @@ func (lc *LightningChannel) SettleHTLC(preimage [32]byte, lc.Lock() defer lc.Unlock() - htlc := lc.remoteUpdateLog.lookupHtlc(htlcIndex) + htlc := lc.updateLogs.Remote.lookupHtlc(htlcIndex) if htlc == nil { return ErrUnknownHtlcIndex{lc.ShortChanID(), htlcIndex} } @@ -6071,7 +6086,7 @@ func (lc *LightningChannel) SettleHTLC(preimage [32]byte, // Now that we know the HTLC exists, before checking to see if the // preimage matches, we'll ensure that we haven't already attempted to // modify the HTLC. - if lc.remoteUpdateLog.htlcHasModification(htlcIndex) { + if lc.updateLogs.Remote.htlcHasModification(htlcIndex) { return ErrHtlcIndexAlreadySettled(htlcIndex) } @@ -6082,7 +6097,7 @@ func (lc *LightningChannel) SettleHTLC(preimage [32]byte, pd := &PaymentDescriptor{ Amount: htlc.Amount, RPreimage: preimage, - LogIndex: lc.localUpdateLog.logIndex, + LogIndex: lc.updateLogs.Local.logIndex, ParentIndex: htlcIndex, EntryType: Settle, SourceRef: sourceRef, @@ -6090,12 +6105,12 @@ func (lc *LightningChannel) SettleHTLC(preimage [32]byte, ClosedCircuitKey: closeKey, } - lc.localUpdateLog.appendUpdate(pd) + lc.updateLogs.Local.appendUpdate(pd) // With the settle added to our local log, we'll now mark the HTLC as // modified to prevent ourselves from accidentally attempting a // duplicate settle. - lc.remoteUpdateLog.markHtlcModified(htlcIndex) + lc.updateLogs.Remote.markHtlcModified(htlcIndex) return nil } @@ -6108,7 +6123,7 @@ func (lc *LightningChannel) ReceiveHTLCSettle(preimage [32]byte, htlcIndex uint6 lc.Lock() defer lc.Unlock() - htlc := lc.localUpdateLog.lookupHtlc(htlcIndex) + htlc := lc.updateLogs.Local.lookupHtlc(htlcIndex) if htlc == nil { return ErrUnknownHtlcIndex{lc.ShortChanID(), htlcIndex} } @@ -6116,7 +6131,7 @@ func (lc *LightningChannel) ReceiveHTLCSettle(preimage [32]byte, htlcIndex uint6 // Now that we know the HTLC exists, before checking to see if the // preimage matches, we'll ensure that they haven't already attempted // to modify the HTLC. - if lc.localUpdateLog.htlcHasModification(htlcIndex) { + if lc.updateLogs.Local.htlcHasModification(htlcIndex) { return ErrHtlcIndexAlreadySettled(htlcIndex) } @@ -6129,16 +6144,16 @@ func (lc *LightningChannel) ReceiveHTLCSettle(preimage [32]byte, htlcIndex uint6 RPreimage: preimage, ParentIndex: htlc.HtlcIndex, RHash: htlc.RHash, - LogIndex: lc.remoteUpdateLog.logIndex, + LogIndex: lc.updateLogs.Remote.logIndex, EntryType: Settle, } - lc.remoteUpdateLog.appendUpdate(pd) + lc.updateLogs.Remote.appendUpdate(pd) // With the settle added to the remote log, we'll now mark the HTLC as // modified to prevent the remote party from accidentally attempting a // duplicate settle. - lc.localUpdateLog.markHtlcModified(htlcIndex) + lc.updateLogs.Local.markHtlcModified(htlcIndex) return nil } @@ -6174,14 +6189,14 @@ func (lc *LightningChannel) FailHTLC(htlcIndex uint64, reason []byte, lc.Lock() defer lc.Unlock() - htlc := lc.remoteUpdateLog.lookupHtlc(htlcIndex) + htlc := lc.updateLogs.Remote.lookupHtlc(htlcIndex) if htlc == nil { return ErrUnknownHtlcIndex{lc.ShortChanID(), htlcIndex} } // Now that we know the HTLC exists, we'll ensure that we haven't // already attempted to fail the HTLC. - if lc.remoteUpdateLog.htlcHasModification(htlcIndex) { + if lc.updateLogs.Remote.htlcHasModification(htlcIndex) { return ErrHtlcIndexAlreadyFailed(htlcIndex) } @@ -6189,7 +6204,7 @@ func (lc *LightningChannel) FailHTLC(htlcIndex uint64, reason []byte, Amount: htlc.Amount, RHash: htlc.RHash, ParentIndex: htlcIndex, - LogIndex: lc.localUpdateLog.logIndex, + LogIndex: lc.updateLogs.Local.logIndex, EntryType: Fail, FailReason: reason, SourceRef: sourceRef, @@ -6197,12 +6212,12 @@ func (lc *LightningChannel) FailHTLC(htlcIndex uint64, reason []byte, ClosedCircuitKey: closeKey, } - lc.localUpdateLog.appendUpdate(pd) + lc.updateLogs.Local.appendUpdate(pd) // With the fail added to the remote log, we'll now mark the HTLC as // modified to prevent ourselves from accidentally attempting a // duplicate fail. - lc.remoteUpdateLog.markHtlcModified(htlcIndex) + lc.updateLogs.Remote.markHtlcModified(htlcIndex) return nil } @@ -6224,14 +6239,14 @@ func (lc *LightningChannel) MalformedFailHTLC(htlcIndex uint64, lc.Lock() defer lc.Unlock() - htlc := lc.remoteUpdateLog.lookupHtlc(htlcIndex) + htlc := lc.updateLogs.Remote.lookupHtlc(htlcIndex) if htlc == nil { return ErrUnknownHtlcIndex{lc.ShortChanID(), htlcIndex} } // Now that we know the HTLC exists, we'll ensure that we haven't // already attempted to fail the HTLC. - if lc.remoteUpdateLog.htlcHasModification(htlcIndex) { + if lc.updateLogs.Remote.htlcHasModification(htlcIndex) { return ErrHtlcIndexAlreadyFailed(htlcIndex) } @@ -6239,19 +6254,19 @@ func (lc *LightningChannel) MalformedFailHTLC(htlcIndex uint64, Amount: htlc.Amount, RHash: htlc.RHash, ParentIndex: htlcIndex, - LogIndex: lc.localUpdateLog.logIndex, + LogIndex: lc.updateLogs.Local.logIndex, EntryType: MalformedFail, FailCode: failCode, ShaOnionBlob: shaOnionBlob, SourceRef: sourceRef, } - lc.localUpdateLog.appendUpdate(pd) + lc.updateLogs.Local.appendUpdate(pd) // With the fail added to the remote log, we'll now mark the HTLC as // modified to prevent ourselves from accidentally attempting a // duplicate fail. - lc.remoteUpdateLog.markHtlcModified(htlcIndex) + lc.updateLogs.Remote.markHtlcModified(htlcIndex) return nil } @@ -6266,14 +6281,14 @@ func (lc *LightningChannel) ReceiveFailHTLC(htlcIndex uint64, reason []byte, lc.Lock() defer lc.Unlock() - htlc := lc.localUpdateLog.lookupHtlc(htlcIndex) + htlc := lc.updateLogs.Local.lookupHtlc(htlcIndex) if htlc == nil { return ErrUnknownHtlcIndex{lc.ShortChanID(), htlcIndex} } // Now that we know the HTLC exists, we'll ensure that they haven't // already attempted to fail the HTLC. - if lc.localUpdateLog.htlcHasModification(htlcIndex) { + if lc.updateLogs.Local.htlcHasModification(htlcIndex) { return ErrHtlcIndexAlreadyFailed(htlcIndex) } @@ -6281,17 +6296,17 @@ func (lc *LightningChannel) ReceiveFailHTLC(htlcIndex uint64, reason []byte, Amount: htlc.Amount, RHash: htlc.RHash, ParentIndex: htlc.HtlcIndex, - LogIndex: lc.remoteUpdateLog.logIndex, + LogIndex: lc.updateLogs.Remote.logIndex, EntryType: Fail, FailReason: reason, } - lc.remoteUpdateLog.appendUpdate(pd) + lc.updateLogs.Remote.appendUpdate(pd) // With the fail added to the remote log, we'll now mark the HTLC as // modified to prevent ourselves from accidentally attempting a // duplicate fail. - lc.localUpdateLog.markHtlcModified(htlcIndex) + lc.updateLogs.Local.markHtlcModified(htlcIndex) return nil } @@ -8159,7 +8174,7 @@ func (lc *LightningChannel) availableBalance( // ACKed. remoteACKedIndex := lc.commitChains.Local.tip().theirMessageIndex htlcView := lc.fetchHTLCView(remoteACKedIndex, - lc.localUpdateLog.logIndex) + lc.updateLogs.Local.logIndex) // Calculate our available balance from our local commitment. // TODO(halseth): could reuse parts validateCommitmentSanity to do this @@ -8391,12 +8406,12 @@ func (lc *LightningChannel) UpdateFee(feePerKw chainfee.SatPerKWeight) error { } pd := &PaymentDescriptor{ - LogIndex: lc.localUpdateLog.logIndex, + LogIndex: lc.updateLogs.Local.logIndex, Amount: lnwire.NewMSatFromSatoshis(btcutil.Amount(feePerKw)), EntryType: FeeUpdate, } - lc.localUpdateLog.appendUpdate(pd) + lc.updateLogs.Local.appendUpdate(pd) return nil } @@ -8415,8 +8430,8 @@ func (lc *LightningChannel) CommitFeeTotalAt( // We want to grab every update in both update logs to calculate the // commitment fees in the worst-case with this fee-rate. - localIdx := lc.localUpdateLog.logIndex - remoteIdx := lc.remoteUpdateLog.logIndex + localIdx := lc.updateLogs.Local.logIndex + remoteIdx := lc.updateLogs.Remote.logIndex localHtlcView := lc.fetchHTLCView(remoteIdx, localIdx) @@ -8463,12 +8478,12 @@ func (lc *LightningChannel) ReceiveUpdateFee(feePerKw chainfee.SatPerKWeight) er // TODO(roasbeef): or just modify to use the other balance? pd := &PaymentDescriptor{ - LogIndex: lc.remoteUpdateLog.logIndex, + LogIndex: lc.updateLogs.Remote.logIndex, Amount: lnwire.NewMSatFromSatoshis(btcutil.Amount(feePerKw)), EntryType: FeeUpdate, } - lc.remoteUpdateLog.appendUpdate(pd) + lc.updateLogs.Remote.appendUpdate(pd) return nil } @@ -8937,7 +8952,7 @@ func (lc *LightningChannel) unsignedLocalUpdates(remoteMessageIndex, localMessageIndex uint64, chanID lnwire.ChannelID) []channeldb.LogUpdate { var localPeerUpdates []channeldb.LogUpdate - for e := lc.localUpdateLog.Front(); e != nil; e = e.Next() { + for e := lc.updateLogs.Local.Front(); e != nil; e = e.Next() { pd := e.Value // We don't save add updates as they are restored from the diff --git a/lnwallet/channel_test.go b/lnwallet/channel_test.go index 1e40a8bac3..04465bc278 100644 --- a/lnwallet/channel_test.go +++ b/lnwallet/channel_test.go @@ -335,21 +335,23 @@ func testAddSettleWorkflow(t *testing.T, tweakless bool, // The logs of both sides should now be cleared since the entry adding // the HTLC should have been removed once both sides receive the // revocation. - if aliceChannel.localUpdateLog.Len() != 0 { + if aliceChannel.updateLogs.Local.Len() != 0 { t.Fatalf("alice's local not updated, should be empty, has %v "+ - "entries instead", aliceChannel.localUpdateLog.Len()) + "entries instead", aliceChannel.updateLogs.Local.Len()) } - if aliceChannel.remoteUpdateLog.Len() != 0 { + if aliceChannel.updateLogs.Remote.Len() != 0 { t.Fatalf("alice's remote not updated, should be empty, has %v "+ - "entries instead", aliceChannel.remoteUpdateLog.Len()) + "entries instead", aliceChannel.updateLogs.Remote.Len()) } - if len(aliceChannel.localUpdateLog.updateIndex) != 0 { - t.Fatalf("alice's local log index not cleared, should be empty but "+ - "has %v entries", len(aliceChannel.localUpdateLog.updateIndex)) + if len(aliceChannel.updateLogs.Local.updateIndex) != 0 { + t.Fatalf("alice's local log index not cleared, should be "+ + "empty but has %v entries", + len(aliceChannel.updateLogs.Local.updateIndex)) } - if len(aliceChannel.remoteUpdateLog.updateIndex) != 0 { - t.Fatalf("alice's remote log index not cleared, should be empty but "+ - "has %v entries", len(aliceChannel.remoteUpdateLog.updateIndex)) + if len(aliceChannel.updateLogs.Remote.updateIndex) != 0 { + t.Fatalf("alice's remote log index not cleared, should be "+ + "empty but has %v entries", + len(aliceChannel.updateLogs.Remote.updateIndex)) } } @@ -1772,26 +1774,26 @@ func TestStateUpdatePersistence(t *testing.T) { // Helper method that asserts the expected number of updates are found // in the update logs. assertNumLogUpdates := func(numAliceUpdates, numBobUpdates int) { - if aliceChannel.localUpdateLog.Len() != numAliceUpdates { + if aliceChannel.updateLogs.Local.Len() != numAliceUpdates { t.Fatalf("expected %d local updates, found %d", numAliceUpdates, - aliceChannel.localUpdateLog.Len()) + aliceChannel.updateLogs.Local.Len()) } - if aliceChannel.remoteUpdateLog.Len() != numBobUpdates { + if aliceChannel.updateLogs.Remote.Len() != numBobUpdates { t.Fatalf("expected %d remote updates, found %d", numBobUpdates, - aliceChannel.remoteUpdateLog.Len()) + aliceChannel.updateLogs.Remote.Len()) } - if bobChannel.localUpdateLog.Len() != numBobUpdates { + if bobChannel.updateLogs.Local.Len() != numBobUpdates { t.Fatalf("expected %d local updates, found %d", numBobUpdates, - bobChannel.localUpdateLog.Len()) + bobChannel.updateLogs.Local.Len()) } - if bobChannel.remoteUpdateLog.Len() != numAliceUpdates { + if bobChannel.updateLogs.Remote.Len() != numAliceUpdates { t.Fatalf("expected %d remote updates, found %d", numAliceUpdates, - bobChannel.remoteUpdateLog.Len()) + bobChannel.updateLogs.Remote.Len()) } } @@ -1867,62 +1869,72 @@ func TestStateUpdatePersistence(t *testing.T) { // The state update logs of the new channels and the old channels // should now be identical other than the height the HTLCs were added. - if aliceChannel.localUpdateLog.logIndex != - aliceChannelNew.localUpdateLog.logIndex { + if aliceChannel.updateLogs.Local.logIndex != + aliceChannelNew.updateLogs.Local.logIndex { + t.Fatalf("alice log counter: expected %v, got %v", - aliceChannel.localUpdateLog.logIndex, - aliceChannelNew.localUpdateLog.logIndex) + aliceChannel.updateLogs.Local.logIndex, + aliceChannelNew.updateLogs.Local.logIndex) } - if aliceChannel.remoteUpdateLog.logIndex != - aliceChannelNew.remoteUpdateLog.logIndex { + if aliceChannel.updateLogs.Remote.logIndex != + aliceChannelNew.updateLogs.Remote.logIndex { + t.Fatalf("alice log counter: expected %v, got %v", - aliceChannel.remoteUpdateLog.logIndex, - aliceChannelNew.remoteUpdateLog.logIndex) + aliceChannel.updateLogs.Remote.logIndex, + aliceChannelNew.updateLogs.Remote.logIndex) } - if aliceChannel.localUpdateLog.Len() != - aliceChannelNew.localUpdateLog.Len() { + if aliceChannel.updateLogs.Local.Len() != + aliceChannelNew.updateLogs.Local.Len() { + t.Fatalf("alice log len: expected %v, got %v", - aliceChannel.localUpdateLog.Len(), - aliceChannelNew.localUpdateLog.Len()) + aliceChannel.updateLogs.Local.Len(), + aliceChannelNew.updateLogs.Local.Len()) } - if aliceChannel.remoteUpdateLog.Len() != - aliceChannelNew.remoteUpdateLog.Len() { + if aliceChannel.updateLogs.Remote.Len() != + aliceChannelNew.updateLogs.Remote.Len() { + t.Fatalf("alice log len: expected %v, got %v", - aliceChannel.remoteUpdateLog.Len(), - aliceChannelNew.remoteUpdateLog.Len()) + aliceChannel.updateLogs.Remote.Len(), + aliceChannelNew.updateLogs.Remote.Len()) } - if bobChannel.localUpdateLog.logIndex != - bobChannelNew.localUpdateLog.logIndex { + if bobChannel.updateLogs.Local.logIndex != + bobChannelNew.updateLogs.Local.logIndex { + t.Fatalf("bob log counter: expected %v, got %v", - bobChannel.localUpdateLog.logIndex, - bobChannelNew.localUpdateLog.logIndex) + bobChannel.updateLogs.Local.logIndex, + bobChannelNew.updateLogs.Local.logIndex) } - if bobChannel.remoteUpdateLog.logIndex != - bobChannelNew.remoteUpdateLog.logIndex { + if bobChannel.updateLogs.Remote.logIndex != + bobChannelNew.updateLogs.Remote.logIndex { + t.Fatalf("bob log counter: expected %v, got %v", - bobChannel.remoteUpdateLog.logIndex, - bobChannelNew.remoteUpdateLog.logIndex) + bobChannel.updateLogs.Remote.logIndex, + bobChannelNew.updateLogs.Remote.logIndex) } - if bobChannel.localUpdateLog.Len() != - bobChannelNew.localUpdateLog.Len() { + if bobChannel.updateLogs.Local.Len() != + bobChannelNew.updateLogs.Local.Len() { + t.Fatalf("bob log len: expected %v, got %v", - bobChannel.localUpdateLog.Len(), - bobChannelNew.localUpdateLog.Len()) + bobChannel.updateLogs.Local.Len(), + bobChannelNew.updateLogs.Local.Len()) } - if bobChannel.remoteUpdateLog.Len() != - bobChannelNew.remoteUpdateLog.Len() { + if bobChannel.updateLogs.Remote.Len() != + bobChannelNew.updateLogs.Remote.Len() { + t.Fatalf("bob log len: expected %v, got %v", - bobChannel.remoteUpdateLog.Len(), - bobChannelNew.remoteUpdateLog.Len()) + bobChannel.updateLogs.Remote.Len(), + bobChannelNew.updateLogs.Remote.Len()) } // TODO(roasbeef): expand test to also ensure state revocation log has // proper pk scripts // Newly generated pkScripts for HTLCs should be the same as in the old channel. - for _, entry := range aliceChannel.localUpdateLog.htlcIndex { + for _, entry := range aliceChannel.updateLogs.Local.htlcIndex { htlc := entry.Value - restoredHtlc := aliceChannelNew.localUpdateLog.lookupHtlc(htlc.HtlcIndex) + restoredHtlc := aliceChannelNew.updateLogs.Local.lookupHtlc( + htlc.HtlcIndex, + ) if !bytes.Equal(htlc.ourPkScript, restoredHtlc.ourPkScript) { t.Fatalf("alice ourPkScript in ourLog: expected %X, got %X", htlc.ourPkScript[:5], restoredHtlc.ourPkScript[:5]) @@ -1932,9 +1944,11 @@ func TestStateUpdatePersistence(t *testing.T) { htlc.theirPkScript[:5], restoredHtlc.theirPkScript[:5]) } } - for _, entry := range aliceChannel.remoteUpdateLog.htlcIndex { + for _, entry := range aliceChannel.updateLogs.Remote.htlcIndex { htlc := entry.Value - restoredHtlc := aliceChannelNew.remoteUpdateLog.lookupHtlc(htlc.HtlcIndex) + restoredHtlc := aliceChannelNew.updateLogs.Remote.lookupHtlc( + htlc.HtlcIndex, + ) if !bytes.Equal(htlc.ourPkScript, restoredHtlc.ourPkScript) { t.Fatalf("alice ourPkScript in theirLog: expected %X, got %X", htlc.ourPkScript[:5], restoredHtlc.ourPkScript[:5]) @@ -1944,9 +1958,11 @@ func TestStateUpdatePersistence(t *testing.T) { htlc.theirPkScript[:5], restoredHtlc.theirPkScript[:5]) } } - for _, entry := range bobChannel.localUpdateLog.htlcIndex { + for _, entry := range bobChannel.updateLogs.Local.htlcIndex { htlc := entry.Value - restoredHtlc := bobChannelNew.localUpdateLog.lookupHtlc(htlc.HtlcIndex) + restoredHtlc := bobChannelNew.updateLogs.Local.lookupHtlc( + htlc.HtlcIndex, + ) if !bytes.Equal(htlc.ourPkScript, restoredHtlc.ourPkScript) { t.Fatalf("bob ourPkScript in ourLog: expected %X, got %X", htlc.ourPkScript[:5], restoredHtlc.ourPkScript[:5]) @@ -1956,9 +1972,11 @@ func TestStateUpdatePersistence(t *testing.T) { htlc.theirPkScript[:5], restoredHtlc.theirPkScript[:5]) } } - for _, entry := range bobChannel.remoteUpdateLog.htlcIndex { + for _, entry := range bobChannel.updateLogs.Remote.htlcIndex { htlc := entry.Value - restoredHtlc := bobChannelNew.remoteUpdateLog.lookupHtlc(htlc.HtlcIndex) + restoredHtlc := bobChannelNew.updateLogs.Remote.lookupHtlc( + htlc.HtlcIndex, + ) if !bytes.Equal(htlc.ourPkScript, restoredHtlc.ourPkScript) { t.Fatalf("bob ourPkScript in theirLog: expected %X, got %X", htlc.ourPkScript[:5], restoredHtlc.ourPkScript[:5]) @@ -3261,39 +3279,39 @@ func testChanSyncOweCommitment(t *testing.T, chanType channeldb.ChannelType) { // Alice's local log counter should be 4 and her HTLC index 3. She // should detect Bob's remote log counter as being 3 and his HTLC index // 3 as well. - if aliceChannel.localUpdateLog.logIndex != 4 { + if aliceChannel.updateLogs.Local.logIndex != 4 { t.Fatalf("incorrect log index: expected %v, got %v", 4, - aliceChannel.localUpdateLog.logIndex) + aliceChannel.updateLogs.Local.logIndex) } - if aliceChannel.localUpdateLog.htlcCounter != 1 { + if aliceChannel.updateLogs.Local.htlcCounter != 1 { t.Fatalf("incorrect htlc index: expected %v, got %v", 1, - aliceChannel.localUpdateLog.htlcCounter) + aliceChannel.updateLogs.Local.htlcCounter) } - if aliceChannel.remoteUpdateLog.logIndex != 3 { + if aliceChannel.updateLogs.Remote.logIndex != 3 { t.Fatalf("incorrect log index: expected %v, got %v", 3, - aliceChannel.localUpdateLog.logIndex) + aliceChannel.updateLogs.Local.logIndex) } - if aliceChannel.remoteUpdateLog.htlcCounter != 3 { + if aliceChannel.updateLogs.Remote.htlcCounter != 3 { t.Fatalf("incorrect htlc index: expected %v, got %v", 3, - aliceChannel.localUpdateLog.htlcCounter) + aliceChannel.updateLogs.Local.htlcCounter) } // Bob should also have the same state, but mirrored. - if bobChannel.localUpdateLog.logIndex != 3 { + if bobChannel.updateLogs.Local.logIndex != 3 { t.Fatalf("incorrect log index: expected %v, got %v", 3, - bobChannel.localUpdateLog.logIndex) + bobChannel.updateLogs.Local.logIndex) } - if bobChannel.localUpdateLog.htlcCounter != 3 { + if bobChannel.updateLogs.Local.htlcCounter != 3 { t.Fatalf("incorrect htlc index: expected %v, got %v", 3, - bobChannel.localUpdateLog.htlcCounter) + bobChannel.updateLogs.Local.htlcCounter) } - if bobChannel.remoteUpdateLog.logIndex != 4 { + if bobChannel.updateLogs.Remote.logIndex != 4 { t.Fatalf("incorrect log index: expected %v, got %v", 4, - bobChannel.localUpdateLog.logIndex) + bobChannel.updateLogs.Local.logIndex) } - if bobChannel.remoteUpdateLog.htlcCounter != 1 { + if bobChannel.updateLogs.Remote.htlcCounter != 1 { t.Fatalf("incorrect htlc index: expected %v, got %v", 1, - bobChannel.localUpdateLog.htlcCounter) + bobChannel.updateLogs.Local.htlcCounter) } // We'll conclude the test by having Bob settle Alice's HTLC, then @@ -4605,7 +4623,7 @@ func TestFeeUpdateOldDiskFormat(t *testing.T) { t.Helper() expUpd := expFee + expAdd - upd, fees := countLog(aliceChannel.localUpdateLog) + upd, fees := countLog(aliceChannel.updateLogs.Local) if upd != expUpd { t.Fatalf("expected %d updates, found %d in Alice's "+ "log", expUpd, upd) @@ -4614,7 +4632,7 @@ func TestFeeUpdateOldDiskFormat(t *testing.T) { t.Fatalf("expected %d fee updates, found %d in "+ "Alice's log", expFee, fees) } - upd, fees = countLog(bobChannel.remoteUpdateLog) + upd, fees = countLog(bobChannel.updateLogs.Remote) if upd != expUpd { t.Fatalf("expected %d updates, found %d in Bob's log", expUpd, upd) @@ -5313,7 +5331,7 @@ func TestChanCommitWeightDustHtlcs(t *testing.T) { lc.commitChains.Local.tip().theirMessageIndex htlcView := lc.fetchHTLCView(remoteACKedIndex, - lc.localUpdateLog.logIndex) + lc.updateLogs.Local.logIndex) _, w := lc.availableCommitmentBalance( htlcView, lntypes.Remote, FeeBuffer, @@ -7020,20 +7038,20 @@ func TestChannelRestoreUpdateLogs(t *testing.T) { // compare all the logs between the old and new channels, to make sure // they all got restored properly. - err = compareLogs(aliceChannel.localUpdateLog, - newAliceChannel.localUpdateLog) + err = compareLogs(aliceChannel.updateLogs.Local, + newAliceChannel.updateLogs.Local) require.NoError(t, err, "alice local log not restored") - err = compareLogs(aliceChannel.remoteUpdateLog, - newAliceChannel.remoteUpdateLog) + err = compareLogs(aliceChannel.updateLogs.Remote, + newAliceChannel.updateLogs.Remote) require.NoError(t, err, "alice remote log not restored") - err = compareLogs(bobChannel.localUpdateLog, - newBobChannel.localUpdateLog) + err = compareLogs(bobChannel.updateLogs.Local, + newBobChannel.updateLogs.Local) require.NoError(t, err, "bob local log not restored") - err = compareLogs(bobChannel.remoteUpdateLog, - newBobChannel.remoteUpdateLog) + err = compareLogs(bobChannel.updateLogs.Remote, + newBobChannel.updateLogs.Remote) require.NoError(t, err, "bob remote log not restored") } @@ -7065,10 +7083,10 @@ func assertInLog(t *testing.T, log *updateLog, numAdds, numFails int) { // assertInLogs asserts that the expected number of Adds and Fails occurs in // the local and remote update log of the given channel. func assertInLogs(t *testing.T, channel *LightningChannel, numAddsLocal, - numFailsLocal, numAddsRemote, numFailsRemote int, -) { - assertInLog(t, channel.localUpdateLog, numAddsLocal, numFailsLocal) - assertInLog(t, channel.remoteUpdateLog, numAddsRemote, numFailsRemote) + numFailsLocal, numAddsRemote, numFailsRemote int) { + + assertInLog(t, channel.updateLogs.Local, numAddsLocal, numFailsLocal) + assertInLog(t, channel.updateLogs.Remote, numAddsRemote, numFailsRemote) } // restoreAndAssert creates a new LightningChannel from the given channel's @@ -7083,8 +7101,10 @@ func restoreAndAssert(t *testing.T, channel *LightningChannel, numAddsLocal, ) require.NoError(t, err, "unable to create new channel") - assertInLog(t, newChannel.localUpdateLog, numAddsLocal, numFailsLocal) - assertInLog(t, newChannel.remoteUpdateLog, numAddsRemote, numFailsRemote) + assertInLog(t, newChannel.updateLogs.Local, numAddsLocal, numFailsLocal) + assertInLog( + t, newChannel.updateLogs.Remote, numAddsRemote, numFailsRemote, + ) } // TestChannelRestoreUpdateLogsFailedHTLC runs through a scenario where an @@ -7348,16 +7368,18 @@ func TestChannelRestoreCommitHeight(t *testing.T) { var pd *PaymentDescriptor if remoteLog { - if newChannel.localUpdateLog.lookupHtlc(htlcIndex) != nil { + h := newChannel.updateLogs.Local.lookupHtlc(htlcIndex) + if h != nil { t.Fatalf("htlc found in wrong log") } - pd = newChannel.remoteUpdateLog.lookupHtlc(htlcIndex) + pd = newChannel.updateLogs.Remote.lookupHtlc(htlcIndex) } else { - if newChannel.remoteUpdateLog.lookupHtlc(htlcIndex) != nil { + h := newChannel.updateLogs.Remote.lookupHtlc(htlcIndex) + if h != nil { t.Fatalf("htlc found in wrong log") } - pd = newChannel.localUpdateLog.lookupHtlc(htlcIndex) + pd = newChannel.updateLogs.Local.lookupHtlc(htlcIndex) } if pd == nil { t.Fatalf("htlc not found in log") @@ -8294,16 +8316,18 @@ func TestFetchParent(t *testing.T) { // Create a lightning channel with newly initialized // local and remote logs. lc := LightningChannel{ - localUpdateLog: newUpdateLog(0, 0), - remoteUpdateLog: newUpdateLog(0, 0), + updateLogs: lntypes.Dual[*updateLog]{ + Local: newUpdateLog(0, 0), + Remote: newUpdateLog(0, 0), + }, } // Add the local and remote entries to update logs. for _, entry := range test.localEntries { - lc.localUpdateLog.appendHtlc(entry) + lc.updateLogs.Local.appendHtlc(entry) } for _, entry := range test.remoteEntries { - lc.remoteUpdateLog.appendHtlc(entry) + lc.updateLogs.Remote.appendHtlc(entry) } parent, err := lc.fetchParent( @@ -8630,23 +8654,25 @@ func TestEvaluateView(t *testing.T) { }, // Create update logs for local and remote. - localUpdateLog: newUpdateLog(0, 0), - remoteUpdateLog: newUpdateLog(0, 0), + updateLogs: lntypes.Dual[*updateLog]{ + Local: newUpdateLog(0, 0), + Remote: newUpdateLog(0, 0), + }, } for _, htlc := range test.ourHtlcs { if htlc.EntryType == Add { - lc.localUpdateLog.appendHtlc(htlc) + lc.updateLogs.Local.appendHtlc(htlc) } else { - lc.localUpdateLog.appendUpdate(htlc) + lc.updateLogs.Local.appendUpdate(htlc) } } for _, htlc := range test.theirHtlcs { if htlc.EntryType == Add { - lc.remoteUpdateLog.appendHtlc(htlc) + lc.updateLogs.Remote.appendHtlc(htlc) } else { - lc.remoteUpdateLog.appendUpdate(htlc) + lc.updateLogs.Remote.appendUpdate(htlc) } } From b337213fb29f1eec0198c72ee621dfe9341b17fa Mon Sep 17 00:00:00 2001 From: Keagan McClelland Date: Fri, 9 Aug 2024 14:52:21 -0700 Subject: [PATCH 049/218] lnwallet: pack commitment message indices into Dual This is yet another commit that packs a symmetric structure into a Dual. This is the last one needed for the time being to consolidate Num{X}UpdatesPendingOn{Y} functions into a single one. --- lnwallet/channel.go | 108 +++++++++++++++++++++------------------ lnwallet/channel_test.go | 2 +- 2 files changed, 60 insertions(+), 50 deletions(-) diff --git a/lnwallet/channel.go b/lnwallet/channel.go index 8b41ff3eda..9bd72e4de1 100644 --- a/lnwallet/channel.go +++ b/lnwallet/channel.go @@ -282,8 +282,7 @@ type commitment struct { // new commitment sent to the remote party includes an index in the // shared log which details which of their updates we're including in // this new commitment. - ourMessageIndex uint64 - theirMessageIndex uint64 + messageIndices lntypes.Dual[uint64] // [our|their]HtlcIndex are the current running counters for the HTLCs // offered by either party. This value is incremented each time a party @@ -500,9 +499,9 @@ func (c *commitment) toDiskCommit( commit := &channeldb.ChannelCommitment{ CommitHeight: c.height, - LocalLogIndex: c.ourMessageIndex, + LocalLogIndex: c.messageIndices.Local, LocalHtlcIndex: c.ourHtlcIndex, - RemoteLogIndex: c.theirMessageIndex, + RemoteLogIndex: c.messageIndices.Remote, RemoteHtlcIndex: c.theirHtlcIndex, LocalBalance: c.ourBalance, RemoteBalance: c.theirBalance, @@ -772,24 +771,28 @@ func (lc *LightningChannel) diskCommitToMemCommit( return nil, err } + messageIndices := lntypes.Dual[uint64]{ + Local: diskCommit.LocalLogIndex, + Remote: diskCommit.RemoteLogIndex, + } + // With the necessary items generated, we'll now re-construct the // commitment state as it was originally present in memory. commit := &commitment{ - height: diskCommit.CommitHeight, - whoseCommit: whoseCommit, - ourBalance: diskCommit.LocalBalance, - theirBalance: diskCommit.RemoteBalance, - ourMessageIndex: diskCommit.LocalLogIndex, - ourHtlcIndex: diskCommit.LocalHtlcIndex, - theirMessageIndex: diskCommit.RemoteLogIndex, - theirHtlcIndex: diskCommit.RemoteHtlcIndex, - txn: diskCommit.CommitTx, - sig: diskCommit.CommitSig, - fee: diskCommit.CommitFee, - feePerKw: chainfee.SatPerKWeight(diskCommit.FeePerKw), - incomingHTLCs: incomingHtlcs, - outgoingHTLCs: outgoingHtlcs, - customBlob: diskCommit.CustomBlob, + height: diskCommit.CommitHeight, + whoseCommit: whoseCommit, + ourBalance: diskCommit.LocalBalance, + theirBalance: diskCommit.RemoteBalance, + messageIndices: messageIndices, + ourHtlcIndex: diskCommit.LocalHtlcIndex, + theirHtlcIndex: diskCommit.RemoteHtlcIndex, + txn: diskCommit.CommitTx, + sig: diskCommit.CommitSig, + fee: diskCommit.CommitFee, + feePerKw: chainfee.SatPerKWeight(diskCommit.FeePerKw), + incomingHTLCs: incomingHtlcs, + outgoingHTLCs: outgoingHtlcs, + customBlob: diskCommit.CustomBlob, } if whoseCommit.IsLocal() { commit.dustLimit = lc.channelState.LocalChanCfg.DustLimit @@ -1758,7 +1761,7 @@ func (lc *LightningChannel) restorePendingRemoteUpdates( // height as this commitment will include these updates for // their new remote commitment. if pendingRemoteCommit != nil { - if logIdx < pendingRemoteCommit.theirMessageIndex { + if logIdx < pendingRemoteCommit.messageIndices.Remote { height = pendingRemoteCommit.height heightSet = true } @@ -2751,22 +2754,26 @@ func (lc *LightningChannel) fetchCommitmentView( return nil, fmt.Errorf("unable to fetch aux leaves: %w", err) } + messageIndices := lntypes.Dual[uint64]{ + Local: ourLogIndex, + Remote: theirLogIndex, + } + // With the commitment view created, store the resulting balances and // transaction with the other parameters for this height. c := &commitment{ - ourBalance: commitTx.ourBalance, - theirBalance: commitTx.theirBalance, - txn: commitTx.txn, - fee: commitTx.fee, - ourMessageIndex: ourLogIndex, - ourHtlcIndex: ourHtlcIndex, - theirMessageIndex: theirLogIndex, - theirHtlcIndex: theirHtlcIndex, - height: nextHeight, - feePerKw: feePerKw, - dustLimit: dustLimit, - whoseCommit: whoseCommitChain, - customBlob: newCommitBlob, + ourBalance: commitTx.ourBalance, + theirBalance: commitTx.theirBalance, + txn: commitTx.txn, + fee: commitTx.fee, + messageIndices: messageIndices, + ourHtlcIndex: ourHtlcIndex, + theirHtlcIndex: theirHtlcIndex, + height: nextHeight, + feePerKw: feePerKw, + dustLimit: dustLimit, + whoseCommit: whoseCommitChain, + customBlob: newCommitBlob, } // In order to ensure _none_ of the HTLC's associated with this new @@ -3493,10 +3500,12 @@ func (lc *LightningChannel) getUnsignedAckedUpdates() []channeldb.LogUpdate { chanID := lnwire.NewChanIDFromOutPoint(lc.channelState.FundingOutpoint) // Fetch the last remote update that we have signed for. - lastRemoteCommitted := lc.commitChains.Remote.tail().theirMessageIndex + lastRemoteCommitted := + lc.commitChains.Remote.tail().messageIndices.Remote // Fetch the last remote update that we have acked. - lastLocalCommitted := lc.commitChains.Local.tail().theirMessageIndex + lastLocalCommitted := + lc.commitChains.Local.tail().messageIndices.Remote // We'll now run through the remote update log to locate the items that // we haven't signed for yet. This will be the set of items we need to @@ -3982,7 +3991,7 @@ func (lc *LightningChannel) SignNextCommitment() (*NewCommitState, error) { } // Determine the last update on the remote log that has been locked in. - remoteACKedIndex := lc.commitChains.Local.tail().theirMessageIndex + remoteACKedIndex := lc.commitChains.Local.tail().messageIndices.Remote remoteHtlcIndex := lc.commitChains.Local.tail().theirHtlcIndex // Before we extend this new commitment to the remote commitment chain, @@ -5014,7 +5023,7 @@ func (lc *LightningChannel) ReceiveNewCommitment(commitSigs *CommitSigs) error { } // Determine the last update on the local log that has been locked in. - localACKedIndex := lc.commitChains.Remote.tail().ourMessageIndex + localACKedIndex := lc.commitChains.Remote.tail().messageIndices.Local localHtlcIndex := lc.commitChains.Remote.tail().ourHtlcIndex // Ensure that this new local update from the remote node respects all @@ -5345,13 +5354,13 @@ func (lc *LightningChannel) oweCommitment(issuer lntypes.ChannelParty) bool { // There are local updates pending if our local update log is // not in sync with our remote commitment tx. localUpdatesPending = lc.updateLogs.Local.logIndex != - lastRemoteCommit.ourMessageIndex + lastRemoteCommit.messageIndices.Local // There are remote updates pending if their remote commitment // tx (our local commitment tx) contains updates that we don't // have added to our remote commitment tx yet. - remoteUpdatesPending = lastLocalCommit.theirMessageIndex != - lastRemoteCommit.theirMessageIndex + remoteUpdatesPending = lastLocalCommit.messageIndices.Remote != + lastRemoteCommit.messageIndices.Remote } else { perspective = "remote" @@ -5360,13 +5369,13 @@ func (lc *LightningChannel) oweCommitment(issuer lntypes.ChannelParty) bool { // updates to their remote tx pending for which they haven't // signed yet. localUpdatesPending = lc.updateLogs.Remote.logIndex != - lastLocalCommit.theirMessageIndex + lastLocalCommit.messageIndices.Remote // There are remote updates pending (remote updates from the // perspective of the remote party) if we have updates on our // remote commitment tx that they haven't added to theirs yet. - remoteUpdatesPending = lastRemoteCommit.ourMessageIndex != - lastLocalCommit.ourMessageIndex + remoteUpdatesPending = lastRemoteCommit.messageIndices.Local != + lastLocalCommit.messageIndices.Local } // If any of the conditions above is true, we owe a commitment @@ -5388,7 +5397,8 @@ func (lc *LightningChannel) PendingLocalUpdateCount() uint64 { lastRemoteCommit := lc.commitChains.Remote.tip() - return lc.updateLogs.Local.logIndex - lastRemoteCommit.ourMessageIndex + return lc.updateLogs.Local.logIndex - + lastRemoteCommit.messageIndices.Local } // RevokeCurrentCommitment revokes the next lowest unrevoked commitment @@ -5647,8 +5657,8 @@ func (lc *LightningChannel) ReceiveRevocation(revMsg *lnwire.RevokeAndAck) ( // We use the remote commitment chain's tip as it will soon become the tail // once advanceTail is called. - remoteMessageIndex := lc.commitChains.Remote.tip().ourMessageIndex - localMessageIndex := lc.commitChains.Local.tail().ourMessageIndex + remoteMessageIndex := lc.commitChains.Remote.tip().messageIndices.Local + localMessageIndex := lc.commitChains.Local.tail().messageIndices.Local localPeerUpdates := lc.unsignedLocalUpdates( remoteMessageIndex, localMessageIndex, chanID, @@ -5970,7 +5980,7 @@ func (lc *LightningChannel) validateAddHtlc(pd *PaymentDescriptor, buffer BufferType) error { // Make sure adding this HTLC won't violate any of the constraints we // must keep on the commitment transactions. - remoteACKedIndex := lc.commitChains.Local.tail().theirMessageIndex + remoteACKedIndex := lc.commitChains.Local.tail().messageIndices.Remote // First we'll check whether this HTLC can be added to the remote // commitment transaction without violation any of the constraints. @@ -6026,7 +6036,7 @@ func (lc *LightningChannel) ReceiveHTLC(htlc *lnwire.UpdateAddHTLC) (uint64, // PR). } - localACKedIndex := lc.commitChains.Remote.tail().ourMessageIndex + localACKedIndex := lc.commitChains.Remote.tail().messageIndices.Local // Clamp down on the number of HTLC's we can receive by checking the // commitment sanity. @@ -8172,7 +8182,7 @@ func (lc *LightningChannel) availableBalance( // We'll grab the current set of log updates that the remote has // ACKed. - remoteACKedIndex := lc.commitChains.Local.tip().theirMessageIndex + remoteACKedIndex := lc.commitChains.Local.tip().messageIndices.Remote htlcView := lc.fetchHTLCView(remoteACKedIndex, lc.updateLogs.Local.logIndex) diff --git a/lnwallet/channel_test.go b/lnwallet/channel_test.go index 04465bc278..532d7b9a6e 100644 --- a/lnwallet/channel_test.go +++ b/lnwallet/channel_test.go @@ -5328,7 +5328,7 @@ func TestChanCommitWeightDustHtlcs(t *testing.T) { // transaction. remoteCommitWeight := func(lc *LightningChannel) lntypes.WeightUnit { remoteACKedIndex := - lc.commitChains.Local.tip().theirMessageIndex + lc.commitChains.Local.tip().messageIndices.Remote htlcView := lc.fetchHTLCView(remoteACKedIndex, lc.updateLogs.Local.logIndex) From d4a4233e96aa76b25812c573140140b7b5fa3ffb Mon Sep 17 00:00:00 2001 From: Keagan McClelland Date: Mon, 11 Dec 2023 19:18:57 -0800 Subject: [PATCH 050/218] lnwallet+htlcswitch: define expanded NumPendingUpdates This commit squashes the below operations for a net result where we have an expanded capability of assessing pending updates. This is made possible by packing the components into Duals in the prior commits. We squash the operations to simplify review. htlcswitch+lnwallet: rename PendingLocalUpdateCount lnwallet: complete pending update queries API for LightningChannel lnwallet+htlcswitch: consolidate NumPendingUpdates using ChannelParty This commit makes the observation that we can cleanly define the NumPendingUpdates function using a single expression by taking advantage of the relevant fields being properly packed into Duals. --- htlcswitch/link.go | 24 ++++++++++++++---------- lnwallet/channel.go | 14 ++++++++------ 2 files changed, 22 insertions(+), 16 deletions(-) diff --git a/htlcswitch/link.go b/htlcswitch/link.go index c459ef9974..81c82cfa25 100644 --- a/htlcswitch/link.go +++ b/htlcswitch/link.go @@ -958,7 +958,7 @@ func (l *channelLink) resolveFwdPkgs() error { // If any of our reprocessing steps require an update to the commitment // txn, we initiate a state transition to capture all relevant changes. - if l.channel.PendingLocalUpdateCount() > 0 { + if l.channel.NumPendingUpdates(lntypes.Local, lntypes.Remote) > 0 { return l.updateCommitTx() } @@ -1286,15 +1286,19 @@ func (l *channelLink) htlcManager() { // the batch ticker so that it can be cleared. Otherwise pause // the ticker to prevent waking up the htlcManager while the // batch is empty. - if l.channel.PendingLocalUpdateCount() > 0 { + numUpdates := l.channel.NumPendingUpdates( + lntypes.Local, lntypes.Remote, + ) + if numUpdates > 0 { l.cfg.BatchTicker.Resume() l.log.Tracef("BatchTicker resumed, "+ - "PendingLocalUpdateCount=%d", - l.channel.PendingLocalUpdateCount()) + "NumPendingUpdates(Local, Remote)=%d", + numUpdates, + ) } else { l.cfg.BatchTicker.Pause() l.log.Trace("BatchTicker paused due to zero " + - "PendingLocalUpdateCount") + "NumPendingUpdates(Local, Remote)") } select { @@ -1652,7 +1656,7 @@ func (l *channelLink) handleDownstreamUpdateAdd(pkt *htlcPacket) error { l.log.Tracef("received downstream htlc: payment_hash=%x, "+ "local_log_index=%v, pend_updates=%v", htlc.PaymentHash[:], index, - l.channel.PendingLocalUpdateCount()) + l.channel.NumPendingUpdates(lntypes.Local, lntypes.Remote)) pkt.outgoingChanID = l.ShortChanID() pkt.outgoingHTLCID = index @@ -1858,7 +1862,8 @@ func (l *channelLink) handleDownstreamPkt(pkt *htlcPacket) { // tryBatchUpdateCommitTx updates the commitment transaction if the batch is // full. func (l *channelLink) tryBatchUpdateCommitTx() { - if l.channel.PendingLocalUpdateCount() < uint64(l.cfg.BatchSize) { + pending := l.channel.NumPendingUpdates(lntypes.Local, lntypes.Remote) + if pending < uint64(l.cfg.BatchSize) { return } @@ -1934,7 +1939,6 @@ func (l *channelLink) cleanupSpuriousResponse(pkt *htlcPacket) { // direct channel with, updating our respective commitment chains. func (l *channelLink) handleUpstreamMsg(msg lnwire.Message) { switch msg := msg.(type) { - case *lnwire.UpdateAddHTLC: if l.IsFlushing(Incoming) { // This is forbidden by the protocol specification. @@ -2592,9 +2596,9 @@ func (l *channelLink) updateCommitTx() error { l.cfg.PendingCommitTicker.Resume() l.log.Trace("PendingCommitTicker resumed") + n := l.channel.NumPendingUpdates(lntypes.Local, lntypes.Remote) l.log.Tracef("revocation window exhausted, unable to send: "+ - "%v, pend_updates=%v, dangling_closes%v", - l.channel.PendingLocalUpdateCount(), + "%v, pend_updates=%v, dangling_closes%v", n, lnutils.SpewLogClosure(l.openedCircuits), lnutils.SpewLogClosure(l.closedCircuits)) diff --git a/lnwallet/channel.go b/lnwallet/channel.go index 9bd72e4de1..8dba079736 100644 --- a/lnwallet/channel.go +++ b/lnwallet/channel.go @@ -5389,16 +5389,18 @@ func (lc *LightningChannel) oweCommitment(issuer lntypes.ChannelParty) bool { return oweCommitment } -// PendingLocalUpdateCount returns the number of local updates that still need -// to be applied to the remote commitment tx. -func (lc *LightningChannel) PendingLocalUpdateCount() uint64 { +// NumPendingUpdates returns the number of updates originated by whoseUpdates +// that have not been committed to the *tip* of whoseCommit's commitment chain. +func (lc *LightningChannel) NumPendingUpdates(whoseUpdates lntypes.ChannelParty, + whoseCommit lntypes.ChannelParty) uint64 { + lc.RLock() defer lc.RUnlock() - lastRemoteCommit := lc.commitChains.Remote.tip() + lastCommit := lc.commitChains.GetForParty(whoseCommit).tip() + updateIndex := lc.updateLogs.GetForParty(whoseUpdates).logIndex - return lc.updateLogs.Local.logIndex - - lastRemoteCommit.messageIndices.Local + return updateIndex - lastCommit.messageIndices.GetForParty(whoseUpdates) } // RevokeCurrentCommitment revokes the next lowest unrevoked commitment From 0c95dc2118baa5a7eda865871629d51908af7b53 Mon Sep 17 00:00:00 2001 From: George Tsagkarelis Date: Mon, 4 Mar 2024 21:46:46 +0100 Subject: [PATCH 051/218] aliasmgr: add delete local alias method --- aliasmgr/aliasmgr.go | 60 ++++++++++++++++++++++++++++++++++ aliasmgr/aliasmgr_test.go | 68 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 128 insertions(+) diff --git a/aliasmgr/aliasmgr.go b/aliasmgr/aliasmgr.go index f33eb391eb..cae637f331 100644 --- a/aliasmgr/aliasmgr.go +++ b/aliasmgr/aliasmgr.go @@ -5,6 +5,7 @@ import ( "fmt" "sync" + "github.com/lightningnetwork/lnd/fn" "github.com/lightningnetwork/lnd/htlcswitch/hop" "github.com/lightningnetwork/lnd/kvdb" "github.com/lightningnetwork/lnd/lnwire" @@ -68,6 +69,10 @@ var ( // errNoPeerAlias is returned when the peer's alias for a given // channel is not found. errNoPeerAlias = fmt.Errorf("no peer alias found") + + // ErrAliasNotFound is returned when the alias is not found and can't + // be mapped to a base SCID. + ErrAliasNotFound = fmt.Errorf("alias not found") ) // Manager is a struct that handles aliases for LND. It has an underlying @@ -340,6 +345,61 @@ func (m *Manager) DeleteSixConfs(baseScid lnwire.ShortChannelID) error { return nil } +// DeleteLocalAlias removes a mapping from the database and the Manager's maps. +func (m *Manager) DeleteLocalAlias(alias, + baseScid lnwire.ShortChannelID) error { + + m.Lock() + defer m.Unlock() + + err := kvdb.Update(m.backend, func(tx kvdb.RwTx) error { + aliasToBaseBucket, err := tx.CreateTopLevelBucket(aliasBucket) + if err != nil { + return err + } + + var aliasBytes [8]byte + byteOrder.PutUint64(aliasBytes[:], alias.ToUint64()) + + // If the user attempts to delete an alias that doesn't exist, + // we'll want to inform them about it and not just do nothing. + if aliasToBaseBucket.Get(aliasBytes[:]) == nil { + return ErrAliasNotFound + } + + return aliasToBaseBucket.Delete(aliasBytes[:]) + }, func() {}) + if err != nil { + return err + } + + // Now that the database state has been updated, we'll delete the + // mapping from the Manager's maps. + aliasSet, ok := m.baseToSet[baseScid] + if !ok { + return ErrAliasNotFound + } + + // We'll filter the alias set and remove the alias from it. + aliasSet = fn.Filter(func(a lnwire.ShortChannelID) bool { + return a.ToUint64() != alias.ToUint64() + }, aliasSet) + + // If the alias set is empty, we'll delete the base SCID from the + // baseToSet map. + if len(aliasSet) == 0 { + delete(m.baseToSet, baseScid) + } else { + m.baseToSet[baseScid] = aliasSet + } + + // Finally, we'll delete the aliasToBase mapping from the Manager's + // cache (but this is only set if we gossip the alias). + delete(m.aliasToBase, alias) + + return nil +} + // PutPeerAlias stores the peer's alias SCID once we learn of it in the // channel_ready message. func (m *Manager) PutPeerAlias(chanID lnwire.ChannelID, diff --git a/aliasmgr/aliasmgr_test.go b/aliasmgr/aliasmgr_test.go index 17159ed877..e0e78ba703 100644 --- a/aliasmgr/aliasmgr_test.go +++ b/aliasmgr/aliasmgr_test.go @@ -68,6 +68,74 @@ func TestAliasStoreRequest(t *testing.T) { require.Equal(t, nextAlias, alias2) } +// TestAliasLifecycle tests that the aliases can be created and deleted. +func TestAliasLifecycle(t *testing.T) { + t.Parallel() + + // Create the backend database and use this to create the aliasStore. + dbPath := filepath.Join(t.TempDir(), "testdb") + db, err := kvdb.Create( + kvdb.BoltBackendName, dbPath, true, kvdb.DefaultDBTimeout, + ) + require.NoError(t, err) + defer db.Close() + + aliasStore, err := NewManager(db) + require.NoError(t, err) + + const ( + base = uint64(123123123) + alias = uint64(456456456) + ) + + // Parse the aliases and base to short channel ID format. + baseScid := lnwire.NewShortChanIDFromInt(base) + aliasScid := lnwire.NewShortChanIDFromInt(alias) + aliasScid2 := lnwire.NewShortChanIDFromInt(alias + 1) + + // Add the first alias. + err = aliasStore.AddLocalAlias(aliasScid, baseScid, false) + require.NoError(t, err) + + // Query the aliases and verify the results. + aliasList := aliasStore.GetAliases(baseScid) + require.Len(t, aliasList, 1) + require.Contains(t, aliasList, aliasScid) + + // Add the second alias. + err = aliasStore.AddLocalAlias(aliasScid2, baseScid, false) + require.NoError(t, err) + + // Query the aliases and verify the results. + aliasList = aliasStore.GetAliases(baseScid) + require.Len(t, aliasList, 2) + require.Contains(t, aliasList, aliasScid) + require.Contains(t, aliasList, aliasScid2) + + // Delete the first alias. + err = aliasStore.DeleteLocalAlias(aliasScid, baseScid) + require.NoError(t, err) + + // We expect to get an error if we attempt to delete the same alias + // again. + err = aliasStore.DeleteLocalAlias(aliasScid, baseScid) + require.ErrorIs(t, err, ErrAliasNotFound) + + // Query the aliases and verify that first one doesn't exist anymore. + aliasList = aliasStore.GetAliases(baseScid) + require.Len(t, aliasList, 1) + require.Contains(t, aliasList, aliasScid2) + require.NotContains(t, aliasList, aliasScid) + + // Delete the second alias. + err = aliasStore.DeleteLocalAlias(aliasScid2, baseScid) + require.NoError(t, err) + + // Query the aliases and verify that none exists. + aliasList = aliasStore.GetAliases(baseScid) + require.Len(t, aliasList, 0) +} + // TestGetNextScid tests that given a current lnwire.ShortChannelID, // getNextScid returns the expected alias to use next. func TestGetNextScid(t *testing.T) { From 4ef68512a9c66b874a497613b6ce292323fd975b Mon Sep 17 00:00:00 2001 From: George Tsagkarelis Date: Tue, 12 Mar 2024 18:15:14 +0100 Subject: [PATCH 052/218] multi: refresh htlcswitch aliases on aliasmgr update --- aliasmgr/aliasmgr.go | 64 ++++++++++++++++++++++++++++++++++----- aliasmgr/aliasmgr_test.go | 44 ++++++++++++++++++++++++--- funding/interfaces.go | 3 +- funding/manager.go | 8 ++--- funding/manager_test.go | 2 +- htlcswitch/switch.go | 35 +++++++++++++++++++++ peer/brontide.go | 3 +- server.go | 13 +++++++- 8 files changed, 152 insertions(+), 20 deletions(-) diff --git a/aliasmgr/aliasmgr.go b/aliasmgr/aliasmgr.go index cae637f331..389004cd88 100644 --- a/aliasmgr/aliasmgr.go +++ b/aliasmgr/aliasmgr.go @@ -11,6 +11,11 @@ import ( "github.com/lightningnetwork/lnd/lnwire" ) +// UpdateLinkAliases is a function type for a function that locates the active +// link that matches the given shortID and triggers an update based on the +// latest values of the alias manager. +type UpdateLinkAliases func(shortID lnwire.ShortChannelID) error + var ( // aliasBucket stores aliases as keys and their base SCIDs as values. // This is used to populate the maps that the Manager uses. The keys @@ -82,6 +87,10 @@ var ( type Manager struct { backend kvdb.Backend + // linkAliasUpdater is a function used by the alias manager to + // facilitate live update of aliases in other subsystems. + linkAliasUpdater UpdateLinkAliases + // baseToSet is a mapping from the "base" SCID to the set of aliases // for this channel. This mapping includes all channels that // negotiated the option-scid-alias feature bit. @@ -103,8 +112,14 @@ type Manager struct { } // NewManager initializes an alias Manager from the passed database backend. -func NewManager(db kvdb.Backend) (*Manager, error) { - m := &Manager{backend: db} +func NewManager(db kvdb.Backend, linkAliasUpdater UpdateLinkAliases) (*Manager, + error) { + + m := &Manager{ + backend: db, + linkAliasUpdater: linkAliasUpdater, + } + m.baseToSet = make(map[lnwire.ShortChannelID][]lnwire.ShortChannelID) m.aliasToBase = make(map[lnwire.ShortChannelID]lnwire.ShortChannelID) m.peerAlias = make(map[lnwire.ChannelID]lnwire.ShortChannelID) @@ -220,12 +235,22 @@ func (m *Manager) populateMaps() error { // AddLocalAlias adds a database mapping from the passed alias to the passed // base SCID. The gossip boolean marks whether or not to create a mapping // that the gossiper will use. It is set to false for the upgrade path where -// the feature-bit is toggled on and there are existing channels. +// the feature-bit is toggled on and there are existing channels. The linkUpdate +// flag is used to signal whether this function should also trigger an update +// on the htlcswitch scid alias maps. func (m *Manager) AddLocalAlias(alias, baseScid lnwire.ShortChannelID, - gossip bool) error { + gossip, linkUpdate bool) error { + // We need to lock the manager for the whole duration of this method, + // except for the very last part where we call the link updater. In + // order for us to safely use a defer _and_ still be able to manually + // unlock, we use a sync.Once. m.Lock() - defer m.Unlock() + unlockOnce := sync.Once{} + unlock := func() { + unlockOnce.Do(m.Unlock) + } + defer unlock() err := kvdb.Update(m.backend, func(tx kvdb.RwTx) error { // If the caller does not want to allow the alias to be used @@ -275,6 +300,18 @@ func (m *Manager) AddLocalAlias(alias, baseScid lnwire.ShortChannelID, m.aliasToBase[alias] = baseScid } + // We definitely need to unlock the Manager before calling the link + // updater. If we don't, we'll deadlock. We use a sync.Once to ensure + // that we only unlock once. + unlock() + + // Finally, we trigger a htlcswitch update if the flag is set, in order + // for any future htlc that references the added alias to be properly + // routed. + if linkUpdate { + return m.linkAliasUpdater(baseScid) + } + return nil } @@ -349,8 +386,16 @@ func (m *Manager) DeleteSixConfs(baseScid lnwire.ShortChannelID) error { func (m *Manager) DeleteLocalAlias(alias, baseScid lnwire.ShortChannelID) error { + // We need to lock the manager for the whole duration of this method, + // except for the very last part where we call the link updater. In + // order for us to safely use a defer _and_ still be able to manually + // unlock, we use a sync.Once. m.Lock() - defer m.Unlock() + unlockOnce := sync.Once{} + unlock := func() { + unlockOnce.Do(m.Unlock) + } + defer unlock() err := kvdb.Update(m.backend, func(tx kvdb.RwTx) error { aliasToBaseBucket, err := tx.CreateTopLevelBucket(aliasBucket) @@ -397,7 +442,12 @@ func (m *Manager) DeleteLocalAlias(alias, // cache (but this is only set if we gossip the alias). delete(m.aliasToBase, alias) - return nil + // We definitely need to unlock the Manager before calling the link + // updater. If we don't, we'll deadlock. We use a sync.Once to ensure + // that we only unlock once. + unlock() + + return m.linkAliasUpdater(baseScid) } // PutPeerAlias stores the peer's alias SCID once we learn of it in the diff --git a/aliasmgr/aliasmgr_test.go b/aliasmgr/aliasmgr_test.go index e0e78ba703..c32de5135b 100644 --- a/aliasmgr/aliasmgr_test.go +++ b/aliasmgr/aliasmgr_test.go @@ -23,7 +23,11 @@ func TestAliasStorePeerAlias(t *testing.T) { require.NoError(t, err) defer db.Close() - aliasStore, err := NewManager(db) + linkUpdater := func(shortID lnwire.ShortChannelID) error { + return nil + } + + aliasStore, err := NewManager(db, linkUpdater) require.NoError(t, err) var chanID1 [32]byte @@ -52,7 +56,11 @@ func TestAliasStoreRequest(t *testing.T) { require.NoError(t, err) defer db.Close() - aliasStore, err := NewManager(db) + linkUpdater := func(shortID lnwire.ShortChannelID) error { + return nil + } + + aliasStore, err := NewManager(db, linkUpdater) require.NoError(t, err) // We'll assert that the very first alias we receive is StartingAlias. @@ -80,7 +88,14 @@ func TestAliasLifecycle(t *testing.T) { require.NoError(t, err) defer db.Close() - aliasStore, err := NewManager(db) + updateChan := make(chan struct{}, 1) + + linkUpdater := func(shortID lnwire.ShortChannelID) error { + updateChan <- struct{}{} + return nil + } + + aliasStore, err := NewManager(db, linkUpdater) require.NoError(t, err) const ( @@ -94,18 +109,24 @@ func TestAliasLifecycle(t *testing.T) { aliasScid2 := lnwire.NewShortChanIDFromInt(alias + 1) // Add the first alias. - err = aliasStore.AddLocalAlias(aliasScid, baseScid, false) + err = aliasStore.AddLocalAlias(aliasScid, baseScid, false, true) require.NoError(t, err) + // The link updater should be called. + <-updateChan + // Query the aliases and verify the results. aliasList := aliasStore.GetAliases(baseScid) require.Len(t, aliasList, 1) require.Contains(t, aliasList, aliasScid) // Add the second alias. - err = aliasStore.AddLocalAlias(aliasScid2, baseScid, false) + err = aliasStore.AddLocalAlias(aliasScid2, baseScid, false, true) require.NoError(t, err) + // The link updater should be called. + <-updateChan + // Query the aliases and verify the results. aliasList = aliasStore.GetAliases(baseScid) require.Len(t, aliasList, 2) @@ -116,11 +137,21 @@ func TestAliasLifecycle(t *testing.T) { err = aliasStore.DeleteLocalAlias(aliasScid, baseScid) require.NoError(t, err) + // The link updater should be called. + <-updateChan + // We expect to get an error if we attempt to delete the same alias // again. err = aliasStore.DeleteLocalAlias(aliasScid, baseScid) require.ErrorIs(t, err, ErrAliasNotFound) + // The link updater should _not_ be called. + select { + case <-updateChan: + t.Fatal("link alias updater should not have been called") + default: + } + // Query the aliases and verify that first one doesn't exist anymore. aliasList = aliasStore.GetAliases(baseScid) require.Len(t, aliasList, 1) @@ -131,6 +162,9 @@ func TestAliasLifecycle(t *testing.T) { err = aliasStore.DeleteLocalAlias(aliasScid2, baseScid) require.NoError(t, err) + // The link updater should be called. + <-updateChan + // Query the aliases and verify that none exists. aliasList = aliasStore.GetAliases(baseScid) require.Len(t, aliasList, 0) diff --git a/funding/interfaces.go b/funding/interfaces.go index b87705424b..30cae08407 100644 --- a/funding/interfaces.go +++ b/funding/interfaces.go @@ -36,7 +36,8 @@ type aliasHandler interface { GetPeerAlias(lnwire.ChannelID) (lnwire.ShortChannelID, error) // AddLocalAlias persists an alias to an underlying alias store. - AddLocalAlias(lnwire.ShortChannelID, lnwire.ShortChannelID, bool) error + AddLocalAlias(lnwire.ShortChannelID, lnwire.ShortChannelID, bool, + bool) error // GetAliases returns the set of aliases given the main SCID of a // channel. This SCID will be an alias for zero-conf channels and will diff --git a/funding/manager.go b/funding/manager.go index 6bb027725d..fb36bd9903 100644 --- a/funding/manager.go +++ b/funding/manager.go @@ -1263,7 +1263,7 @@ func (f *Manager) advancePendingChannelState( // Persist the alias to the alias database. baseScid := channel.ShortChannelID err := f.cfg.AliasManager.AddLocalAlias( - baseScid, baseScid, true, + baseScid, baseScid, true, false, ) if err != nil { return fmt.Errorf("error adding local alias to "+ @@ -3149,7 +3149,7 @@ func (f *Manager) handleFundingConfirmation( } err = f.cfg.AliasManager.AddLocalAlias( - aliasScid, confChannel.shortChanID, true, + aliasScid, confChannel.shortChanID, true, false, ) if err != nil { return fmt.Errorf("unable to request alias: %w", err) @@ -3315,7 +3315,7 @@ func (f *Manager) sendChannelReady(completeChan *channeldb.OpenChannel, err = f.cfg.AliasManager.AddLocalAlias( alias, completeChan.ShortChannelID, - false, + false, false, ) if err != nil { return err @@ -3892,7 +3892,7 @@ func (f *Manager) handleChannelReady(peer lnpeer.Peer, //nolint:funlen } err = f.cfg.AliasManager.AddLocalAlias( - alias, channel.ShortChannelID, false, + alias, channel.ShortChannelID, false, false, ) if err != nil { log.Errorf("unable to add local alias: %v", diff --git a/funding/manager_test.go b/funding/manager_test.go index 471d0209fb..d1aa9ec146 100644 --- a/funding/manager_test.go +++ b/funding/manager_test.go @@ -162,7 +162,7 @@ func (m *mockAliasMgr) GetPeerAlias(lnwire.ChannelID) (lnwire.ShortChannelID, } func (m *mockAliasMgr) AddLocalAlias(lnwire.ShortChannelID, - lnwire.ShortChannelID, bool) error { + lnwire.ShortChannelID, bool, bool) error { return nil } diff --git a/htlcswitch/switch.go b/htlcswitch/switch.go index efd469f785..a839ec3bf0 100644 --- a/htlcswitch/switch.go +++ b/htlcswitch/switch.go @@ -2090,6 +2090,26 @@ func (s *Switch) addLiveLink(link ChannelLink) { } s.interfaceIndex[peerPub][link.ChanID()] = link + s.updateLinkAliases(link) +} + +// UpdateLinkAliases is the externally exposed wrapper for updating link +// aliases. It acquires the indexMtx and calls the internal method. +func (s *Switch) UpdateLinkAliases(link ChannelLink) { + s.indexMtx.Lock() + defer s.indexMtx.Unlock() + + s.updateLinkAliases(link) +} + +// updateLinkAliases updates the aliases for a given link. This will cause the +// htlcswitch to consult the alias manager on the up to date values of its +// alias maps. +// +// NOTE: this MUST be called with the indexMtx held. +func (s *Switch) updateLinkAliases(link ChannelLink) { + linkScid := link.ShortChanID() + aliases := link.getAliases() if link.isZeroConf() { if link.zeroConfConfirmed() { @@ -2114,6 +2134,21 @@ func (s *Switch) addLiveLink(link ChannelLink) { s.baseIndex[alias] = linkScid } } else if link.negotiatedAliasFeature() { + // First, we flush any alias mappings for this link's scid + // before we populate the map again, in order to get rid of old + // values that no longer exist. + for alias, real := range s.aliasToReal { + if real == linkScid { + delete(s.aliasToReal, alias) + } + } + + for alias, real := range s.baseIndex { + if real == linkScid { + delete(s.baseIndex, alias) + } + } + // The link's SCID is the confirmed SCID for non-zero-conf // option-scid-alias feature bit channels. for _, alias := range aliases { diff --git a/peer/brontide.go b/peer/brontide.go index 1324044da3..bbb4aa287d 100644 --- a/peer/brontide.go +++ b/peer/brontide.go @@ -370,7 +370,7 @@ type Config struct { // AddLocalAlias persists an alias to an underlying alias store. AddLocalAlias func(alias, base lnwire.ShortChannelID, - gossip bool) error + gossip, liveUpdate bool) error // AuxLeafStore is an optional store that can be used to store auxiliary // leaves for certain custom channel types. @@ -912,6 +912,7 @@ func (p *Brontide) loadActiveChannels(chans []*channeldb.OpenChannel) ( err = p.cfg.AddLocalAlias( aliasScid, dbChan.ShortChanID(), false, + false, ) if err != nil { return nil, err diff --git a/server.go b/server.go index 21c65399e2..257bde4bfc 100644 --- a/server.go +++ b/server.go @@ -644,7 +644,18 @@ func newServer(cfg *Config, listenAddrs []net.Addr, thresholdSats := btcutil.Amount(cfg.MaxFeeExposure) thresholdMSats := lnwire.NewMSatFromSatoshis(thresholdSats) - s.aliasMgr, err = aliasmgr.NewManager(dbs.ChanStateDB) + linkUpdater := func(shortID lnwire.ShortChannelID) error { + link, err := s.htlcSwitch.GetLinkByShortID(shortID) + if err != nil { + return err + } + + s.htlcSwitch.UpdateLinkAliases(link) + + return nil + } + + s.aliasMgr, err = aliasmgr.NewManager(dbs.ChanStateDB, linkUpdater) if err != nil { return nil, err } From 4757e891a2add42aaff49b49cb4d83c707635ed4 Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Fri, 9 Aug 2024 15:12:41 +0200 Subject: [PATCH 053/218] aliasmgr: add map type alias --- aliasmgr/aliasmgr.go | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/aliasmgr/aliasmgr.go b/aliasmgr/aliasmgr.go index 389004cd88..29ac2c44c2 100644 --- a/aliasmgr/aliasmgr.go +++ b/aliasmgr/aliasmgr.go @@ -16,6 +16,10 @@ import ( // latest values of the alias manager. type UpdateLinkAliases func(shortID lnwire.ShortChannelID) error +// ScidAliasMap is a map from a base short channel ID to a set of alias short +// channel IDs. +type ScidAliasMap map[lnwire.ShortChannelID][]lnwire.ShortChannelID + var ( // aliasBucket stores aliases as keys and their base SCIDs as values. // This is used to populate the maps that the Manager uses. The keys @@ -94,7 +98,7 @@ type Manager struct { // baseToSet is a mapping from the "base" SCID to the set of aliases // for this channel. This mapping includes all channels that // negotiated the option-scid-alias feature bit. - baseToSet map[lnwire.ShortChannelID][]lnwire.ShortChannelID + baseToSet ScidAliasMap // aliasToBase is a mapping that maps all aliases for a given channel // to its base SCID. This is only used for channels that have @@ -117,10 +121,10 @@ func NewManager(db kvdb.Backend, linkAliasUpdater UpdateLinkAliases) (*Manager, m := &Manager{ backend: db, + baseToSet: make(ScidAliasMap), linkAliasUpdater: linkAliasUpdater, } - m.baseToSet = make(map[lnwire.ShortChannelID][]lnwire.ShortChannelID) m.aliasToBase = make(map[lnwire.ShortChannelID]lnwire.ShortChannelID) m.peerAlias = make(map[lnwire.ChannelID]lnwire.ShortChannelID) @@ -543,11 +547,11 @@ func (m *Manager) RequestAlias() (lnwire.ShortChannelID, error) { // ListAliases returns a carbon copy of baseToSet. This is used by the rpc // layer. -func (m *Manager) ListAliases() map[lnwire.ShortChannelID][]lnwire.ShortChannelID { +func (m *Manager) ListAliases() ScidAliasMap { m.RLock() defer m.RUnlock() - baseCopy := make(map[lnwire.ShortChannelID][]lnwire.ShortChannelID) + baseCopy := make(ScidAliasMap) for k, v := range m.baseToSet { setCopy := make([]lnwire.ShortChannelID, len(v)) From 148ba49530066bf72b2896c923a367b1377953c1 Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Fri, 9 Aug 2024 15:13:15 +0200 Subject: [PATCH 054/218] aliasmgr: avoid collision when requesting alias With the new RPC calls that we are going to add in the next commits, it will be possible for users to add (local only, non-gossipped) SCID aliases for channels. Since those will be in the same range as the ones given out by RequestAlias, we need to make sure that when we generate a new one that it doesn't collide with an already existing one. --- aliasmgr/aliasmgr.go | 65 ++++++++++++++++++++++++++++++++++++--- aliasmgr/aliasmgr_test.go | 20 +++++++++++- 2 files changed, 79 insertions(+), 6 deletions(-) diff --git a/aliasmgr/aliasmgr.go b/aliasmgr/aliasmgr.go index 29ac2c44c2..689af13661 100644 --- a/aliasmgr/aliasmgr.go +++ b/aliasmgr/aliasmgr.go @@ -9,6 +9,7 @@ import ( "github.com/lightningnetwork/lnd/htlcswitch/hop" "github.com/lightningnetwork/lnd/kvdb" "github.com/lightningnetwork/lnd/lnwire" + "golang.org/x/exp/maps" ) // UpdateLinkAliases is a function type for a function that locates the active @@ -58,16 +59,16 @@ var ( byteOrder = binary.BigEndian // startBlockHeight is the starting block height of the alias range. - startingBlockHeight = 16_000_000 + startingBlockHeight uint32 = 16_000_000 // endBlockHeight is the ending block height of the alias range. - endBlockHeight = 16_250_000 + endBlockHeight uint32 = 16_250_000 // StartingAlias is the first alias ShortChannelID that will get // assigned by RequestAlias. The starting BlockHeight is chosen so that // legitimate SCIDs in integration tests aren't mistaken for an alias. StartingAlias = lnwire.ShortChannelID{ - BlockHeight: uint32(startingBlockHeight), + BlockHeight: startingBlockHeight, TxIndex: 0, TxPosition: 0, } @@ -506,6 +507,19 @@ func (m *Manager) GetPeerAlias(chanID lnwire.ChannelID) (lnwire.ShortChannelID, func (m *Manager) RequestAlias() (lnwire.ShortChannelID, error) { var nextAlias lnwire.ShortChannelID + m.RLock() + defer m.RUnlock() + + // haveAlias returns true if the passed alias is already assigned to a + // channel in the baseToSet map. + haveAlias := func(maybeNextAlias lnwire.ShortChannelID) bool { + return fn.Any(func(aliasList []lnwire.ShortChannelID) bool { + return fn.Any(func(alias lnwire.ShortChannelID) bool { + return alias == maybeNextAlias + }, aliasList) + }, maps.Values(m.baseToSet)) + } + err := kvdb.Update(m.backend, func(tx kvdb.RwTx) error { bucket, err := tx.CreateTopLevelBucket(aliasAllocBucket) if err != nil { @@ -518,6 +532,27 @@ func (m *Manager) RequestAlias() (lnwire.ShortChannelID, error) { // StartingAlias to it. nextAlias = StartingAlias + // If the very first alias is already assigned, we'll + // keep incrementing until we find an unassigned alias. + // This is to avoid collision with custom added SCID + // aliases that fall into the same range as the ones we + // generate here monotonically. Those custom SCIDs are + // stored in a different bucket, but we can just check + // the in-memory map for simplicity. + for { + if !haveAlias(nextAlias) { + break + } + + nextAlias = getNextScid(nextAlias) + + // Abort if we've reached the end of the range. + if nextAlias.BlockHeight >= endBlockHeight { + return fmt.Errorf("range for custom " + + "aliases exhausted") + } + } + var scratch [8]byte byteOrder.PutUint64(scratch[:], nextAlias.ToUint64()) return bucket.Put(lastAliasKey, scratch[:]) @@ -532,6 +567,26 @@ func (m *Manager) RequestAlias() (lnwire.ShortChannelID, error) { ) nextAlias = getNextScid(lastScid) + // If the next alias is already assigned, we'll keep + // incrementing until we find an unassigned alias. This is to + // avoid collision with custom added SCID aliases that fall into + // the same range as the ones we generate here monotonically. + // Those custom SCIDs are stored in a different bucket, but we + // can just check the in-memory map for simplicity. + for { + if !haveAlias(nextAlias) { + break + } + + nextAlias = getNextScid(nextAlias) + + // Abort if we've reached the end of the range. + if nextAlias.BlockHeight >= endBlockHeight { + return fmt.Errorf("range for custom " + + "aliases exhausted") + } + } + var scratch [8]byte byteOrder.PutUint64(scratch[:], nextAlias.ToUint64()) return bucket.Put(lastAliasKey, scratch[:]) @@ -614,6 +669,6 @@ func getNextScid(last lnwire.ShortChannelID) lnwire.ShortChannelID { // assigned by RequestAlias. These bounds only apply to aliases we generate. // Our peers are free to use any range they choose. func IsAlias(scid lnwire.ShortChannelID) bool { - return scid.BlockHeight >= uint32(startingBlockHeight) && - scid.BlockHeight < uint32(endBlockHeight) + return scid.BlockHeight >= startingBlockHeight && + scid.BlockHeight < endBlockHeight } diff --git a/aliasmgr/aliasmgr_test.go b/aliasmgr/aliasmgr_test.go index c32de5135b..2d9a54e231 100644 --- a/aliasmgr/aliasmgr_test.go +++ b/aliasmgr/aliasmgr_test.go @@ -168,6 +168,24 @@ func TestAliasLifecycle(t *testing.T) { // Query the aliases and verify that none exists. aliasList = aliasStore.GetAliases(baseScid) require.Len(t, aliasList, 0) + + // We now request an alias generated by the aliasStore. This should give + // the first from the pre-defined list of allocated aliases. + firstRequested, err := aliasStore.RequestAlias() + require.NoError(t, err) + require.Equal(t, StartingAlias, firstRequested) + + // We now manually add the next alias from the range as a custom alias. + secondAlias := getNextScid(firstRequested) + err = aliasStore.AddLocalAlias(secondAlias, baseScid, false, true) + require.NoError(t, err) + + // When we now request another alias from the allocation list, we expect + // the third one (tx position 2) to be returned. + thirdRequested, err := aliasStore.RequestAlias() + require.NoError(t, err) + require.Equal(t, getNextScid(secondAlias), thirdRequested) + require.EqualValues(t, 2, thirdRequested.TxPosition) } // TestGetNextScid tests that given a current lnwire.ShortChannelID, @@ -182,7 +200,7 @@ func TestGetNextScid(t *testing.T) { name: "starting alias", current: StartingAlias, expected: lnwire.ShortChannelID{ - BlockHeight: uint32(startingBlockHeight), + BlockHeight: startingBlockHeight, TxIndex: 0, TxPosition: 1, }, From 2b1f347f98fd4307ce65c4417e4c8a5f5d6b14db Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Fri, 9 Aug 2024 16:05:04 +0200 Subject: [PATCH 055/218] aliasmgr: export alias start and end ranges Because we restrict custom SCID aliases to be in a specific range, we export the range start and end values so a user of the RPCs we're going to add in the next commits can adjust their values to fit within the range. --- aliasmgr/aliasmgr.go | 23 +++++++++++++---------- aliasmgr/aliasmgr_test.go | 2 +- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/aliasmgr/aliasmgr.go b/aliasmgr/aliasmgr.go index 689af13661..f06cb53d79 100644 --- a/aliasmgr/aliasmgr.go +++ b/aliasmgr/aliasmgr.go @@ -58,17 +58,18 @@ var ( // operations. byteOrder = binary.BigEndian - // startBlockHeight is the starting block height of the alias range. - startingBlockHeight uint32 = 16_000_000 + // AliasStartBlockHeight is the starting block height of the alias + // range. + AliasStartBlockHeight uint32 = 16_000_000 - // endBlockHeight is the ending block height of the alias range. - endBlockHeight uint32 = 16_250_000 + // AliasEndBlockHeight is the ending block height of the alias range. + AliasEndBlockHeight uint32 = 16_250_000 // StartingAlias is the first alias ShortChannelID that will get // assigned by RequestAlias. The starting BlockHeight is chosen so that // legitimate SCIDs in integration tests aren't mistaken for an alias. StartingAlias = lnwire.ShortChannelID{ - BlockHeight: startingBlockHeight, + BlockHeight: AliasStartBlockHeight, TxIndex: 0, TxPosition: 0, } @@ -547,7 +548,9 @@ func (m *Manager) RequestAlias() (lnwire.ShortChannelID, error) { nextAlias = getNextScid(nextAlias) // Abort if we've reached the end of the range. - if nextAlias.BlockHeight >= endBlockHeight { + if nextAlias.BlockHeight >= + AliasEndBlockHeight { + return fmt.Errorf("range for custom " + "aliases exhausted") } @@ -581,7 +584,7 @@ func (m *Manager) RequestAlias() (lnwire.ShortChannelID, error) { nextAlias = getNextScid(nextAlias) // Abort if we've reached the end of the range. - if nextAlias.BlockHeight >= endBlockHeight { + if nextAlias.BlockHeight >= AliasEndBlockHeight { return fmt.Errorf("range for custom " + "aliases exhausted") } @@ -665,10 +668,10 @@ func getNextScid(last lnwire.ShortChannelID) lnwire.ShortChannelID { // IsAlias returns true if the passed SCID is an alias. The function determines // this by looking at the BlockHeight. If the BlockHeight is greater than -// startingBlockHeight and less than endBlockHeight, then it is an alias +// AliasStartBlockHeight and less than AliasEndBlockHeight, then it is an alias // assigned by RequestAlias. These bounds only apply to aliases we generate. // Our peers are free to use any range they choose. func IsAlias(scid lnwire.ShortChannelID) bool { - return scid.BlockHeight >= startingBlockHeight && - scid.BlockHeight < endBlockHeight + return scid.BlockHeight >= AliasStartBlockHeight && + scid.BlockHeight < AliasEndBlockHeight } diff --git a/aliasmgr/aliasmgr_test.go b/aliasmgr/aliasmgr_test.go index 2d9a54e231..1844a82ef2 100644 --- a/aliasmgr/aliasmgr_test.go +++ b/aliasmgr/aliasmgr_test.go @@ -200,7 +200,7 @@ func TestGetNextScid(t *testing.T) { name: "starting alias", current: StartingAlias, expected: lnwire.ShortChannelID{ - BlockHeight: startingBlockHeight, + BlockHeight: AliasStartBlockHeight, TxIndex: 0, TxPosition: 1, }, From d6ef2a122f833b7916608be87a5f824fa18722f0 Mon Sep 17 00:00:00 2001 From: George Tsagkarelis Date: Thu, 29 Feb 2024 13:07:37 +0100 Subject: [PATCH 056/218] routerrpc: add XAddLocalChanAliases & XDeleteLocalChanAliases --- lnrpc/marshall_utils.go | 18 +- lnrpc/routerrpc/config.go | 5 + lnrpc/routerrpc/router.pb.go | 710 +++++++++++++++++++--------- lnrpc/routerrpc/router.pb.gw.go | 162 +++++++ lnrpc/routerrpc/router.pb.json.go | 50 ++ lnrpc/routerrpc/router.proto | 35 ++ lnrpc/routerrpc/router.swagger.json | 128 +++++ lnrpc/routerrpc/router.yaml | 7 + lnrpc/routerrpc/router_grpc.pb.go | 92 ++++ lnrpc/routerrpc/router_server.go | 136 ++++++ rpcserver.go | 15 +- subrpcserver_config.go | 11 +- 12 files changed, 1137 insertions(+), 232 deletions(-) diff --git a/lnrpc/marshall_utils.go b/lnrpc/marshall_utils.go index 2e86d8eca4..0dcbe46fab 100644 --- a/lnrpc/marshall_utils.go +++ b/lnrpc/marshall_utils.go @@ -3,15 +3,18 @@ package lnrpc import ( "encoding/hex" "errors" - fmt "fmt" + "fmt" "github.com/btcsuite/btcd/btcutil" "github.com/btcsuite/btcd/chaincfg" "github.com/btcsuite/btcd/txscript" "github.com/btcsuite/btcd/wire" "github.com/btcsuite/btcwallet/wallet" + "github.com/lightningnetwork/lnd/aliasmgr" + "github.com/lightningnetwork/lnd/fn" "github.com/lightningnetwork/lnd/lnwallet" "github.com/lightningnetwork/lnd/lnwire" + "golang.org/x/exp/maps" ) var ( @@ -207,3 +210,16 @@ func UnmarshallCoinSelectionStrategy(strategy CoinSelectionStrategy, "%v", strategy) } } + +// MarshalAliasMap converts a ScidAliasMap to its proto counterpart. This is +// used in various RPCs that handle scid alias mappings. +func MarshalAliasMap(scidMap aliasmgr.ScidAliasMap) []*AliasMap { + return fn.Map(func(base lnwire.ShortChannelID) *AliasMap { + return &AliasMap{ + BaseScid: base.ToUint64(), + Aliases: fn.Map(func(a lnwire.ShortChannelID) uint64 { + return a.ToUint64() + }, scidMap[base]), + } + }, maps.Keys(scidMap)) +} diff --git a/lnrpc/routerrpc/config.go b/lnrpc/routerrpc/config.go index 7b6d145999..20e3fa09bf 100644 --- a/lnrpc/routerrpc/config.go +++ b/lnrpc/routerrpc/config.go @@ -1,6 +1,7 @@ package routerrpc import ( + "github.com/lightningnetwork/lnd/aliasmgr" "github.com/lightningnetwork/lnd/macaroons" "github.com/lightningnetwork/lnd/routing" ) @@ -46,6 +47,10 @@ type Config struct { // RouterBackend contains shared logic between this sub server and the // main rpc server. RouterBackend *RouterBackend + + // AliasMgr is the alias manager instance that is used to handle all the + // SCID alias related information for channels. + AliasMgr *aliasmgr.Manager } // DefaultConfig defines the config defaults. diff --git a/lnrpc/routerrpc/router.pb.go b/lnrpc/routerrpc/router.pb.go index 7b91c445e8..c1517a595f 100644 --- a/lnrpc/routerrpc/router.pb.go +++ b/lnrpc/routerrpc/router.pb.go @@ -3346,6 +3346,194 @@ func (*UpdateChanStatusResponse) Descriptor() ([]byte, []int) { return file_routerrpc_router_proto_rawDescGZIP(), []int{40} } +type AddAliasesRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + AliasMaps []*lnrpc.AliasMap `protobuf:"bytes,1,rep,name=alias_maps,json=aliasMaps,proto3" json:"alias_maps,omitempty"` +} + +func (x *AddAliasesRequest) Reset() { + *x = AddAliasesRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_routerrpc_router_proto_msgTypes[41] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *AddAliasesRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AddAliasesRequest) ProtoMessage() {} + +func (x *AddAliasesRequest) ProtoReflect() protoreflect.Message { + mi := &file_routerrpc_router_proto_msgTypes[41] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use AddAliasesRequest.ProtoReflect.Descriptor instead. +func (*AddAliasesRequest) Descriptor() ([]byte, []int) { + return file_routerrpc_router_proto_rawDescGZIP(), []int{41} +} + +func (x *AddAliasesRequest) GetAliasMaps() []*lnrpc.AliasMap { + if x != nil { + return x.AliasMaps + } + return nil +} + +type AddAliasesResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + AliasMaps []*lnrpc.AliasMap `protobuf:"bytes,1,rep,name=alias_maps,json=aliasMaps,proto3" json:"alias_maps,omitempty"` +} + +func (x *AddAliasesResponse) Reset() { + *x = AddAliasesResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_routerrpc_router_proto_msgTypes[42] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *AddAliasesResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AddAliasesResponse) ProtoMessage() {} + +func (x *AddAliasesResponse) ProtoReflect() protoreflect.Message { + mi := &file_routerrpc_router_proto_msgTypes[42] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use AddAliasesResponse.ProtoReflect.Descriptor instead. +func (*AddAliasesResponse) Descriptor() ([]byte, []int) { + return file_routerrpc_router_proto_rawDescGZIP(), []int{42} +} + +func (x *AddAliasesResponse) GetAliasMaps() []*lnrpc.AliasMap { + if x != nil { + return x.AliasMaps + } + return nil +} + +type DeleteAliasesRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + AliasMaps []*lnrpc.AliasMap `protobuf:"bytes,1,rep,name=alias_maps,json=aliasMaps,proto3" json:"alias_maps,omitempty"` +} + +func (x *DeleteAliasesRequest) Reset() { + *x = DeleteAliasesRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_routerrpc_router_proto_msgTypes[43] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *DeleteAliasesRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DeleteAliasesRequest) ProtoMessage() {} + +func (x *DeleteAliasesRequest) ProtoReflect() protoreflect.Message { + mi := &file_routerrpc_router_proto_msgTypes[43] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use DeleteAliasesRequest.ProtoReflect.Descriptor instead. +func (*DeleteAliasesRequest) Descriptor() ([]byte, []int) { + return file_routerrpc_router_proto_rawDescGZIP(), []int{43} +} + +func (x *DeleteAliasesRequest) GetAliasMaps() []*lnrpc.AliasMap { + if x != nil { + return x.AliasMaps + } + return nil +} + +type DeleteAliasesResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + AliasMaps []*lnrpc.AliasMap `protobuf:"bytes,1,rep,name=alias_maps,json=aliasMaps,proto3" json:"alias_maps,omitempty"` +} + +func (x *DeleteAliasesResponse) Reset() { + *x = DeleteAliasesResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_routerrpc_router_proto_msgTypes[44] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *DeleteAliasesResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DeleteAliasesResponse) ProtoMessage() {} + +func (x *DeleteAliasesResponse) ProtoReflect() protoreflect.Message { + mi := &file_routerrpc_router_proto_msgTypes[44] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use DeleteAliasesResponse.ProtoReflect.Descriptor instead. +func (*DeleteAliasesResponse) Descriptor() ([]byte, []int) { + return file_routerrpc_router_proto_rawDescGZIP(), []int{44} +} + +func (x *DeleteAliasesResponse) GetAliasMaps() []*lnrpc.AliasMap { + if x != nil { + return x.AliasMaps + } + return nil +} + var File_routerrpc_router_proto protoreflect.FileDescriptor var file_routerrpc_router_proto_rawDesc = []byte{ @@ -3771,160 +3959,189 @@ var file_routerrpc_router_proto_rawDesc = []byte{ 0x6e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x1a, 0x0a, 0x18, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x2a, 0x81, 0x04, 0x0a, 0x0d, 0x46, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x44, 0x65, 0x74, - 0x61, 0x69, 0x6c, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, - 0x12, 0x0d, 0x0a, 0x09, 0x4e, 0x4f, 0x5f, 0x44, 0x45, 0x54, 0x41, 0x49, 0x4c, 0x10, 0x01, 0x12, - 0x10, 0x0a, 0x0c, 0x4f, 0x4e, 0x49, 0x4f, 0x4e, 0x5f, 0x44, 0x45, 0x43, 0x4f, 0x44, 0x45, 0x10, - 0x02, 0x12, 0x15, 0x0a, 0x11, 0x4c, 0x49, 0x4e, 0x4b, 0x5f, 0x4e, 0x4f, 0x54, 0x5f, 0x45, 0x4c, - 0x49, 0x47, 0x49, 0x42, 0x4c, 0x45, 0x10, 0x03, 0x12, 0x14, 0x0a, 0x10, 0x4f, 0x4e, 0x5f, 0x43, - 0x48, 0x41, 0x49, 0x4e, 0x5f, 0x54, 0x49, 0x4d, 0x45, 0x4f, 0x55, 0x54, 0x10, 0x04, 0x12, 0x14, - 0x0a, 0x10, 0x48, 0x54, 0x4c, 0x43, 0x5f, 0x45, 0x58, 0x43, 0x45, 0x45, 0x44, 0x53, 0x5f, 0x4d, - 0x41, 0x58, 0x10, 0x05, 0x12, 0x18, 0x0a, 0x14, 0x49, 0x4e, 0x53, 0x55, 0x46, 0x46, 0x49, 0x43, - 0x49, 0x45, 0x4e, 0x54, 0x5f, 0x42, 0x41, 0x4c, 0x41, 0x4e, 0x43, 0x45, 0x10, 0x06, 0x12, 0x16, - 0x0a, 0x12, 0x49, 0x4e, 0x43, 0x4f, 0x4d, 0x50, 0x4c, 0x45, 0x54, 0x45, 0x5f, 0x46, 0x4f, 0x52, - 0x57, 0x41, 0x52, 0x44, 0x10, 0x07, 0x12, 0x13, 0x0a, 0x0f, 0x48, 0x54, 0x4c, 0x43, 0x5f, 0x41, - 0x44, 0x44, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x45, 0x44, 0x10, 0x08, 0x12, 0x15, 0x0a, 0x11, 0x46, - 0x4f, 0x52, 0x57, 0x41, 0x52, 0x44, 0x53, 0x5f, 0x44, 0x49, 0x53, 0x41, 0x42, 0x4c, 0x45, 0x44, - 0x10, 0x09, 0x12, 0x14, 0x0a, 0x10, 0x49, 0x4e, 0x56, 0x4f, 0x49, 0x43, 0x45, 0x5f, 0x43, 0x41, - 0x4e, 0x43, 0x45, 0x4c, 0x45, 0x44, 0x10, 0x0a, 0x12, 0x15, 0x0a, 0x11, 0x49, 0x4e, 0x56, 0x4f, - 0x49, 0x43, 0x45, 0x5f, 0x55, 0x4e, 0x44, 0x45, 0x52, 0x50, 0x41, 0x49, 0x44, 0x10, 0x0b, 0x12, - 0x1b, 0x0a, 0x17, 0x49, 0x4e, 0x56, 0x4f, 0x49, 0x43, 0x45, 0x5f, 0x45, 0x58, 0x50, 0x49, 0x52, - 0x59, 0x5f, 0x54, 0x4f, 0x4f, 0x5f, 0x53, 0x4f, 0x4f, 0x4e, 0x10, 0x0c, 0x12, 0x14, 0x0a, 0x10, - 0x49, 0x4e, 0x56, 0x4f, 0x49, 0x43, 0x45, 0x5f, 0x4e, 0x4f, 0x54, 0x5f, 0x4f, 0x50, 0x45, 0x4e, - 0x10, 0x0d, 0x12, 0x17, 0x0a, 0x13, 0x4d, 0x50, 0x50, 0x5f, 0x49, 0x4e, 0x56, 0x4f, 0x49, 0x43, - 0x45, 0x5f, 0x54, 0x49, 0x4d, 0x45, 0x4f, 0x55, 0x54, 0x10, 0x0e, 0x12, 0x14, 0x0a, 0x10, 0x41, - 0x44, 0x44, 0x52, 0x45, 0x53, 0x53, 0x5f, 0x4d, 0x49, 0x53, 0x4d, 0x41, 0x54, 0x43, 0x48, 0x10, - 0x0f, 0x12, 0x16, 0x0a, 0x12, 0x53, 0x45, 0x54, 0x5f, 0x54, 0x4f, 0x54, 0x41, 0x4c, 0x5f, 0x4d, - 0x49, 0x53, 0x4d, 0x41, 0x54, 0x43, 0x48, 0x10, 0x10, 0x12, 0x15, 0x0a, 0x11, 0x53, 0x45, 0x54, - 0x5f, 0x54, 0x4f, 0x54, 0x41, 0x4c, 0x5f, 0x54, 0x4f, 0x4f, 0x5f, 0x4c, 0x4f, 0x57, 0x10, 0x11, - 0x12, 0x10, 0x0a, 0x0c, 0x53, 0x45, 0x54, 0x5f, 0x4f, 0x56, 0x45, 0x52, 0x50, 0x41, 0x49, 0x44, - 0x10, 0x12, 0x12, 0x13, 0x0a, 0x0f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x5f, 0x49, 0x4e, - 0x56, 0x4f, 0x49, 0x43, 0x45, 0x10, 0x13, 0x12, 0x13, 0x0a, 0x0f, 0x49, 0x4e, 0x56, 0x41, 0x4c, - 0x49, 0x44, 0x5f, 0x4b, 0x45, 0x59, 0x53, 0x45, 0x4e, 0x44, 0x10, 0x14, 0x12, 0x13, 0x0a, 0x0f, - 0x4d, 0x50, 0x50, 0x5f, 0x49, 0x4e, 0x5f, 0x50, 0x52, 0x4f, 0x47, 0x52, 0x45, 0x53, 0x53, 0x10, - 0x15, 0x12, 0x12, 0x0a, 0x0e, 0x43, 0x49, 0x52, 0x43, 0x55, 0x4c, 0x41, 0x52, 0x5f, 0x52, 0x4f, - 0x55, 0x54, 0x45, 0x10, 0x16, 0x2a, 0xae, 0x01, 0x0a, 0x0c, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, - 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x0d, 0x0a, 0x09, 0x49, 0x4e, 0x5f, 0x46, 0x4c, 0x49, - 0x47, 0x48, 0x54, 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x53, 0x55, 0x43, 0x43, 0x45, 0x45, 0x44, - 0x45, 0x44, 0x10, 0x01, 0x12, 0x12, 0x0a, 0x0e, 0x46, 0x41, 0x49, 0x4c, 0x45, 0x44, 0x5f, 0x54, - 0x49, 0x4d, 0x45, 0x4f, 0x55, 0x54, 0x10, 0x02, 0x12, 0x13, 0x0a, 0x0f, 0x46, 0x41, 0x49, 0x4c, - 0x45, 0x44, 0x5f, 0x4e, 0x4f, 0x5f, 0x52, 0x4f, 0x55, 0x54, 0x45, 0x10, 0x03, 0x12, 0x10, 0x0a, - 0x0c, 0x46, 0x41, 0x49, 0x4c, 0x45, 0x44, 0x5f, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, 0x04, 0x12, - 0x24, 0x0a, 0x20, 0x46, 0x41, 0x49, 0x4c, 0x45, 0x44, 0x5f, 0x49, 0x4e, 0x43, 0x4f, 0x52, 0x52, - 0x45, 0x43, 0x54, 0x5f, 0x50, 0x41, 0x59, 0x4d, 0x45, 0x4e, 0x54, 0x5f, 0x44, 0x45, 0x54, 0x41, - 0x49, 0x4c, 0x53, 0x10, 0x05, 0x12, 0x1f, 0x0a, 0x1b, 0x46, 0x41, 0x49, 0x4c, 0x45, 0x44, 0x5f, - 0x49, 0x4e, 0x53, 0x55, 0x46, 0x46, 0x49, 0x43, 0x49, 0x45, 0x4e, 0x54, 0x5f, 0x42, 0x41, 0x4c, - 0x41, 0x4e, 0x43, 0x45, 0x10, 0x06, 0x2a, 0x3c, 0x0a, 0x18, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x76, - 0x65, 0x48, 0x6f, 0x6c, 0x64, 0x46, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x41, 0x63, 0x74, 0x69, - 0x6f, 0x6e, 0x12, 0x0a, 0x0a, 0x06, 0x53, 0x45, 0x54, 0x54, 0x4c, 0x45, 0x10, 0x00, 0x12, 0x08, - 0x0a, 0x04, 0x46, 0x41, 0x49, 0x4c, 0x10, 0x01, 0x12, 0x0a, 0x0a, 0x06, 0x52, 0x45, 0x53, 0x55, - 0x4d, 0x45, 0x10, 0x02, 0x2a, 0x35, 0x0a, 0x10, 0x43, 0x68, 0x61, 0x6e, 0x53, 0x74, 0x61, 0x74, - 0x75, 0x73, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x0a, 0x0a, 0x06, 0x45, 0x4e, 0x41, 0x42, - 0x4c, 0x45, 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x44, 0x49, 0x53, 0x41, 0x42, 0x4c, 0x45, 0x10, - 0x01, 0x12, 0x08, 0x0a, 0x04, 0x41, 0x55, 0x54, 0x4f, 0x10, 0x02, 0x32, 0xb5, 0x0c, 0x0a, 0x06, - 0x52, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x12, 0x40, 0x0a, 0x0d, 0x53, 0x65, 0x6e, 0x64, 0x50, 0x61, - 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x56, 0x32, 0x12, 0x1d, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, - 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x52, + 0x65, 0x22, 0x43, 0x0a, 0x11, 0x41, 0x64, 0x64, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x65, 0x73, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2e, 0x0a, 0x0a, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x5f, + 0x6d, 0x61, 0x70, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x6c, 0x6e, 0x72, + 0x70, 0x63, 0x2e, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x4d, 0x61, 0x70, 0x52, 0x09, 0x61, 0x6c, 0x69, + 0x61, 0x73, 0x4d, 0x61, 0x70, 0x73, 0x22, 0x44, 0x0a, 0x12, 0x41, 0x64, 0x64, 0x41, 0x6c, 0x69, + 0x61, 0x73, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2e, 0x0a, 0x0a, + 0x61, 0x6c, 0x69, 0x61, 0x73, 0x5f, 0x6d, 0x61, 0x70, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x0f, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x4d, 0x61, + 0x70, 0x52, 0x09, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x4d, 0x61, 0x70, 0x73, 0x22, 0x46, 0x0a, 0x14, + 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x65, 0x73, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x12, 0x2e, 0x0a, 0x0a, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x5f, 0x6d, 0x61, + 0x70, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, + 0x2e, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x4d, 0x61, 0x70, 0x52, 0x09, 0x61, 0x6c, 0x69, 0x61, 0x73, + 0x4d, 0x61, 0x70, 0x73, 0x22, 0x47, 0x0a, 0x15, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x41, 0x6c, + 0x69, 0x61, 0x73, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2e, 0x0a, + 0x0a, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x5f, 0x6d, 0x61, 0x70, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x0f, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x4d, + 0x61, 0x70, 0x52, 0x09, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x4d, 0x61, 0x70, 0x73, 0x2a, 0x81, 0x04, + 0x0a, 0x0d, 0x46, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x12, + 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, + 0x4e, 0x4f, 0x5f, 0x44, 0x45, 0x54, 0x41, 0x49, 0x4c, 0x10, 0x01, 0x12, 0x10, 0x0a, 0x0c, 0x4f, + 0x4e, 0x49, 0x4f, 0x4e, 0x5f, 0x44, 0x45, 0x43, 0x4f, 0x44, 0x45, 0x10, 0x02, 0x12, 0x15, 0x0a, + 0x11, 0x4c, 0x49, 0x4e, 0x4b, 0x5f, 0x4e, 0x4f, 0x54, 0x5f, 0x45, 0x4c, 0x49, 0x47, 0x49, 0x42, + 0x4c, 0x45, 0x10, 0x03, 0x12, 0x14, 0x0a, 0x10, 0x4f, 0x4e, 0x5f, 0x43, 0x48, 0x41, 0x49, 0x4e, + 0x5f, 0x54, 0x49, 0x4d, 0x45, 0x4f, 0x55, 0x54, 0x10, 0x04, 0x12, 0x14, 0x0a, 0x10, 0x48, 0x54, + 0x4c, 0x43, 0x5f, 0x45, 0x58, 0x43, 0x45, 0x45, 0x44, 0x53, 0x5f, 0x4d, 0x41, 0x58, 0x10, 0x05, + 0x12, 0x18, 0x0a, 0x14, 0x49, 0x4e, 0x53, 0x55, 0x46, 0x46, 0x49, 0x43, 0x49, 0x45, 0x4e, 0x54, + 0x5f, 0x42, 0x41, 0x4c, 0x41, 0x4e, 0x43, 0x45, 0x10, 0x06, 0x12, 0x16, 0x0a, 0x12, 0x49, 0x4e, + 0x43, 0x4f, 0x4d, 0x50, 0x4c, 0x45, 0x54, 0x45, 0x5f, 0x46, 0x4f, 0x52, 0x57, 0x41, 0x52, 0x44, + 0x10, 0x07, 0x12, 0x13, 0x0a, 0x0f, 0x48, 0x54, 0x4c, 0x43, 0x5f, 0x41, 0x44, 0x44, 0x5f, 0x46, + 0x41, 0x49, 0x4c, 0x45, 0x44, 0x10, 0x08, 0x12, 0x15, 0x0a, 0x11, 0x46, 0x4f, 0x52, 0x57, 0x41, + 0x52, 0x44, 0x53, 0x5f, 0x44, 0x49, 0x53, 0x41, 0x42, 0x4c, 0x45, 0x44, 0x10, 0x09, 0x12, 0x14, + 0x0a, 0x10, 0x49, 0x4e, 0x56, 0x4f, 0x49, 0x43, 0x45, 0x5f, 0x43, 0x41, 0x4e, 0x43, 0x45, 0x4c, + 0x45, 0x44, 0x10, 0x0a, 0x12, 0x15, 0x0a, 0x11, 0x49, 0x4e, 0x56, 0x4f, 0x49, 0x43, 0x45, 0x5f, + 0x55, 0x4e, 0x44, 0x45, 0x52, 0x50, 0x41, 0x49, 0x44, 0x10, 0x0b, 0x12, 0x1b, 0x0a, 0x17, 0x49, + 0x4e, 0x56, 0x4f, 0x49, 0x43, 0x45, 0x5f, 0x45, 0x58, 0x50, 0x49, 0x52, 0x59, 0x5f, 0x54, 0x4f, + 0x4f, 0x5f, 0x53, 0x4f, 0x4f, 0x4e, 0x10, 0x0c, 0x12, 0x14, 0x0a, 0x10, 0x49, 0x4e, 0x56, 0x4f, + 0x49, 0x43, 0x45, 0x5f, 0x4e, 0x4f, 0x54, 0x5f, 0x4f, 0x50, 0x45, 0x4e, 0x10, 0x0d, 0x12, 0x17, + 0x0a, 0x13, 0x4d, 0x50, 0x50, 0x5f, 0x49, 0x4e, 0x56, 0x4f, 0x49, 0x43, 0x45, 0x5f, 0x54, 0x49, + 0x4d, 0x45, 0x4f, 0x55, 0x54, 0x10, 0x0e, 0x12, 0x14, 0x0a, 0x10, 0x41, 0x44, 0x44, 0x52, 0x45, + 0x53, 0x53, 0x5f, 0x4d, 0x49, 0x53, 0x4d, 0x41, 0x54, 0x43, 0x48, 0x10, 0x0f, 0x12, 0x16, 0x0a, + 0x12, 0x53, 0x45, 0x54, 0x5f, 0x54, 0x4f, 0x54, 0x41, 0x4c, 0x5f, 0x4d, 0x49, 0x53, 0x4d, 0x41, + 0x54, 0x43, 0x48, 0x10, 0x10, 0x12, 0x15, 0x0a, 0x11, 0x53, 0x45, 0x54, 0x5f, 0x54, 0x4f, 0x54, + 0x41, 0x4c, 0x5f, 0x54, 0x4f, 0x4f, 0x5f, 0x4c, 0x4f, 0x57, 0x10, 0x11, 0x12, 0x10, 0x0a, 0x0c, + 0x53, 0x45, 0x54, 0x5f, 0x4f, 0x56, 0x45, 0x52, 0x50, 0x41, 0x49, 0x44, 0x10, 0x12, 0x12, 0x13, + 0x0a, 0x0f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x5f, 0x49, 0x4e, 0x56, 0x4f, 0x49, 0x43, + 0x45, 0x10, 0x13, 0x12, 0x13, 0x0a, 0x0f, 0x49, 0x4e, 0x56, 0x41, 0x4c, 0x49, 0x44, 0x5f, 0x4b, + 0x45, 0x59, 0x53, 0x45, 0x4e, 0x44, 0x10, 0x14, 0x12, 0x13, 0x0a, 0x0f, 0x4d, 0x50, 0x50, 0x5f, + 0x49, 0x4e, 0x5f, 0x50, 0x52, 0x4f, 0x47, 0x52, 0x45, 0x53, 0x53, 0x10, 0x15, 0x12, 0x12, 0x0a, + 0x0e, 0x43, 0x49, 0x52, 0x43, 0x55, 0x4c, 0x41, 0x52, 0x5f, 0x52, 0x4f, 0x55, 0x54, 0x45, 0x10, + 0x16, 0x2a, 0xae, 0x01, 0x0a, 0x0c, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x53, 0x74, 0x61, + 0x74, 0x65, 0x12, 0x0d, 0x0a, 0x09, 0x49, 0x4e, 0x5f, 0x46, 0x4c, 0x49, 0x47, 0x48, 0x54, 0x10, + 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x53, 0x55, 0x43, 0x43, 0x45, 0x45, 0x44, 0x45, 0x44, 0x10, 0x01, + 0x12, 0x12, 0x0a, 0x0e, 0x46, 0x41, 0x49, 0x4c, 0x45, 0x44, 0x5f, 0x54, 0x49, 0x4d, 0x45, 0x4f, + 0x55, 0x54, 0x10, 0x02, 0x12, 0x13, 0x0a, 0x0f, 0x46, 0x41, 0x49, 0x4c, 0x45, 0x44, 0x5f, 0x4e, + 0x4f, 0x5f, 0x52, 0x4f, 0x55, 0x54, 0x45, 0x10, 0x03, 0x12, 0x10, 0x0a, 0x0c, 0x46, 0x41, 0x49, + 0x4c, 0x45, 0x44, 0x5f, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, 0x04, 0x12, 0x24, 0x0a, 0x20, 0x46, + 0x41, 0x49, 0x4c, 0x45, 0x44, 0x5f, 0x49, 0x4e, 0x43, 0x4f, 0x52, 0x52, 0x45, 0x43, 0x54, 0x5f, + 0x50, 0x41, 0x59, 0x4d, 0x45, 0x4e, 0x54, 0x5f, 0x44, 0x45, 0x54, 0x41, 0x49, 0x4c, 0x53, 0x10, + 0x05, 0x12, 0x1f, 0x0a, 0x1b, 0x46, 0x41, 0x49, 0x4c, 0x45, 0x44, 0x5f, 0x49, 0x4e, 0x53, 0x55, + 0x46, 0x46, 0x49, 0x43, 0x49, 0x45, 0x4e, 0x54, 0x5f, 0x42, 0x41, 0x4c, 0x41, 0x4e, 0x43, 0x45, + 0x10, 0x06, 0x2a, 0x3c, 0x0a, 0x18, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x48, 0x6f, 0x6c, + 0x64, 0x46, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x0a, + 0x0a, 0x06, 0x53, 0x45, 0x54, 0x54, 0x4c, 0x45, 0x10, 0x00, 0x12, 0x08, 0x0a, 0x04, 0x46, 0x41, + 0x49, 0x4c, 0x10, 0x01, 0x12, 0x0a, 0x0a, 0x06, 0x52, 0x45, 0x53, 0x55, 0x4d, 0x45, 0x10, 0x02, + 0x2a, 0x35, 0x0a, 0x10, 0x43, 0x68, 0x61, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x41, 0x63, + 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x0a, 0x0a, 0x06, 0x45, 0x4e, 0x41, 0x42, 0x4c, 0x45, 0x10, 0x00, + 0x12, 0x0b, 0x0a, 0x07, 0x44, 0x49, 0x53, 0x41, 0x42, 0x4c, 0x45, 0x10, 0x01, 0x12, 0x08, 0x0a, + 0x04, 0x41, 0x55, 0x54, 0x4f, 0x10, 0x02, 0x32, 0xe8, 0x0d, 0x0a, 0x06, 0x52, 0x6f, 0x75, 0x74, + 0x65, 0x72, 0x12, 0x40, 0x0a, 0x0d, 0x53, 0x65, 0x6e, 0x64, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, + 0x74, 0x56, 0x32, 0x12, 0x1d, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, + 0x53, 0x65, 0x6e, 0x64, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x0e, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x61, 0x79, 0x6d, 0x65, + 0x6e, 0x74, 0x30, 0x01, 0x12, 0x42, 0x0a, 0x0e, 0x54, 0x72, 0x61, 0x63, 0x6b, 0x50, 0x61, 0x79, + 0x6d, 0x65, 0x6e, 0x74, 0x56, 0x32, 0x12, 0x1e, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, + 0x70, 0x63, 0x2e, 0x54, 0x72, 0x61, 0x63, 0x6b, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x0e, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x50, - 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x30, 0x01, 0x12, 0x42, 0x0a, 0x0e, 0x54, 0x72, 0x61, 0x63, - 0x6b, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x56, 0x32, 0x12, 0x1e, 0x2e, 0x72, 0x6f, 0x75, - 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x54, 0x72, 0x61, 0x63, 0x6b, 0x50, 0x61, 0x79, 0x6d, - 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x0e, 0x2e, 0x6c, 0x6e, 0x72, - 0x70, 0x63, 0x2e, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x30, 0x01, 0x12, 0x42, 0x0a, 0x0d, - 0x54, 0x72, 0x61, 0x63, 0x6b, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x1f, 0x2e, - 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x54, 0x72, 0x61, 0x63, 0x6b, 0x50, - 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x0e, - 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x30, 0x01, - 0x12, 0x4b, 0x0a, 0x10, 0x45, 0x73, 0x74, 0x69, 0x6d, 0x61, 0x74, 0x65, 0x52, 0x6f, 0x75, 0x74, - 0x65, 0x46, 0x65, 0x65, 0x12, 0x1a, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, - 0x2e, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x46, 0x65, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x1b, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x6f, 0x75, - 0x74, 0x65, 0x46, 0x65, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x51, 0x0a, - 0x0b, 0x53, 0x65, 0x6e, 0x64, 0x54, 0x6f, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x12, 0x1d, 0x2e, 0x72, - 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x54, 0x6f, 0x52, - 0x6f, 0x75, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x72, 0x6f, - 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x54, 0x6f, 0x52, 0x6f, - 0x75, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x03, 0x88, 0x02, 0x01, - 0x12, 0x42, 0x0a, 0x0d, 0x53, 0x65, 0x6e, 0x64, 0x54, 0x6f, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x56, - 0x32, 0x12, 0x1d, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, - 0x6e, 0x64, 0x54, 0x6f, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x12, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x48, 0x54, 0x4c, 0x43, 0x41, 0x74, 0x74, - 0x65, 0x6d, 0x70, 0x74, 0x12, 0x64, 0x0a, 0x13, 0x52, 0x65, 0x73, 0x65, 0x74, 0x4d, 0x69, 0x73, - 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x12, 0x25, 0x2e, 0x72, 0x6f, - 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x65, 0x73, 0x65, 0x74, 0x4d, 0x69, 0x73, - 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x52, - 0x65, 0x73, 0x65, 0x74, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x6e, 0x74, 0x72, - 0x6f, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x64, 0x0a, 0x13, 0x51, 0x75, - 0x65, 0x72, 0x79, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, - 0x6c, 0x12, 0x25, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x51, 0x75, - 0x65, 0x72, 0x79, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, - 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, - 0x72, 0x72, 0x70, 0x63, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6f, - 0x6e, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x12, 0x6a, 0x0a, 0x15, 0x58, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x4d, 0x69, 0x73, 0x73, 0x69, - 0x6f, 0x6e, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x12, 0x27, 0x2e, 0x72, 0x6f, 0x75, 0x74, - 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x58, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x4d, 0x69, 0x73, - 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x58, - 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x6e, - 0x74, 0x72, 0x6f, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x70, 0x0a, 0x17, + 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x30, 0x01, 0x12, 0x42, 0x0a, 0x0d, 0x54, 0x72, 0x61, 0x63, + 0x6b, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x1f, 0x2e, 0x72, 0x6f, 0x75, 0x74, + 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x54, 0x72, 0x61, 0x63, 0x6b, 0x50, 0x61, 0x79, 0x6d, 0x65, + 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x0e, 0x2e, 0x6c, 0x6e, 0x72, + 0x70, 0x63, 0x2e, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x30, 0x01, 0x12, 0x4b, 0x0a, 0x10, + 0x45, 0x73, 0x74, 0x69, 0x6d, 0x61, 0x74, 0x65, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x46, 0x65, 0x65, + 0x12, 0x1a, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x6f, 0x75, + 0x74, 0x65, 0x46, 0x65, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x72, + 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x46, 0x65, + 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x51, 0x0a, 0x0b, 0x53, 0x65, 0x6e, + 0x64, 0x54, 0x6f, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x12, 0x1d, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, + 0x72, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x54, 0x6f, 0x52, 0x6f, 0x75, 0x74, 0x65, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, + 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x54, 0x6f, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x03, 0x88, 0x02, 0x01, 0x12, 0x42, 0x0a, 0x0d, + 0x53, 0x65, 0x6e, 0x64, 0x54, 0x6f, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x56, 0x32, 0x12, 0x1d, 0x2e, + 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x54, 0x6f, + 0x52, 0x6f, 0x75, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x12, 0x2e, 0x6c, + 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x48, 0x54, 0x4c, 0x43, 0x41, 0x74, 0x74, 0x65, 0x6d, 0x70, 0x74, + 0x12, 0x64, 0x0a, 0x13, 0x52, 0x65, 0x73, 0x65, 0x74, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, + 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x12, 0x25, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, + 0x72, 0x70, 0x63, 0x2e, 0x52, 0x65, 0x73, 0x65, 0x74, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, + 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, + 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x65, 0x73, 0x65, 0x74, + 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x64, 0x0a, 0x13, 0x51, 0x75, 0x65, 0x72, 0x79, 0x4d, + 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x12, 0x25, 0x2e, + 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x4d, + 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, + 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x6e, + 0x74, 0x72, 0x6f, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x6a, 0x0a, 0x15, + 0x58, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x6f, + 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x12, 0x27, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, + 0x63, 0x2e, 0x58, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, + 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, + 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x58, 0x49, 0x6d, 0x70, 0x6f, + 0x72, 0x74, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x70, 0x0a, 0x17, 0x47, 0x65, 0x74, 0x4d, + 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x43, 0x6f, 0x6e, + 0x66, 0x69, 0x67, 0x12, 0x29, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, - 0x6c, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x29, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, - 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x6f, - 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x47, - 0x65, 0x74, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, - 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x70, - 0x0a, 0x17, 0x53, 0x65, 0x74, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x6e, 0x74, - 0x72, 0x6f, 0x6c, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x29, 0x2e, 0x72, 0x6f, 0x75, 0x74, - 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x74, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, - 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, - 0x2e, 0x53, 0x65, 0x74, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x6e, 0x74, 0x72, - 0x6f, 0x6c, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x12, 0x5b, 0x0a, 0x10, 0x51, 0x75, 0x65, 0x72, 0x79, 0x50, 0x72, 0x6f, 0x62, 0x61, 0x62, 0x69, - 0x6c, 0x69, 0x74, 0x79, 0x12, 0x22, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, + 0x6c, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, + 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4d, 0x69, + 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x43, 0x6f, 0x6e, 0x66, + 0x69, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x70, 0x0a, 0x17, 0x53, 0x65, + 0x74, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x43, + 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x29, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, + 0x63, 0x2e, 0x53, 0x65, 0x74, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x6e, 0x74, + 0x72, 0x6f, 0x6c, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x2a, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x74, + 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x43, 0x6f, + 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5b, 0x0a, 0x10, + 0x51, 0x75, 0x65, 0x72, 0x79, 0x50, 0x72, 0x6f, 0x62, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, + 0x12, 0x22, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x51, 0x75, 0x65, + 0x72, 0x79, 0x50, 0x72, 0x6f, 0x62, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x50, 0x72, 0x6f, 0x62, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, - 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, - 0x72, 0x72, 0x70, 0x63, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x50, 0x72, 0x6f, 0x62, 0x61, 0x62, - 0x69, 0x6c, 0x69, 0x74, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x49, 0x0a, - 0x0a, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x12, 0x1c, 0x2e, 0x72, 0x6f, - 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x52, 0x6f, 0x75, - 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x72, 0x6f, 0x75, 0x74, - 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x52, 0x6f, 0x75, 0x74, 0x65, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x54, 0x0a, 0x13, 0x53, 0x75, 0x62, 0x73, - 0x63, 0x72, 0x69, 0x62, 0x65, 0x48, 0x74, 0x6c, 0x63, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x12, - 0x25, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x75, 0x62, 0x73, - 0x63, 0x72, 0x69, 0x62, 0x65, 0x48, 0x74, 0x6c, 0x63, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x14, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, - 0x70, 0x63, 0x2e, 0x48, 0x74, 0x6c, 0x63, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x30, 0x01, 0x12, 0x4d, - 0x0a, 0x0b, 0x53, 0x65, 0x6e, 0x64, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x1d, 0x2e, - 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x50, 0x61, - 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x72, - 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, - 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x03, 0x88, 0x02, 0x01, 0x30, 0x01, 0x12, 0x4f, 0x0a, - 0x0c, 0x54, 0x72, 0x61, 0x63, 0x6b, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x1e, 0x2e, - 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x54, 0x72, 0x61, 0x63, 0x6b, 0x50, - 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, - 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, - 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x03, 0x88, 0x02, 0x01, 0x30, 0x01, 0x12, 0x66, - 0x0a, 0x0f, 0x48, 0x74, 0x6c, 0x63, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, - 0x72, 0x12, 0x27, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x6f, - 0x72, 0x77, 0x61, 0x72, 0x64, 0x48, 0x74, 0x6c, 0x63, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, - 0x70, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x1a, 0x26, 0x2e, 0x72, 0x6f, 0x75, - 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x48, 0x74, - 0x6c, 0x63, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x28, 0x01, 0x30, 0x01, 0x12, 0x5b, 0x0a, 0x10, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, - 0x43, 0x68, 0x61, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x22, 0x2e, 0x72, 0x6f, 0x75, + 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x49, 0x0a, 0x0a, 0x42, 0x75, 0x69, + 0x6c, 0x64, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x12, 0x1c, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, + 0x72, 0x70, 0x63, 0x2e, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, + 0x63, 0x2e, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x54, 0x0a, 0x13, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, + 0x65, 0x48, 0x74, 0x6c, 0x63, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x25, 0x2e, 0x72, 0x6f, + 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, + 0x65, 0x48, 0x74, 0x6c, 0x63, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x14, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x48, + 0x74, 0x6c, 0x63, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x30, 0x01, 0x12, 0x4d, 0x0a, 0x0b, 0x53, 0x65, + 0x6e, 0x64, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x1d, 0x2e, 0x72, 0x6f, 0x75, 0x74, + 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, + 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, + 0x72, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x53, 0x74, 0x61, 0x74, + 0x75, 0x73, 0x22, 0x03, 0x88, 0x02, 0x01, 0x30, 0x01, 0x12, 0x4f, 0x0a, 0x0c, 0x54, 0x72, 0x61, + 0x63, 0x6b, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x1e, 0x2e, 0x72, 0x6f, 0x75, 0x74, + 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x54, 0x72, 0x61, 0x63, 0x6b, 0x50, 0x61, 0x79, 0x6d, 0x65, + 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x72, 0x6f, 0x75, 0x74, + 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x53, 0x74, 0x61, + 0x74, 0x75, 0x73, 0x22, 0x03, 0x88, 0x02, 0x01, 0x30, 0x01, 0x12, 0x66, 0x0a, 0x0f, 0x48, 0x74, + 0x6c, 0x63, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x12, 0x27, 0x2e, + 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x6f, 0x72, 0x77, 0x61, 0x72, + 0x64, 0x48, 0x74, 0x6c, 0x63, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x1a, 0x26, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, + 0x70, 0x63, 0x2e, 0x46, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x48, 0x74, 0x6c, 0x63, 0x49, 0x6e, + 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x28, 0x01, + 0x30, 0x01, 0x12, 0x5b, 0x0a, 0x10, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x43, 0x68, 0x61, 0x6e, + 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x22, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, + 0x70, 0x63, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x53, 0x74, 0x61, + 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x43, 0x68, 0x61, - 0x6e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, - 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, - 0x65, 0x43, 0x68, 0x61, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x42, 0x31, 0x5a, 0x2f, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, - 0x6d, 0x2f, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x6e, 0x69, 0x6e, 0x67, 0x6e, 0x65, 0x74, 0x77, 0x6f, - 0x72, 0x6b, 0x2f, 0x6c, 0x6e, 0x64, 0x2f, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2f, 0x72, 0x6f, 0x75, - 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x6e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x53, 0x0a, 0x14, 0x58, 0x41, 0x64, 0x64, 0x4c, 0x6f, 0x63, 0x61, 0x6c, 0x43, 0x68, 0x61, 0x6e, + 0x41, 0x6c, 0x69, 0x61, 0x73, 0x65, 0x73, 0x12, 0x1c, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, + 0x72, 0x70, 0x63, 0x2e, 0x41, 0x64, 0x64, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x65, 0x73, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, + 0x63, 0x2e, 0x41, 0x64, 0x64, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5c, 0x0a, 0x17, 0x58, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4c, + 0x6f, 0x63, 0x61, 0x6c, 0x43, 0x68, 0x61, 0x6e, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x65, 0x73, 0x12, + 0x1f, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x44, 0x65, 0x6c, 0x65, + 0x74, 0x65, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x20, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x44, 0x65, 0x6c, + 0x65, 0x74, 0x65, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x42, 0x31, 0x5a, 0x2f, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, + 0x2f, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x6e, 0x69, 0x6e, 0x67, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, + 0x6b, 0x2f, 0x6c, 0x6e, 0x64, 0x2f, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2f, 0x72, 0x6f, 0x75, 0x74, + 0x65, 0x72, 0x72, 0x70, 0x63, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -3940,7 +4157,7 @@ func file_routerrpc_router_proto_rawDescGZIP() []byte { } var file_routerrpc_router_proto_enumTypes = make([]protoimpl.EnumInfo, 6) -var file_routerrpc_router_proto_msgTypes = make([]protoimpl.MessageInfo, 43) +var file_routerrpc_router_proto_msgTypes = make([]protoimpl.MessageInfo, 47) var file_routerrpc_router_proto_goTypes = []interface{}{ (FailureDetail)(0), // 0: routerrpc.FailureDetail (PaymentState)(0), // 1: routerrpc.PaymentState @@ -3989,25 +4206,30 @@ var file_routerrpc_router_proto_goTypes = []interface{}{ (*ForwardHtlcInterceptResponse)(nil), // 44: routerrpc.ForwardHtlcInterceptResponse (*UpdateChanStatusRequest)(nil), // 45: routerrpc.UpdateChanStatusRequest (*UpdateChanStatusResponse)(nil), // 46: routerrpc.UpdateChanStatusResponse - nil, // 47: routerrpc.SendPaymentRequest.DestCustomRecordsEntry - nil, // 48: routerrpc.ForwardHtlcInterceptRequest.CustomRecordsEntry - (*lnrpc.RouteHint)(nil), // 49: lnrpc.RouteHint - (lnrpc.FeatureBit)(0), // 50: lnrpc.FeatureBit - (lnrpc.PaymentFailureReason)(0), // 51: lnrpc.PaymentFailureReason - (*lnrpc.Route)(nil), // 52: lnrpc.Route - (*lnrpc.Failure)(nil), // 53: lnrpc.Failure - (lnrpc.Failure_FailureCode)(0), // 54: lnrpc.Failure.FailureCode - (*lnrpc.HTLCAttempt)(nil), // 55: lnrpc.HTLCAttempt - (*lnrpc.ChannelPoint)(nil), // 56: lnrpc.ChannelPoint - (*lnrpc.Payment)(nil), // 57: lnrpc.Payment + (*AddAliasesRequest)(nil), // 47: routerrpc.AddAliasesRequest + (*AddAliasesResponse)(nil), // 48: routerrpc.AddAliasesResponse + (*DeleteAliasesRequest)(nil), // 49: routerrpc.DeleteAliasesRequest + (*DeleteAliasesResponse)(nil), // 50: routerrpc.DeleteAliasesResponse + nil, // 51: routerrpc.SendPaymentRequest.DestCustomRecordsEntry + nil, // 52: routerrpc.ForwardHtlcInterceptRequest.CustomRecordsEntry + (*lnrpc.RouteHint)(nil), // 53: lnrpc.RouteHint + (lnrpc.FeatureBit)(0), // 54: lnrpc.FeatureBit + (lnrpc.PaymentFailureReason)(0), // 55: lnrpc.PaymentFailureReason + (*lnrpc.Route)(nil), // 56: lnrpc.Route + (*lnrpc.Failure)(nil), // 57: lnrpc.Failure + (lnrpc.Failure_FailureCode)(0), // 58: lnrpc.Failure.FailureCode + (*lnrpc.HTLCAttempt)(nil), // 59: lnrpc.HTLCAttempt + (*lnrpc.ChannelPoint)(nil), // 60: lnrpc.ChannelPoint + (*lnrpc.AliasMap)(nil), // 61: lnrpc.AliasMap + (*lnrpc.Payment)(nil), // 62: lnrpc.Payment } var file_routerrpc_router_proto_depIdxs = []int32{ - 49, // 0: routerrpc.SendPaymentRequest.route_hints:type_name -> lnrpc.RouteHint - 47, // 1: routerrpc.SendPaymentRequest.dest_custom_records:type_name -> routerrpc.SendPaymentRequest.DestCustomRecordsEntry - 50, // 2: routerrpc.SendPaymentRequest.dest_features:type_name -> lnrpc.FeatureBit - 51, // 3: routerrpc.RouteFeeResponse.failure_reason:type_name -> lnrpc.PaymentFailureReason - 52, // 4: routerrpc.SendToRouteRequest.route:type_name -> lnrpc.Route - 53, // 5: routerrpc.SendToRouteResponse.failure:type_name -> lnrpc.Failure + 53, // 0: routerrpc.SendPaymentRequest.route_hints:type_name -> lnrpc.RouteHint + 51, // 1: routerrpc.SendPaymentRequest.dest_custom_records:type_name -> routerrpc.SendPaymentRequest.DestCustomRecordsEntry + 54, // 2: routerrpc.SendPaymentRequest.dest_features:type_name -> lnrpc.FeatureBit + 55, // 3: routerrpc.RouteFeeResponse.failure_reason:type_name -> lnrpc.PaymentFailureReason + 56, // 4: routerrpc.SendToRouteRequest.route:type_name -> lnrpc.Route + 57, // 5: routerrpc.SendToRouteResponse.failure:type_name -> lnrpc.Failure 19, // 6: routerrpc.QueryMissionControlResponse.pairs:type_name -> routerrpc.PairHistory 19, // 7: routerrpc.XImportMissionControlRequest.pairs:type_name -> routerrpc.PairHistory 20, // 8: routerrpc.PairHistory.history:type_name -> routerrpc.PairData @@ -4017,7 +4239,7 @@ var file_routerrpc_router_proto_depIdxs = []int32{ 27, // 12: routerrpc.MissionControlConfig.apriori:type_name -> routerrpc.AprioriParameters 26, // 13: routerrpc.MissionControlConfig.bimodal:type_name -> routerrpc.BimodalParameters 20, // 14: routerrpc.QueryProbabilityResponse.history:type_name -> routerrpc.PairData - 52, // 15: routerrpc.BuildRouteResponse.route:type_name -> lnrpc.Route + 56, // 15: routerrpc.BuildRouteResponse.route:type_name -> lnrpc.Route 5, // 16: routerrpc.HtlcEvent.event_type:type_name -> routerrpc.HtlcEvent.EventType 35, // 17: routerrpc.HtlcEvent.forward_event:type_name -> routerrpc.ForwardEvent 36, // 18: routerrpc.HtlcEvent.forward_fail_event:type_name -> routerrpc.ForwardFailEvent @@ -4027,58 +4249,66 @@ var file_routerrpc_router_proto_depIdxs = []int32{ 38, // 22: routerrpc.HtlcEvent.final_htlc_event:type_name -> routerrpc.FinalHtlcEvent 34, // 23: routerrpc.ForwardEvent.info:type_name -> routerrpc.HtlcInfo 34, // 24: routerrpc.LinkFailEvent.info:type_name -> routerrpc.HtlcInfo - 54, // 25: routerrpc.LinkFailEvent.wire_failure:type_name -> lnrpc.Failure.FailureCode + 58, // 25: routerrpc.LinkFailEvent.wire_failure:type_name -> lnrpc.Failure.FailureCode 0, // 26: routerrpc.LinkFailEvent.failure_detail:type_name -> routerrpc.FailureDetail 1, // 27: routerrpc.PaymentStatus.state:type_name -> routerrpc.PaymentState - 55, // 28: routerrpc.PaymentStatus.htlcs:type_name -> lnrpc.HTLCAttempt + 59, // 28: routerrpc.PaymentStatus.htlcs:type_name -> lnrpc.HTLCAttempt 42, // 29: routerrpc.ForwardHtlcInterceptRequest.incoming_circuit_key:type_name -> routerrpc.CircuitKey - 48, // 30: routerrpc.ForwardHtlcInterceptRequest.custom_records:type_name -> routerrpc.ForwardHtlcInterceptRequest.CustomRecordsEntry + 52, // 30: routerrpc.ForwardHtlcInterceptRequest.custom_records:type_name -> routerrpc.ForwardHtlcInterceptRequest.CustomRecordsEntry 42, // 31: routerrpc.ForwardHtlcInterceptResponse.incoming_circuit_key:type_name -> routerrpc.CircuitKey 2, // 32: routerrpc.ForwardHtlcInterceptResponse.action:type_name -> routerrpc.ResolveHoldForwardAction - 54, // 33: routerrpc.ForwardHtlcInterceptResponse.failure_code:type_name -> lnrpc.Failure.FailureCode - 56, // 34: routerrpc.UpdateChanStatusRequest.chan_point:type_name -> lnrpc.ChannelPoint + 58, // 33: routerrpc.ForwardHtlcInterceptResponse.failure_code:type_name -> lnrpc.Failure.FailureCode + 60, // 34: routerrpc.UpdateChanStatusRequest.chan_point:type_name -> lnrpc.ChannelPoint 3, // 35: routerrpc.UpdateChanStatusRequest.action:type_name -> routerrpc.ChanStatusAction - 6, // 36: routerrpc.Router.SendPaymentV2:input_type -> routerrpc.SendPaymentRequest - 7, // 37: routerrpc.Router.TrackPaymentV2:input_type -> routerrpc.TrackPaymentRequest - 8, // 38: routerrpc.Router.TrackPayments:input_type -> routerrpc.TrackPaymentsRequest - 9, // 39: routerrpc.Router.EstimateRouteFee:input_type -> routerrpc.RouteFeeRequest - 11, // 40: routerrpc.Router.SendToRoute:input_type -> routerrpc.SendToRouteRequest - 11, // 41: routerrpc.Router.SendToRouteV2:input_type -> routerrpc.SendToRouteRequest - 13, // 42: routerrpc.Router.ResetMissionControl:input_type -> routerrpc.ResetMissionControlRequest - 15, // 43: routerrpc.Router.QueryMissionControl:input_type -> routerrpc.QueryMissionControlRequest - 17, // 44: routerrpc.Router.XImportMissionControl:input_type -> routerrpc.XImportMissionControlRequest - 21, // 45: routerrpc.Router.GetMissionControlConfig:input_type -> routerrpc.GetMissionControlConfigRequest - 23, // 46: routerrpc.Router.SetMissionControlConfig:input_type -> routerrpc.SetMissionControlConfigRequest - 28, // 47: routerrpc.Router.QueryProbability:input_type -> routerrpc.QueryProbabilityRequest - 30, // 48: routerrpc.Router.BuildRoute:input_type -> routerrpc.BuildRouteRequest - 32, // 49: routerrpc.Router.SubscribeHtlcEvents:input_type -> routerrpc.SubscribeHtlcEventsRequest - 6, // 50: routerrpc.Router.SendPayment:input_type -> routerrpc.SendPaymentRequest - 7, // 51: routerrpc.Router.TrackPayment:input_type -> routerrpc.TrackPaymentRequest - 44, // 52: routerrpc.Router.HtlcInterceptor:input_type -> routerrpc.ForwardHtlcInterceptResponse - 45, // 53: routerrpc.Router.UpdateChanStatus:input_type -> routerrpc.UpdateChanStatusRequest - 57, // 54: routerrpc.Router.SendPaymentV2:output_type -> lnrpc.Payment - 57, // 55: routerrpc.Router.TrackPaymentV2:output_type -> lnrpc.Payment - 57, // 56: routerrpc.Router.TrackPayments:output_type -> lnrpc.Payment - 10, // 57: routerrpc.Router.EstimateRouteFee:output_type -> routerrpc.RouteFeeResponse - 12, // 58: routerrpc.Router.SendToRoute:output_type -> routerrpc.SendToRouteResponse - 55, // 59: routerrpc.Router.SendToRouteV2:output_type -> lnrpc.HTLCAttempt - 14, // 60: routerrpc.Router.ResetMissionControl:output_type -> routerrpc.ResetMissionControlResponse - 16, // 61: routerrpc.Router.QueryMissionControl:output_type -> routerrpc.QueryMissionControlResponse - 18, // 62: routerrpc.Router.XImportMissionControl:output_type -> routerrpc.XImportMissionControlResponse - 22, // 63: routerrpc.Router.GetMissionControlConfig:output_type -> routerrpc.GetMissionControlConfigResponse - 24, // 64: routerrpc.Router.SetMissionControlConfig:output_type -> routerrpc.SetMissionControlConfigResponse - 29, // 65: routerrpc.Router.QueryProbability:output_type -> routerrpc.QueryProbabilityResponse - 31, // 66: routerrpc.Router.BuildRoute:output_type -> routerrpc.BuildRouteResponse - 33, // 67: routerrpc.Router.SubscribeHtlcEvents:output_type -> routerrpc.HtlcEvent - 41, // 68: routerrpc.Router.SendPayment:output_type -> routerrpc.PaymentStatus - 41, // 69: routerrpc.Router.TrackPayment:output_type -> routerrpc.PaymentStatus - 43, // 70: routerrpc.Router.HtlcInterceptor:output_type -> routerrpc.ForwardHtlcInterceptRequest - 46, // 71: routerrpc.Router.UpdateChanStatus:output_type -> routerrpc.UpdateChanStatusResponse - 54, // [54:72] is the sub-list for method output_type - 36, // [36:54] is the sub-list for method input_type - 36, // [36:36] is the sub-list for extension type_name - 36, // [36:36] is the sub-list for extension extendee - 0, // [0:36] is the sub-list for field type_name + 61, // 36: routerrpc.AddAliasesRequest.alias_maps:type_name -> lnrpc.AliasMap + 61, // 37: routerrpc.AddAliasesResponse.alias_maps:type_name -> lnrpc.AliasMap + 61, // 38: routerrpc.DeleteAliasesRequest.alias_maps:type_name -> lnrpc.AliasMap + 61, // 39: routerrpc.DeleteAliasesResponse.alias_maps:type_name -> lnrpc.AliasMap + 6, // 40: routerrpc.Router.SendPaymentV2:input_type -> routerrpc.SendPaymentRequest + 7, // 41: routerrpc.Router.TrackPaymentV2:input_type -> routerrpc.TrackPaymentRequest + 8, // 42: routerrpc.Router.TrackPayments:input_type -> routerrpc.TrackPaymentsRequest + 9, // 43: routerrpc.Router.EstimateRouteFee:input_type -> routerrpc.RouteFeeRequest + 11, // 44: routerrpc.Router.SendToRoute:input_type -> routerrpc.SendToRouteRequest + 11, // 45: routerrpc.Router.SendToRouteV2:input_type -> routerrpc.SendToRouteRequest + 13, // 46: routerrpc.Router.ResetMissionControl:input_type -> routerrpc.ResetMissionControlRequest + 15, // 47: routerrpc.Router.QueryMissionControl:input_type -> routerrpc.QueryMissionControlRequest + 17, // 48: routerrpc.Router.XImportMissionControl:input_type -> routerrpc.XImportMissionControlRequest + 21, // 49: routerrpc.Router.GetMissionControlConfig:input_type -> routerrpc.GetMissionControlConfigRequest + 23, // 50: routerrpc.Router.SetMissionControlConfig:input_type -> routerrpc.SetMissionControlConfigRequest + 28, // 51: routerrpc.Router.QueryProbability:input_type -> routerrpc.QueryProbabilityRequest + 30, // 52: routerrpc.Router.BuildRoute:input_type -> routerrpc.BuildRouteRequest + 32, // 53: routerrpc.Router.SubscribeHtlcEvents:input_type -> routerrpc.SubscribeHtlcEventsRequest + 6, // 54: routerrpc.Router.SendPayment:input_type -> routerrpc.SendPaymentRequest + 7, // 55: routerrpc.Router.TrackPayment:input_type -> routerrpc.TrackPaymentRequest + 44, // 56: routerrpc.Router.HtlcInterceptor:input_type -> routerrpc.ForwardHtlcInterceptResponse + 45, // 57: routerrpc.Router.UpdateChanStatus:input_type -> routerrpc.UpdateChanStatusRequest + 47, // 58: routerrpc.Router.XAddLocalChanAliases:input_type -> routerrpc.AddAliasesRequest + 49, // 59: routerrpc.Router.XDeleteLocalChanAliases:input_type -> routerrpc.DeleteAliasesRequest + 62, // 60: routerrpc.Router.SendPaymentV2:output_type -> lnrpc.Payment + 62, // 61: routerrpc.Router.TrackPaymentV2:output_type -> lnrpc.Payment + 62, // 62: routerrpc.Router.TrackPayments:output_type -> lnrpc.Payment + 10, // 63: routerrpc.Router.EstimateRouteFee:output_type -> routerrpc.RouteFeeResponse + 12, // 64: routerrpc.Router.SendToRoute:output_type -> routerrpc.SendToRouteResponse + 59, // 65: routerrpc.Router.SendToRouteV2:output_type -> lnrpc.HTLCAttempt + 14, // 66: routerrpc.Router.ResetMissionControl:output_type -> routerrpc.ResetMissionControlResponse + 16, // 67: routerrpc.Router.QueryMissionControl:output_type -> routerrpc.QueryMissionControlResponse + 18, // 68: routerrpc.Router.XImportMissionControl:output_type -> routerrpc.XImportMissionControlResponse + 22, // 69: routerrpc.Router.GetMissionControlConfig:output_type -> routerrpc.GetMissionControlConfigResponse + 24, // 70: routerrpc.Router.SetMissionControlConfig:output_type -> routerrpc.SetMissionControlConfigResponse + 29, // 71: routerrpc.Router.QueryProbability:output_type -> routerrpc.QueryProbabilityResponse + 31, // 72: routerrpc.Router.BuildRoute:output_type -> routerrpc.BuildRouteResponse + 33, // 73: routerrpc.Router.SubscribeHtlcEvents:output_type -> routerrpc.HtlcEvent + 41, // 74: routerrpc.Router.SendPayment:output_type -> routerrpc.PaymentStatus + 41, // 75: routerrpc.Router.TrackPayment:output_type -> routerrpc.PaymentStatus + 43, // 76: routerrpc.Router.HtlcInterceptor:output_type -> routerrpc.ForwardHtlcInterceptRequest + 46, // 77: routerrpc.Router.UpdateChanStatus:output_type -> routerrpc.UpdateChanStatusResponse + 48, // 78: routerrpc.Router.XAddLocalChanAliases:output_type -> routerrpc.AddAliasesResponse + 50, // 79: routerrpc.Router.XDeleteLocalChanAliases:output_type -> routerrpc.DeleteAliasesResponse + 60, // [60:80] is the sub-list for method output_type + 40, // [40:60] is the sub-list for method input_type + 40, // [40:40] is the sub-list for extension type_name + 40, // [40:40] is the sub-list for extension extendee + 0, // [0:40] is the sub-list for field type_name } func init() { file_routerrpc_router_proto_init() } @@ -4579,6 +4809,54 @@ func file_routerrpc_router_proto_init() { return nil } } + file_routerrpc_router_proto_msgTypes[41].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*AddAliasesRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_routerrpc_router_proto_msgTypes[42].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*AddAliasesResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_routerrpc_router_proto_msgTypes[43].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*DeleteAliasesRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_routerrpc_router_proto_msgTypes[44].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*DeleteAliasesResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } } file_routerrpc_router_proto_msgTypes[19].OneofWrappers = []interface{}{ (*MissionControlConfig_Apriori)(nil), @@ -4598,7 +4876,7 @@ func file_routerrpc_router_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_routerrpc_router_proto_rawDesc, NumEnums: 6, - NumMessages: 43, + NumMessages: 47, NumExtensions: 0, NumServices: 1, }, diff --git a/lnrpc/routerrpc/router.pb.gw.go b/lnrpc/routerrpc/router.pb.gw.go index fcfc3ad82e..be2498bf5c 100644 --- a/lnrpc/routerrpc/router.pb.gw.go +++ b/lnrpc/routerrpc/router.pb.gw.go @@ -564,6 +564,74 @@ func local_request_Router_UpdateChanStatus_0(ctx context.Context, marshaler runt } +func request_Router_XAddLocalChanAliases_0(ctx context.Context, marshaler runtime.Marshaler, client RouterClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq AddAliasesRequest + var metadata runtime.ServerMetadata + + newReader, berr := utilities.IOReaderFactory(req.Body) + if berr != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) + } + if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.XAddLocalChanAliases(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Router_XAddLocalChanAliases_0(ctx context.Context, marshaler runtime.Marshaler, server RouterServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq AddAliasesRequest + var metadata runtime.ServerMetadata + + newReader, berr := utilities.IOReaderFactory(req.Body) + if berr != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) + } + if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := server.XAddLocalChanAliases(ctx, &protoReq) + return msg, metadata, err + +} + +func request_Router_XDeleteLocalChanAliases_0(ctx context.Context, marshaler runtime.Marshaler, client RouterClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq DeleteAliasesRequest + var metadata runtime.ServerMetadata + + newReader, berr := utilities.IOReaderFactory(req.Body) + if berr != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) + } + if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.XDeleteLocalChanAliases(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Router_XDeleteLocalChanAliases_0(ctx context.Context, marshaler runtime.Marshaler, server RouterServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq DeleteAliasesRequest + var metadata runtime.ServerMetadata + + newReader, berr := utilities.IOReaderFactory(req.Body) + if berr != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) + } + if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := server.XDeleteLocalChanAliases(ctx, &protoReq) + return msg, metadata, err + +} + // RegisterRouterHandlerServer registers the http handlers for service Router to "mux". // UnaryRPC :call RouterServer directly. // StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906. @@ -835,6 +903,52 @@ func RegisterRouterHandlerServer(ctx context.Context, mux *runtime.ServeMux, ser }) + mux.Handle("POST", pattern_Router_XAddLocalChanAliases_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/routerrpc.Router/XAddLocalChanAliases", runtime.WithHTTPPathPattern("/v2/router/x/addaliases")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_Router_XAddLocalChanAliases_0(rctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Router_XAddLocalChanAliases_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("POST", pattern_Router_XDeleteLocalChanAliases_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/routerrpc.Router/XDeleteLocalChanAliases", runtime.WithHTTPPathPattern("/v2/router/x/deletealiases")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_Router_XDeleteLocalChanAliases_0(rctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Router_XDeleteLocalChanAliases_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + return nil } @@ -1176,6 +1290,46 @@ func RegisterRouterHandlerClient(ctx context.Context, mux *runtime.ServeMux, cli }) + mux.Handle("POST", pattern_Router_XAddLocalChanAliases_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateContext(ctx, mux, req, "/routerrpc.Router/XAddLocalChanAliases", runtime.WithHTTPPathPattern("/v2/router/x/addaliases")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_Router_XAddLocalChanAliases_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Router_XAddLocalChanAliases_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("POST", pattern_Router_XDeleteLocalChanAliases_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateContext(ctx, mux, req, "/routerrpc.Router/XDeleteLocalChanAliases", runtime.WithHTTPPathPattern("/v2/router/x/deletealiases")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_Router_XDeleteLocalChanAliases_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Router_XDeleteLocalChanAliases_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + return nil } @@ -1209,6 +1363,10 @@ var ( pattern_Router_HtlcInterceptor_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v2", "router", "htlcinterceptor"}, "")) pattern_Router_UpdateChanStatus_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v2", "router", "updatechanstatus"}, "")) + + pattern_Router_XAddLocalChanAliases_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"v2", "router", "x", "addaliases"}, "")) + + pattern_Router_XDeleteLocalChanAliases_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"v2", "router", "x", "deletealiases"}, "")) ) var ( @@ -1241,4 +1399,8 @@ var ( forward_Router_HtlcInterceptor_0 = runtime.ForwardResponseStream forward_Router_UpdateChanStatus_0 = runtime.ForwardResponseMessage + + forward_Router_XAddLocalChanAliases_0 = runtime.ForwardResponseMessage + + forward_Router_XDeleteLocalChanAliases_0 = runtime.ForwardResponseMessage ) diff --git a/lnrpc/routerrpc/router.pb.json.go b/lnrpc/routerrpc/router.pb.json.go index 03b9e8a24e..1b87402942 100644 --- a/lnrpc/routerrpc/router.pb.json.go +++ b/lnrpc/routerrpc/router.pb.json.go @@ -547,4 +547,54 @@ func RegisterRouterJSONCallbacks(registry map[string]func(ctx context.Context, } callback(string(respBytes), nil) } + + registry["routerrpc.Router.XAddLocalChanAliases"] = func(ctx context.Context, + conn *grpc.ClientConn, reqJSON string, callback func(string, error)) { + + req := &AddAliasesRequest{} + err := marshaler.Unmarshal([]byte(reqJSON), req) + if err != nil { + callback("", err) + return + } + + client := NewRouterClient(conn) + resp, err := client.XAddLocalChanAliases(ctx, req) + if err != nil { + callback("", err) + return + } + + respBytes, err := marshaler.Marshal(resp) + if err != nil { + callback("", err) + return + } + callback(string(respBytes), nil) + } + + registry["routerrpc.Router.XDeleteLocalChanAliases"] = func(ctx context.Context, + conn *grpc.ClientConn, reqJSON string, callback func(string, error)) { + + req := &DeleteAliasesRequest{} + err := marshaler.Unmarshal([]byte(reqJSON), req) + if err != nil { + callback("", err) + return + } + + client := NewRouterClient(conn) + resp, err := client.XDeleteLocalChanAliases(ctx, req) + if err != nil { + callback("", err) + return + } + + respBytes, err := marshaler.Marshal(resp) + if err != nil { + callback("", err) + return + } + callback(string(respBytes), nil) + } } diff --git a/lnrpc/routerrpc/router.proto b/lnrpc/routerrpc/router.proto index 1856bccbee..03a481b97c 100644 --- a/lnrpc/routerrpc/router.proto +++ b/lnrpc/routerrpc/router.proto @@ -176,6 +176,25 @@ service Router { */ rpc UpdateChanStatus (UpdateChanStatusRequest) returns (UpdateChanStatusResponse); + + /* + XAddLocalChanAliases is an experimental API that creates a set of new + channel SCID alias mappings. The final total set of aliases in the manager + after the add operation is returned. This is only a locally stored alias, + and will not be communicated to the channel peer via any message. Therefore, + routing over such an alias will only work if the peer also calls this same + RPC on their end. If an alias already exists, an error is returned + */ + rpc XAddLocalChanAliases (AddAliasesRequest) returns (AddAliasesResponse); + + /* + XDeleteLocalChanAliases is an experimental API that deletes a set of alias + mappings. The final total set of aliases in the manager after the delete + operation is returned. The deletion will not be communicated to the channel + peer via any message. + */ + rpc XDeleteLocalChanAliases (DeleteAliasesRequest) + returns (DeleteAliasesResponse); } message SendPaymentRequest { @@ -1021,3 +1040,19 @@ enum ChanStatusAction { message UpdateChanStatusResponse { } + +message AddAliasesRequest { + repeated lnrpc.AliasMap alias_maps = 1; +} + +message AddAliasesResponse { + repeated lnrpc.AliasMap alias_maps = 1; +} + +message DeleteAliasesRequest { + repeated lnrpc.AliasMap alias_maps = 1; +} + +message DeleteAliasesResponse { + repeated lnrpc.AliasMap alias_maps = 1; +} \ No newline at end of file diff --git a/lnrpc/routerrpc/router.swagger.json b/lnrpc/routerrpc/router.swagger.json index 7c8aa6217d..5e9cc70c59 100644 --- a/lnrpc/routerrpc/router.swagger.json +++ b/lnrpc/routerrpc/router.swagger.json @@ -514,6 +514,72 @@ ] } }, + "/v2/router/x/addaliases": { + "post": { + "summary": "XAddLocalChanAliases is an experimental API that creates a set of new\nchannel SCID alias mappings. The final total set of aliases in the manager\nafter the add operation is returned. This is only a locally stored alias,\nand will not be communicated to the channel peer via any message. Therefore,\nrouting over such an alias will only work if the peer also calls this same\nRPC on their end. If an alias already exists, an error is returned", + "operationId": "Router_XAddLocalChanAliases", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/routerrpcAddAliasesResponse" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + } + }, + "parameters": [ + { + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/routerrpcAddAliasesRequest" + } + } + ], + "tags": [ + "Router" + ] + } + }, + "/v2/router/x/deletealiases": { + "post": { + "summary": "XDeleteLocalChanAliases is an experimental API that deletes a set of alias\nmappings. The final total set of aliases in the manager after the delete\noperation is returned. The deletion will not be communicated to the channel\npeer via any message.", + "operationId": "Router_XDeleteLocalChanAliases", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/routerrpcDeleteAliasesResponse" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + } + }, + "parameters": [ + { + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/routerrpcDeleteAliasesRequest" + } + } + ], + "tags": [ + "Router" + ] + } + }, "/v2/router/x/importhistory": { "post": { "summary": "lncli: `importmc`\nXImportMissionControl is an experimental API that imports the state provided\nto the internal mission control's state, using all results which are more\nrecent than our existing values. These values will only be imported\nin-memory, and will not be persisted across restarts.", @@ -619,6 +685,24 @@ } } }, + "lnrpcAliasMap": { + "type": "object", + "properties": { + "base_scid": { + "type": "string", + "format": "uint64", + "description": "For non-zero-conf channels, this is the confirmed SCID. Otherwise, this is\nthe first assigned \"base\" alias." + }, + "aliases": { + "type": "array", + "items": { + "type": "string", + "format": "uint64" + }, + "description": "The set of all aliases stored for the base SCID." + } + } + }, "lnrpcChannelPoint": { "type": "object", "properties": { @@ -1102,6 +1186,28 @@ } } }, + "routerrpcAddAliasesRequest": { + "type": "object", + "properties": { + "alias_maps": { + "type": "array", + "items": { + "$ref": "#/definitions/lnrpcAliasMap" + } + } + } + }, + "routerrpcAddAliasesResponse": { + "type": "object", + "properties": { + "alias_maps": { + "type": "array", + "items": { + "$ref": "#/definitions/lnrpcAliasMap" + } + } + } + }, "routerrpcAprioriParameters": { "type": "object", "properties": { @@ -1213,6 +1319,28 @@ } } }, + "routerrpcDeleteAliasesRequest": { + "type": "object", + "properties": { + "alias_maps": { + "type": "array", + "items": { + "$ref": "#/definitions/lnrpcAliasMap" + } + } + } + }, + "routerrpcDeleteAliasesResponse": { + "type": "object", + "properties": { + "alias_maps": { + "type": "array", + "items": { + "$ref": "#/definitions/lnrpcAliasMap" + } + } + } + }, "routerrpcFailureDetail": { "type": "string", "enum": [ diff --git a/lnrpc/routerrpc/router.yaml b/lnrpc/routerrpc/router.yaml index 7c69376edf..4d1d370ffb 100644 --- a/lnrpc/routerrpc/router.yaml +++ b/lnrpc/routerrpc/router.yaml @@ -48,3 +48,10 @@ http: - selector: routerrpc.Router.UpdateChanStatus post: "/v2/router/updatechanstatus" body: "*" + - selector: routerrpc.Router.XAddLocalChanAliases + post: "/v2/router/x/addaliases" + body: "*" + - selector: routerrpc.Router.XDeleteLocalChanAliases + post: "/v2/router/x/deletealiases" + body: "*" + diff --git a/lnrpc/routerrpc/router_grpc.pb.go b/lnrpc/routerrpc/router_grpc.pb.go index 2259943375..cb68f128e5 100644 --- a/lnrpc/routerrpc/router_grpc.pb.go +++ b/lnrpc/routerrpc/router_grpc.pb.go @@ -116,6 +116,18 @@ type RouterClient interface { // channel to stay disabled until a subsequent manual request of either // "enable" or "auto". UpdateChanStatus(ctx context.Context, in *UpdateChanStatusRequest, opts ...grpc.CallOption) (*UpdateChanStatusResponse, error) + // XAddLocalChanAliases is an experimental API that creates a set of new + // channel SCID alias mappings. The final total set of aliases in the manager + // after the add operation is returned. This is only a locally stored alias, + // and will not be communicated to the channel peer via any message. Therefore, + // routing over such an alias will only work if the peer also calls this same + // RPC on their end. If an alias already exists, an error is returned + XAddLocalChanAliases(ctx context.Context, in *AddAliasesRequest, opts ...grpc.CallOption) (*AddAliasesResponse, error) + // XDeleteLocalChanAliases is an experimental API that deletes a set of alias + // mappings. The final total set of aliases in the manager after the delete + // operation is returned. The deletion will not be communicated to the channel + // peer via any message. + XDeleteLocalChanAliases(ctx context.Context, in *DeleteAliasesRequest, opts ...grpc.CallOption) (*DeleteAliasesResponse, error) } type routerClient struct { @@ -451,6 +463,24 @@ func (c *routerClient) UpdateChanStatus(ctx context.Context, in *UpdateChanStatu return out, nil } +func (c *routerClient) XAddLocalChanAliases(ctx context.Context, in *AddAliasesRequest, opts ...grpc.CallOption) (*AddAliasesResponse, error) { + out := new(AddAliasesResponse) + err := c.cc.Invoke(ctx, "/routerrpc.Router/XAddLocalChanAliases", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *routerClient) XDeleteLocalChanAliases(ctx context.Context, in *DeleteAliasesRequest, opts ...grpc.CallOption) (*DeleteAliasesResponse, error) { + out := new(DeleteAliasesResponse) + err := c.cc.Invoke(ctx, "/routerrpc.Router/XDeleteLocalChanAliases", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + // RouterServer is the server API for Router service. // All implementations must embed UnimplementedRouterServer // for forward compatibility @@ -552,6 +582,18 @@ type RouterServer interface { // channel to stay disabled until a subsequent manual request of either // "enable" or "auto". UpdateChanStatus(context.Context, *UpdateChanStatusRequest) (*UpdateChanStatusResponse, error) + // XAddLocalChanAliases is an experimental API that creates a set of new + // channel SCID alias mappings. The final total set of aliases in the manager + // after the add operation is returned. This is only a locally stored alias, + // and will not be communicated to the channel peer via any message. Therefore, + // routing over such an alias will only work if the peer also calls this same + // RPC on their end. If an alias already exists, an error is returned + XAddLocalChanAliases(context.Context, *AddAliasesRequest) (*AddAliasesResponse, error) + // XDeleteLocalChanAliases is an experimental API that deletes a set of alias + // mappings. The final total set of aliases in the manager after the delete + // operation is returned. The deletion will not be communicated to the channel + // peer via any message. + XDeleteLocalChanAliases(context.Context, *DeleteAliasesRequest) (*DeleteAliasesResponse, error) mustEmbedUnimplementedRouterServer() } @@ -613,6 +655,12 @@ func (UnimplementedRouterServer) HtlcInterceptor(Router_HtlcInterceptorServer) e func (UnimplementedRouterServer) UpdateChanStatus(context.Context, *UpdateChanStatusRequest) (*UpdateChanStatusResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method UpdateChanStatus not implemented") } +func (UnimplementedRouterServer) XAddLocalChanAliases(context.Context, *AddAliasesRequest) (*AddAliasesResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method XAddLocalChanAliases not implemented") +} +func (UnimplementedRouterServer) XDeleteLocalChanAliases(context.Context, *DeleteAliasesRequest) (*DeleteAliasesResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method XDeleteLocalChanAliases not implemented") +} func (UnimplementedRouterServer) mustEmbedUnimplementedRouterServer() {} // UnsafeRouterServer may be embedded to opt out of forward compatibility for this service. @@ -976,6 +1024,42 @@ func _Router_UpdateChanStatus_Handler(srv interface{}, ctx context.Context, dec return interceptor(ctx, in, info, handler) } +func _Router_XAddLocalChanAliases_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(AddAliasesRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(RouterServer).XAddLocalChanAliases(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/routerrpc.Router/XAddLocalChanAliases", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(RouterServer).XAddLocalChanAliases(ctx, req.(*AddAliasesRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Router_XDeleteLocalChanAliases_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(DeleteAliasesRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(RouterServer).XDeleteLocalChanAliases(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/routerrpc.Router/XDeleteLocalChanAliases", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(RouterServer).XDeleteLocalChanAliases(ctx, req.(*DeleteAliasesRequest)) + } + return interceptor(ctx, in, info, handler) +} + // Router_ServiceDesc is the grpc.ServiceDesc for Router service. // It's only intended for direct use with grpc.RegisterService, // and not to be introspected or modified (even as a copy) @@ -1027,6 +1111,14 @@ var Router_ServiceDesc = grpc.ServiceDesc{ MethodName: "UpdateChanStatus", Handler: _Router_UpdateChanStatus_Handler, }, + { + MethodName: "XAddLocalChanAliases", + Handler: _Router_XAddLocalChanAliases_Handler, + }, + { + MethodName: "XDeleteLocalChanAliases", + Handler: _Router_XDeleteLocalChanAliases_Handler, + }, }, Streams: []grpc.StreamDesc{ { diff --git a/lnrpc/routerrpc/router_server.go b/lnrpc/routerrpc/router_server.go index 30dba22272..f43f95929f 100644 --- a/lnrpc/routerrpc/router_server.go +++ b/lnrpc/routerrpc/router_server.go @@ -14,6 +14,7 @@ import ( "github.com/btcsuite/btcd/btcutil" "github.com/btcsuite/btcd/wire" "github.com/grpc-ecosystem/grpc-gateway/v2/runtime" + "github.com/lightningnetwork/lnd/aliasmgr" "github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/fn" "github.com/lightningnetwork/lnd/lnrpc" @@ -55,6 +56,14 @@ var ( errUnexpectedFailureSource = errors.New("unexpected failure source") + // ErrAliasAlreadyExists is returned if a new SCID alias is attempted + // to be added that already exists. + ErrAliasAlreadyExists = errors.New("alias already exists") + + // ErrNoValidAlias is returned if an alias is not in the valid range for + // allowed SCID aliases. + ErrNoValidAlias = errors.New("not a valid alias") + // macaroonOps are the set of capabilities that our minted macaroon (if // it doesn't already exist) will have. macaroonOps = []bakery.Op{ @@ -142,6 +151,14 @@ var ( Entity: "offchain", Action: "write", }}, + "/routerrpc.Router/XAddLocalChanAliases": {{ + Entity: "offchain", + Action: "write", + }}, + "/routerrpc.Router/XDeleteLocalChanAliases": {{ + Entity: "offchain", + Action: "write", + }}, } // DefaultRouterMacFilename is the default name of the router macaroon @@ -1531,6 +1548,125 @@ func (s *Server) HtlcInterceptor(stream Router_HtlcInterceptorServer) error { ).run() } +// XAddLocalChanAliases is an experimental API that creates a set of new +// channel SCID alias mappings. The final total set of aliases in the manager +// after the add operation is returned. This is only a locally stored alias, and +// will not be communicated to the channel peer via any message. Therefore, +// routing over such an alias will only work if the peer also calls this same +// RPC on their end. If an alias already exists, an error is returned. +func (s *Server) XAddLocalChanAliases(_ context.Context, + in *AddAliasesRequest) (*AddAliasesResponse, error) { + + existingAliases := s.cfg.AliasMgr.ListAliases() + + // aliasExists checks if the new alias already exists in the alias map. + aliasExists := func(newAlias uint64, + baseScid lnwire.ShortChannelID) (bool, error) { + + // First check that we actually have a channel for the given + // base scid. This should succeed for any channel where the + // option-scid-alias feature bit was negotiated. + if _, ok := existingAliases[baseScid]; !ok { + return false, fmt.Errorf("base scid %v not found", + baseScid) + } + + for base, aliases := range existingAliases { + for _, alias := range aliases { + exists := alias.ToUint64() == newAlias + + // Trying to add an alias that we already have + // for another channel is wrong. + if exists && base != baseScid { + return true, fmt.Errorf("%w: alias %v "+ + "already exists for base scid "+ + "%v", ErrAliasAlreadyExists, + alias, base) + } + + if exists { + return true, nil + } + } + } + + return false, nil + } + + for _, v := range in.AliasMaps { + baseScid := lnwire.NewShortChanIDFromInt(v.BaseScid) + + for _, rpcAlias := range v.Aliases { + // If not, let's add it to the alias manager now. + aliasScid := lnwire.NewShortChanIDFromInt(rpcAlias) + + // But we only add it, if it's a valid alias, as defined + // by the BOLT spec. + if !aliasmgr.IsAlias(aliasScid) { + return nil, fmt.Errorf("%w: SCID alias %v is "+ + "not a valid alias", ErrNoValidAlias, + aliasScid) + } + + exists, err := aliasExists(rpcAlias, baseScid) + if err != nil { + return nil, err + } + + // If the alias already exists, we see that as an error. + // This is to avoid "silent" collisions. + if exists { + return nil, fmt.Errorf("%w: SCID alias %v "+ + "already exists", ErrAliasAlreadyExists, + rpcAlias) + } + + err = s.cfg.AliasMgr.AddLocalAlias( + aliasScid, baseScid, false, true, + ) + if err != nil { + return nil, fmt.Errorf("error adding scid "+ + "alias, base_scid=%v, alias_scid=%v: "+ + "%w", baseScid, aliasScid, err) + } + } + } + + return &AddAliasesResponse{ + AliasMaps: lnrpc.MarshalAliasMap(s.cfg.AliasMgr.ListAliases()), + }, nil +} + +// XDeleteLocalChanAliases is an experimental API that deletes a set of alias +// mappings. The final total set of aliases in the manager after the delete +// operation is returned. The deletion will not be communicated to the channel +// peer via any message. +func (s *Server) XDeleteLocalChanAliases(_ context.Context, + in *DeleteAliasesRequest) (*DeleteAliasesResponse, + error) { + + for _, v := range in.AliasMaps { + baseScid := lnwire.NewShortChanIDFromInt(v.BaseScid) + + for _, alias := range v.Aliases { + aliasScid := lnwire.NewShortChanIDFromInt(alias) + + err := s.cfg.AliasMgr.DeleteLocalAlias( + aliasScid, baseScid, + ) + if err != nil { + return nil, fmt.Errorf("error deleting scid "+ + "alias, base_scid=%v, alias_scid=%v: "+ + "%w", baseScid, aliasScid, err) + } + } + } + + return &DeleteAliasesResponse{ + AliasMaps: lnrpc.MarshalAliasMap(s.cfg.AliasMgr.ListAliases()), + }, nil +} + func extractOutPoint(req *UpdateChanStatusRequest) (*wire.OutPoint, error) { chanPoint := req.GetChanPoint() txid, err := lnrpc.GetChanPointFundingTxid(chanPoint) diff --git a/rpcserver.go b/rpcserver.go index 7fef873266..ad54601577 100644 --- a/rpcserver.go +++ b/rpcserver.go @@ -761,7 +761,7 @@ func (r *rpcServer) addDeps(s *server, macService *macaroons.Service, s.sweeper, tower, s.towerClientMgr, r.cfg.net.ResolveTCPAddr, genInvoiceFeatures, genAmpInvoiceFeatures, s.getNodeAnnouncement, s.updateAndBrodcastSelfNode, parseAddr, - rpcsLog, s.aliasMgr.GetPeerAlias, + rpcsLog, s.aliasMgr, ) if err != nil { return err @@ -8548,17 +8548,8 @@ func (r *rpcServer) ListAliases(ctx context.Context, AliasMaps: make([]*lnrpc.AliasMap, 0), } - for base, set := range mapAliases { - rpcMap := &lnrpc.AliasMap{ - BaseScid: base.ToUint64(), - } - for _, alias := range set { - rpcMap.Aliases = append( - rpcMap.Aliases, alias.ToUint64(), - ) - } - resp.AliasMaps = append(resp.AliasMaps, rpcMap) - } + // Now we need to parse the created mappings into an rpc response. + resp.AliasMaps = lnrpc.MarshalAliasMap(mapAliases) return resp, nil } diff --git a/subrpcserver_config.go b/subrpcserver_config.go index 6687f71a7d..5127068b65 100644 --- a/subrpcserver_config.go +++ b/subrpcserver_config.go @@ -7,6 +7,7 @@ import ( "github.com/btcsuite/btcd/chaincfg" "github.com/btcsuite/btclog" + "github.com/lightningnetwork/lnd/aliasmgr" "github.com/lightningnetwork/lnd/autopilot" "github.com/lightningnetwork/lnd/chainreg" "github.com/lightningnetwork/lnd/channeldb" @@ -121,8 +122,7 @@ func (s *subRPCServerConfigs) PopulateDependencies(cfg *Config, updateNodeAnnouncement func(features *lnwire.RawFeatureVector, modifiers ...netann.NodeAnnModifier) error, parseAddr func(addr string) (net.Addr, error), - rpcLogger btclog.Logger, - getAlias func(lnwire.ChannelID) (lnwire.ShortChannelID, error)) error { + rpcLogger btclog.Logger, aliasMgr *aliasmgr.Manager) error { // First, we'll use reflect to obtain a version of the config struct // that allows us to programmatically inspect its fields. @@ -264,7 +264,7 @@ func (s *subRPCServerConfigs) PopulateDependencies(cfg *Config, reflect.ValueOf(genAmpInvoiceFeatures), ) subCfgValue.FieldByName("GetAlias").Set( - reflect.ValueOf(getAlias), + reflect.ValueOf(aliasMgr.GetPeerAlias), ) case *neutrinorpc.Config: @@ -277,6 +277,11 @@ func (s *subRPCServerConfigs) PopulateDependencies(cfg *Config, // RouterRPC isn't conditionally compiled and doesn't need to be // populated using reflection. case *routerrpc.Config: + subCfgValue := extractReflectValue(subCfg) + + subCfgValue.FieldByName("AliasMgr").Set( + reflect.ValueOf(aliasMgr), + ) case *watchtowerrpc.Config: subCfgValue := extractReflectValue(subCfg) From 08ca28773c9e5a6a760c61b34923d37a84307337 Mon Sep 17 00:00:00 2001 From: George Tsagkarelis Date: Fri, 8 Mar 2024 13:21:22 +0100 Subject: [PATCH 057/218] itest: add dynamic scid alias routing test --- itest/list_on_test.go | 4 + itest/lnd_routing_test.go | 189 ++++++++++++++++++++++++++++++++++++++ lntest/rpc/harness_rpc.go | 3 + lntest/rpc/router.go | 48 ++++++++++ 4 files changed, 244 insertions(+) diff --git a/itest/list_on_test.go b/itest/list_on_test.go index 82fcdd99b5..147a0de0c0 100644 --- a/itest/list_on_test.go +++ b/itest/list_on_test.go @@ -358,6 +358,10 @@ var allTestCases = []*lntest.TestCase{ Name: "invoice routing hints", TestFunc: testInvoiceRoutingHints, }, + { + Name: "scid alias routing hints", + TestFunc: testScidAliasRoutingHints, + }, { Name: "multi-hop payments over private channels", TestFunc: testMultiHopOverPrivateChannels, diff --git a/itest/lnd_routing_test.go b/itest/lnd_routing_test.go index 0b1cfebe84..d92de5aba5 100644 --- a/itest/lnd_routing_test.go +++ b/itest/lnd_routing_test.go @@ -3,6 +3,7 @@ package itest import ( "encoding/hex" "fmt" + "math" "testing" "time" @@ -746,6 +747,194 @@ func testInvoiceRoutingHints(ht *lntest.HarnessTest) { ht.ForceCloseChannel(alice, chanPointEve) } +// testScidAliasRoutingHints tests that dynamically created aliases via the RPC +// are properly used when routing. +func testScidAliasRoutingHints(ht *lntest.HarnessTest) { + const chanAmt = btcutil.Amount(800000) + + // Option-scid-alias is opt-in, as is anchors. + scidAliasArgs := []string{ + "--protocol.option-scid-alias", + "--protocol.anchors", + } + + // We'll have a network Bob -> Carol -> Dave in the end, with both Carol + // and Dave having an alias for the channel between them. + carol := ht.NewNode("Carol", scidAliasArgs) + dave := ht.NewNode("Dave", scidAliasArgs) + + ht.FundCoins(btcutil.SatoshiPerBitcoin, carol) + ht.FundCoins(btcutil.SatoshiPerBitcoin, dave) + + ht.ConnectNodes(carol, dave) + + // Create a channel between Carol and Dave, which uses the scid alias + // feature. + chanPointCD := ht.OpenChannel(carol, dave, lntest.OpenChannelParams{ + Amt: chanAmt, + PushAmt: chanAmt / 2, + ScidAlias: true, + Private: true, + }) + + // Find the channel ID of the channel between Carol and Dave. + carolDaveChan := ht.QueryChannelByChanPoint(carol, chanPointCD) + + // Make sure we can't add an alias that's not actually in the alias + // range (which is the case with the base SCID). + err := carol.RPC.XAddLocalChanAliasesErr(&routerrpc.AddAliasesRequest{ + AliasMaps: []*lnrpc.AliasMap{ + { + BaseScid: carolDaveChan.ChanId, + Aliases: []uint64{ + carolDaveChan.ChanId, + }, + }, + }, + }) + require.ErrorContains(ht, err, routerrpc.ErrNoValidAlias.Error()) + + // Create an ephemeral alias that will be used as a routing hint. + ephemeralChanPoint := lnwire.ShortChannelID{ + BlockHeight: 16_100_000, + TxIndex: 1, + TxPosition: 1, + } + ephemeralAlias := ephemeralChanPoint.ToUint64() + ephemeralAliasMap := []*lnrpc.AliasMap{ + { + BaseScid: carolDaveChan.ChanId, + Aliases: []uint64{ + ephemeralAlias, + }, + }, + } + + // Add the alias to Carol. + carol.RPC.XAddLocalChanAliases(&routerrpc.AddAliasesRequest{ + AliasMaps: ephemeralAliasMap, + }) + + // We shouldn't be able to add the same alias again. + err = carol.RPC.XAddLocalChanAliasesErr(&routerrpc.AddAliasesRequest{ + AliasMaps: ephemeralAliasMap, + }) + require.ErrorContains(ht, err, routerrpc.ErrAliasAlreadyExists.Error()) + + // Add the alias to Dave. This isn't strictly needed for the test, as + // the payment will go from Bob -> Carol -> Dave, so only Carol needs + // to know about the alias. So we'll later on remove it again to + // demonstrate that. + dave.RPC.XAddLocalChanAliases(&routerrpc.AddAliasesRequest{ + AliasMaps: ephemeralAliasMap, + }) + + carolChans := carol.RPC.ListChannels(&lnrpc.ListChannelsRequest{}) + require.Len(ht, carolChans.Channels, 1, "expected one channel") + + // Get the alias scids for Carol's channel. + aliases := carolChans.Channels[0].AliasScids + + // There should be two aliases. + require.Len(ht, aliases, 2, "expected two aliases") + + // The ephemeral alias should be included. + require.Contains( + ht, aliases, ephemeralAlias, "expected ephemeral alias", + ) + + // List Dave's Channels. + daveChans := dave.RPC.ListChannels(&lnrpc.ListChannelsRequest{}) + + require.Len(ht, daveChans.Channels, 1, "expected one channel") + + // Get the alias scids for his channel. + aliases = daveChans.Channels[0].AliasScids + + // There should be two aliases. + require.Len(ht, aliases, 2, "expected two aliases") + + // The ephemeral alias should be included. + require.Contains( + ht, aliases, ephemeralAlias, "expected ephemeral alias", + ) + + // Now that we've asserted that the alias is properly set up, we'll + // delete the one for Dave again. The payment should still succeed. + dave.RPC.XDeleteLocalChanAliases(&routerrpc.DeleteAliasesRequest{ + AliasMaps: ephemeralAliasMap, + }) + + // Connect the existing Bob node with Carol via a public channel. + ht.ConnectNodes(ht.Bob, carol) + chanPointBC := ht.OpenChannel(ht.Bob, carol, lntest.OpenChannelParams{ + Amt: chanAmt, + PushAmt: chanAmt / 2, + }) + + // Create the hop hint that Dave will use to craft his invoice. The + // goal here is to define only the ephemeral alias as a hop hint. + hopHint := &lnrpc.HopHint{ + NodeId: carol.PubKeyStr, + ChanId: ephemeralAlias, + FeeBaseMsat: uint32( + chainreg.DefaultBitcoinBaseFeeMSat, + ), + FeeProportionalMillionths: uint32( + chainreg.DefaultBitcoinFeeRate, + ), + CltvExpiryDelta: chainreg.DefaultBitcoinTimeLockDelta, + } + + // Define the invoice that Dave will add to his node. + invoice := &lnrpc.Invoice{ + Memo: "dynamic alias", + Value: int64(chanAmt / 4), + RouteHints: []*lnrpc.RouteHint{ + { + HopHints: []*lnrpc.HopHint{hopHint}, + }, + }, + } + + // Add the invoice and retrieve the payment request. + payReq := dave.RPC.AddInvoice(invoice).PaymentRequest + + // Now Alice will try to pay to that payment request. + timeout := time.Second * 15 + stream := ht.Bob.RPC.SendPayment(&routerrpc.SendPaymentRequest{ + PaymentRequest: payReq, + TimeoutSeconds: int32(timeout.Seconds()), + FeeLimitSat: math.MaxInt64, + }) + + // Payment should eventually succeed. + ht.AssertPaymentSucceedWithTimeout(stream, timeout) + + // Check that Dave's invoice appears as settled. + invoices := dave.RPC.ListInvoices(&lnrpc.ListInvoiceRequest{}) + require.Len(ht, invoices.Invoices, 1, "expected one invoice") + require.Equal(ht, invoices.Invoices[0].State, lnrpc.Invoice_SETTLED, + "expected settled invoice") + + // We'll now delete the alias again, but only on Carol's end. That + // should be enough to make the payment fail, since she doesn't know + // about the alias in the hop hint anymore. + carol.RPC.XDeleteLocalChanAliases(&routerrpc.DeleteAliasesRequest{ + AliasMaps: ephemeralAliasMap, + }) + payReq2 := dave.RPC.AddInvoice(invoice).PaymentRequest + stream2 := ht.Bob.RPC.SendPayment(&routerrpc.SendPaymentRequest{ + PaymentRequest: payReq2, + TimeoutSeconds: int32(timeout.Seconds()), + FeeLimitSat: math.MaxInt64, + }) + ht.AssertPaymentStatusFromStream(stream2, lnrpc.Payment_FAILED) + + ht.CloseChannel(carol, chanPointCD) + ht.CloseChannel(ht.Bob, chanPointBC) +} + // testMultiHopOverPrivateChannels tests that private channels can be used as // intermediate hops in a route for payments. func testMultiHopOverPrivateChannels(ht *lntest.HarnessTest) { diff --git a/lntest/rpc/harness_rpc.go b/lntest/rpc/harness_rpc.go index 0e8256ce11..0640581dcd 100644 --- a/lntest/rpc/harness_rpc.go +++ b/lntest/rpc/harness_rpc.go @@ -7,6 +7,7 @@ import ( "github.com/btcsuite/btcd/wire" "github.com/lightningnetwork/lnd/lnrpc" "github.com/lightningnetwork/lnd/lnrpc/chainrpc" + "github.com/lightningnetwork/lnd/lnrpc/devrpc" "github.com/lightningnetwork/lnd/lnrpc/invoicesrpc" "github.com/lightningnetwork/lnd/lnrpc/neutrinorpc" "github.com/lightningnetwork/lnd/lnrpc/peersrpc" @@ -42,6 +43,7 @@ type HarnessRPC struct { ChainKit chainrpc.ChainKitClient NeutrinoKit neutrinorpc.NeutrinoKitClient Peer peersrpc.PeersClient + DevRPC devrpc.DevClient // Name is the HarnessNode's name. Name string @@ -73,6 +75,7 @@ func NewHarnessRPC(ctxt context.Context, t *testing.T, c *grpc.ClientConn, ChainKit: chainrpc.NewChainKitClient(c), NeutrinoKit: neutrinorpc.NewNeutrinoKitClient(c), Peer: peersrpc.NewPeersClient(c), + DevRPC: devrpc.NewDevClient(c), Name: name, } diff --git a/lntest/rpc/router.go b/lntest/rpc/router.go index 8bb8a92e39..7c3f2e7c46 100644 --- a/lntest/rpc/router.go +++ b/lntest/rpc/router.go @@ -208,6 +208,54 @@ func (h *HarnessRPC) HtlcInterceptor() (InterceptorClient, context.CancelFunc) { return resp, cancel } +// XAddLocalChanAliases adds a list of aliases to the node's alias map. +func (h *HarnessRPC) XAddLocalChanAliases(req *routerrpc.AddAliasesRequest) { + ctxt, cancel := context.WithTimeout(h.runCtx, DefaultTimeout) + defer cancel() + + _, err := h.Router.XAddLocalChanAliases(ctxt, req) + h.NoError(err, "XAddLocalChanAliases") +} + +// XAddLocalChanAliasesErr adds a list of aliases to the node's alias map and +// expects an error. +func (h *HarnessRPC) XAddLocalChanAliasesErr( + req *routerrpc.AddAliasesRequest) error { + + ctxt, cancel := context.WithTimeout(h.runCtx, DefaultTimeout) + defer cancel() + + _, err := h.Router.XAddLocalChanAliases(ctxt, req) + require.Error(h, err) + + return err +} + +// XDeleteLocalChanAliases deleted a set of alias mappings. +func (h *HarnessRPC) XDeleteLocalChanAliases( + req *routerrpc.DeleteAliasesRequest) { + + ctxt, cancel := context.WithTimeout(h.runCtx, DefaultTimeout) + defer cancel() + + _, err := h.Router.XDeleteLocalChanAliases(ctxt, req) + h.NoError(err, "XDeleteLocalChanAliases") +} + +// XDeleteLocalChanAliasesErr deleted a set of alias mappings and expects an +// error. +func (h *HarnessRPC) XDeleteLocalChanAliasesErr( + req *routerrpc.DeleteAliasesRequest) error { + + ctxt, cancel := context.WithTimeout(h.runCtx, DefaultTimeout) + defer cancel() + + _, err := h.Router.XDeleteLocalChanAliases(ctxt, req) + require.Error(h, err) + + return err +} + type TrackPaymentsClient routerrpc.Router_TrackPaymentsClient // TrackPayments makes a RPC call to the node's RouterClient and asserts. From b07e857aef09ce3fbae3f5dc1e8ca5a0b6868458 Mon Sep 17 00:00:00 2001 From: ffranr Date: Fri, 3 May 2024 15:24:42 +0100 Subject: [PATCH 058/218] lnwire: add unit tests for `ExtraOpaqueData.PackRecords` --- lnwire/extra_bytes_test.go | 68 +++++++++++++++++++++++++++++++++++--- 1 file changed, 64 insertions(+), 4 deletions(-) diff --git a/lnwire/extra_bytes_test.go b/lnwire/extra_bytes_test.go index 97a908bcec..1ecc7adf5f 100644 --- a/lnwire/extra_bytes_test.go +++ b/lnwire/extra_bytes_test.go @@ -71,10 +71,7 @@ func TestExtraOpaqueDataEncodeDecode(t *testing.T) { newTestCase.inputBytes = make([]byte, numBytes) _, err := r.Read(newTestCase.inputBytes) - if err != nil { - t.Fatalf("unable to gen random bytes: %v", err) - return - } + require.NoError(t, err) } v[0] = reflect.ValueOf(newTestCase) @@ -143,3 +140,66 @@ func TestExtraOpaqueDataPackUnpackRecords(t *testing.T) { t.Fatalf("type2 not found in typeMap") } } + +// TestPackRecords tests that we're able to pack a set of records into an +// ExtraOpaqueData instance, and then extract them back out. Crucially, we'll +// ensure that records can be packed in any order, and we'll ensure that the +// unpacked records are valid. +func TestPackRecords(t *testing.T) { + t.Parallel() + + // Create an empty ExtraOpaqueData instance. + extraBytes := ExtraOpaqueData{} + + var ( + // Record type 1. + tlvType1 tlv.TlvType1 + recordBytes1 = []byte("recordBytes1") + tlvRecord1 = tlv.NewPrimitiveRecord[tlv.TlvType1]( + recordBytes1, + ) + + // Record type 2. + tlvType2 tlv.TlvType2 + recordBytes2 = []byte("recordBytes2") + tlvRecord2 = tlv.NewPrimitiveRecord[tlv.TlvType2]( + recordBytes2, + ) + + // Record type 3. + tlvType3 tlv.TlvType3 + recordBytes3 = []byte("recordBytes3") + tlvRecord3 = tlv.NewPrimitiveRecord[tlv.TlvType3]( + recordBytes3, + ) + ) + + // Pack records 1 and 2 into the ExtraOpaqueData instance. + err := extraBytes.PackRecords( + []tlv.RecordProducer{&tlvRecord1, &tlvRecord2}..., + ) + require.NoError(t, err) + + // Examine the records that were packed into the ExtraOpaqueData. + extractedRecords, err := extraBytes.ExtractRecords() + require.NoError(t, err) + + require.Equal(t, 2, len(extractedRecords)) + require.Equal(t, recordBytes1, extractedRecords[tlvType1.TypeVal()]) + require.Equal(t, recordBytes2, extractedRecords[tlvType2.TypeVal()]) + + // Pack records 1, 2, and 3 into the ExtraOpaqueData instance. + err = extraBytes.PackRecords( + []tlv.RecordProducer{&tlvRecord3, &tlvRecord1, &tlvRecord2}..., + ) + require.NoError(t, err) + + // Examine the records that were packed into the ExtraOpaqueData. + extractedRecords, err = extraBytes.ExtractRecords() + require.NoError(t, err) + + require.Equal(t, 3, len(extractedRecords)) + require.Equal(t, recordBytes1, extractedRecords[tlvType1.TypeVal()]) + require.Equal(t, recordBytes2, extractedRecords[tlvType2.TypeVal()]) + require.Equal(t, recordBytes3, extractedRecords[tlvType3.TypeVal()]) +} From ed69bb8757f32ef760263d5b28effc89fc17ea67 Mon Sep 17 00:00:00 2001 From: ffranr Date: Fri, 3 May 2024 18:36:00 +0100 Subject: [PATCH 059/218] lnwire: add `ExtraOpaqueData` helper functions and methods Introduces a couple of new helper functions for both the ExtraOpaqueData and CustomRecords types along with new methods on the ExtraOpaqueData. --- lnwire/custom_records.go | 143 +++++++++++++--- lnwire/custom_records_test.go | 6 +- lnwire/extra_bytes.go | 197 +++++++++++++++++---- lnwire/extra_bytes_test.go | 314 +++++++++++++++++++++++++++++++++- 4 files changed, 590 insertions(+), 70 deletions(-) diff --git a/lnwire/custom_records.go b/lnwire/custom_records.go index 1e19888426..f0f59185e9 100644 --- a/lnwire/custom_records.go +++ b/lnwire/custom_records.go @@ -3,8 +3,10 @@ package lnwire import ( "bytes" "fmt" + "io" "sort" + "github.com/lightningnetwork/lnd/fn" "github.com/lightningnetwork/lnd/tlv" ) @@ -44,12 +46,12 @@ func NewCustomRecords(tlvMap tlv.TypeMap) (CustomRecords, error) { // ParseCustomRecords creates a new CustomRecords instance from a tlv.Blob. func ParseCustomRecords(b tlv.Blob) (CustomRecords, error) { - stream, err := tlv.NewStream() - if err != nil { - return nil, fmt.Errorf("error creating stream: %w", err) - } + return ParseCustomRecordsFrom(bytes.NewReader(b)) +} - typeMap, err := stream.DecodeWithParsedTypes(bytes.NewReader(b)) +// ParseCustomRecordsFrom creates a new CustomRecords instance from a reader. +func ParseCustomRecordsFrom(r io.Reader) (CustomRecords, error) { + typeMap, err := DecodeRecords(r) if err != nil { return nil, fmt.Errorf("error decoding HTLC record: %w", err) } @@ -121,21 +123,14 @@ func (c CustomRecords) ExtendRecordProducers( // Convert the custom records map to a TLV record producer slice and // append them to the exiting records slice. - crRecords := tlv.MapToRecords(c) - for _, record := range crRecords { - r := recordProducer{record} - producers = append(producers, &r) - } + customRecordProducers := RecordsAsProducers(tlv.MapToRecords(c)) + producers = append(producers, customRecordProducers...) // If the records slice which was given as an argument included TLV // values greater than or equal to the minimum custom records TLV type // we will sort the extended records slice to ensure that it is ordered // correctly. - sort.Slice(producers, func(i, j int) bool { - recordI := producers[i].Record() - recordJ := producers[j].Record() - return recordI.Type() < recordJ.Type() - }) + SortProducers(producers) return producers, nil } @@ -150,27 +145,119 @@ func (c CustomRecords) RecordProducers() []tlv.RecordProducer { // Convert the custom records map to a TLV record producer slice. records := tlv.MapToRecords(c) - // Convert the records to record producers. - producers := make([]tlv.RecordProducer, len(records)) - for i, record := range records { - producers[i] = &recordProducer{record} - } - - return producers + return RecordsAsProducers(records) } // Serialize serializes the custom records into a byte slice. func (c CustomRecords) Serialize() ([]byte, error) { records := tlv.MapToRecords(c) - stream, err := tlv.NewStream(records...) + return EncodeRecords(records) +} + +// SerializeTo serializes the custom records into the given writer. +func (c CustomRecords) SerializeTo(w io.Writer) error { + records := tlv.MapToRecords(c) + return EncodeRecordsTo(w, records) +} + +// ProduceRecordsSorted converts a slice of record producers into a slice of +// records and then sorts it by type. +func ProduceRecordsSorted(recordProducers ...tlv.RecordProducer) []tlv.Record { + records := fn.Map(func(producer tlv.RecordProducer) tlv.Record { + return producer.Record() + }, recordProducers) + + // Ensure that the set of records are sorted before we attempt to + // decode from the stream, to ensure they're canonical. + tlv.SortRecords(records) + + return records +} + +// SortProducers sorts the given record producers by their type. +func SortProducers(producers []tlv.RecordProducer) { + sort.Slice(producers, func(i, j int) bool { + recordI := producers[i].Record() + recordJ := producers[j].Record() + return recordI.Type() < recordJ.Type() + }) +} + +// TlvMapToRecords converts a TLV map into a slice of records. +func TlvMapToRecords(tlvMap tlv.TypeMap) []tlv.Record { + tlvMapGeneric := make(map[uint64][]byte) + for k, v := range tlvMap { + tlvMapGeneric[uint64(k)] = v + } + + return tlv.MapToRecords(tlvMapGeneric) +} + +// RecordsAsProducers converts a slice of records into a slice of record +// producers. +func RecordsAsProducers(records []tlv.Record) []tlv.RecordProducer { + return fn.Map(func(record tlv.Record) tlv.RecordProducer { + return &record + }, records) +} + +// EncodeRecords encodes the given records into a byte slice. +func EncodeRecords(records []tlv.Record) ([]byte, error) { + var buf bytes.Buffer + if err := EncodeRecordsTo(&buf, records); err != nil { + return nil, err + } + + return buf.Bytes(), nil +} + +// EncodeRecordsTo encodes the given records into the given writer. +func EncodeRecordsTo(w io.Writer, records []tlv.Record) error { + tlvStream, err := tlv.NewStream(records...) + if err != nil { + return err + } + + return tlvStream.Encode(w) +} + +// DecodeRecords decodes the given byte slice into the given records and returns +// the rest as a TLV type map. +func DecodeRecords(r io.Reader, + records ...tlv.Record) (tlv.TypeMap, error) { + + tlvStream, err := tlv.NewStream(records...) + if err != nil { + return nil, err + } + + return tlvStream.DecodeWithParsedTypes(r) +} + +// DecodeRecordsP2P decodes the given byte slice into the given records and +// returns the rest as a TLV type map. This function is identical to +// DecodeRecords except that the record size is capped at 65535. +func DecodeRecordsP2P(r *bytes.Reader, + records ...tlv.Record) (tlv.TypeMap, error) { + + tlvStream, err := tlv.NewStream(records...) if err != nil { - return nil, fmt.Errorf("error creating stream: %w", err) + return nil, err } - var b bytes.Buffer - if err := stream.Encode(&b); err != nil { - return nil, fmt.Errorf("error encoding custom records: %w", err) + return tlvStream.DecodeWithParsedTypesP2P(r) +} + +// AssertUniqueTypes asserts that the given records have unique types. +func AssertUniqueTypes(r []tlv.Record) error { + seen := make(fn.Set[tlv.Type], len(r)) + for _, record := range r { + t := record.Type() + if seen.Contains(t) { + return fmt.Errorf("duplicate record type: %d", t) + } + seen.Add(t) } - return b.Bytes(), nil + return nil } diff --git a/lnwire/custom_records_test.go b/lnwire/custom_records_test.go index 0338d0159c..1d30e21000 100644 --- a/lnwire/custom_records_test.go +++ b/lnwire/custom_records_test.go @@ -144,10 +144,8 @@ func TestCustomRecordsExtendRecordProducers(t *testing.T) { for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { nonCustomRecords := tlv.MapToRecords(tc.existingTypes) - nonCustomProducers := fn.Map( - func(r tlv.Record) tlv.RecordProducer { - return &recordProducer{r} - }, nonCustomRecords, + nonCustomProducers := RecordsAsProducers( + nonCustomRecords, ) combined, err := tc.customRecords.ExtendRecordProducers( diff --git a/lnwire/extra_bytes.go b/lnwire/extra_bytes.go index 0ebf48f57d..c4ca260e1e 100644 --- a/lnwire/extra_bytes.go +++ b/lnwire/extra_bytes.go @@ -5,6 +5,7 @@ import ( "fmt" "io" + "github.com/lightningnetwork/lnd/fn" "github.com/lightningnetwork/lnd/tlv" ) @@ -15,6 +16,21 @@ import ( // upgrades to the network in a forwards compatible manner. type ExtraOpaqueData []byte +// NewExtraOpaqueData creates a new ExtraOpaqueData instance from a tlv.TypeMap. +func NewExtraOpaqueData(tlvMap tlv.TypeMap) (ExtraOpaqueData, error) { + // If the tlv map is empty, we'll want to mirror the behavior of + // decoding an empty extra opaque data field (see Decode method). + if len(tlvMap) == 0 { + return make([]byte, 0), nil + } + + // Convert the TLV map into a slice of records. + records := TlvMapToRecords(tlvMap) + + // Encode the records into the extra data byte slice. + return EncodeRecords(records) +} + // Encode attempts to encode the raw extra bytes into the passed io.Writer. func (e *ExtraOpaqueData) Encode(w *bytes.Buffer) error { eBytes := []byte((*e)[:]) @@ -25,8 +41,8 @@ func (e *ExtraOpaqueData) Encode(w *bytes.Buffer) error { return nil } -// Decode attempts to unpack the raw bytes encoded in the passed io.Reader as a -// set of extra opaque data. +// Decode attempts to unpack the raw bytes encoded in the passed-in io.Reader as +// a set of extra opaque data. func (e *ExtraOpaqueData) Decode(r io.Reader) error { // First, we'll attempt to read a set of bytes contained within the // passed io.Reader (if any exist). @@ -39,7 +55,7 @@ func (e *ExtraOpaqueData) Decode(r io.Reader) error { // This ensures that any struct that embeds this type will properly // store the bytes once this method exits. if len(rawBytes) > 0 { - *e = ExtraOpaqueData(rawBytes) + *e = rawBytes } else { *e = make([]byte, 0) } @@ -50,28 +66,17 @@ func (e *ExtraOpaqueData) Decode(r io.Reader) error { // PackRecords attempts to encode the set of tlv records into the target // ExtraOpaqueData instance. The records will be encoded as a raw TLV stream // and stored within the backing slice pointer. -func (e *ExtraOpaqueData) PackRecords(recordProducers ...tlv.RecordProducer) error { - // First, assemble all the records passed in in series. - records := make([]tlv.Record, 0, len(recordProducers)) - for _, producer := range recordProducers { - records = append(records, producer.Record()) - } - - // Ensure that the set of records are sorted before we encode them into - // the stream, to ensure they're canonical. - tlv.SortRecords(records) +func (e *ExtraOpaqueData) PackRecords( + recordProducers ...tlv.RecordProducer) error { - tlvStream, err := tlv.NewStream(records...) + // Assemble all the records passed in series, then encode them. + records := ProduceRecordsSorted(recordProducers...) + encoded, err := EncodeRecords(records) if err != nil { return err } - var extraBytesWriter bytes.Buffer - if err := tlvStream.Encode(&extraBytesWriter); err != nil { - return err - } - - *e = ExtraOpaqueData(extraBytesWriter.Bytes()) + *e = encoded return nil } @@ -80,29 +85,38 @@ func (e *ExtraOpaqueData) PackRecords(recordProducers ...tlv.RecordProducer) err // it were a tlv stream. The set of raw parsed types is returned, and any // passed records (if found in the stream) will be parsed into the proper // tlv.Record. -func (e *ExtraOpaqueData) ExtractRecords(recordProducers ...tlv.RecordProducer) ( - tlv.TypeMap, error) { +func (e *ExtraOpaqueData) ExtractRecords( + recordProducers ...tlv.RecordProducer) (tlv.TypeMap, error) { - // First, assemble all the records passed in in series. - records := make([]tlv.Record, 0, len(recordProducers)) - for _, producer := range recordProducers { - records = append(records, producer.Record()) - } + // First, assemble all the records passed in series. + records := ProduceRecordsSorted(recordProducers...) + extraBytesReader := bytes.NewReader(*e) - // Ensure that the set of records are sorted before we attempt to - // decode from the stream, to ensure they're canonical. - tlv.SortRecords(records) + // Since ExtraOpaqueData is provided by a potentially malicious peer, + // pass it into the P2P decoding variant. + return DecodeRecordsP2P(extraBytesReader, records...) +} - extraBytesReader := bytes.NewReader(*e) +// RecordProducers parses ExtraOpaqueData into a slice of TLV record producers +// by interpreting it as a TLV map. +func (e *ExtraOpaqueData) RecordProducers() ([]tlv.RecordProducer, error) { + var recordProducers []tlv.RecordProducer - tlvStream, err := tlv.NewStream(records...) + // If the instance is nil or empty, return an empty slice. + if e == nil || len(*e) == 0 { + return recordProducers, nil + } + + // Parse the extra opaque data as a TLV map. + tlvMap, err := e.ExtractRecords() if err != nil { return nil, err } - // Since ExtraOpaqueData is provided by a potentially malicious peer, - // pass it into the P2P decoding variant. - return tlvStream.DecodeWithParsedTypesP2P(extraBytesReader) + // Convert the TLV map into a slice of record producers. + records := TlvMapToRecords(tlvMap) + + return RecordsAsProducers(records), nil } // EncodeMessageExtraData encodes the given recordProducers into the given @@ -120,3 +134,116 @@ func EncodeMessageExtraData(extraData *ExtraOpaqueData, // are all properly sorted. return extraData.PackRecords(recordProducers...) } + +// ParseAndExtractCustomRecords parses the given extra data into the passed-in +// records, then returns any remaining records split into custom records and +// extra data. +func ParseAndExtractCustomRecords(allExtraData ExtraOpaqueData, + knownRecords ...tlv.RecordProducer) (CustomRecords, + fn.Set[tlv.Type], ExtraOpaqueData, error) { + + extraDataTlvMap, err := allExtraData.ExtractRecords(knownRecords...) + if err != nil { + return nil, nil, nil, err + } + + // Remove the known and now extracted records from the leftover extra + // data map. + parsedKnownRecords := make(fn.Set[tlv.Type], len(knownRecords)) + for _, producer := range knownRecords { + r := producer.Record() + + // Only remove the records if it was parsed (remainder is nil). + // We'll just store the type so we can tell the caller which + // records were actually parsed fully. + val, ok := extraDataTlvMap[r.Type()] + if ok && val == nil { + parsedKnownRecords.Add(r.Type()) + delete(extraDataTlvMap, r.Type()) + } + } + + // Any records from the extra data TLV map which are in the custom + // records TLV type range will be included in the custom records field + // and removed from the extra data field. + customRecordsTlvMap := make(tlv.TypeMap, len(extraDataTlvMap)) + for k, v := range extraDataTlvMap { + // Skip records that are not in the custom records TLV type + // range. + if k < MinCustomRecordsTlvType { + continue + } + + // Include the record in the custom records map. + customRecordsTlvMap[k] = v + + // Now that the record is included in the custom records map, + // we can remove it from the extra data TLV map. + delete(extraDataTlvMap, k) + } + + // Set the custom records field to the custom records specific TLV + // record map. + customRecords, err := NewCustomRecords(customRecordsTlvMap) + if err != nil { + return nil, nil, nil, err + } + + // Encode the remaining records back into the extra data field. These + // records are not in the custom records TLV type range and do not + // have associated fields in the struct that produced the records. + extraData, err := NewExtraOpaqueData(extraDataTlvMap) + if err != nil { + return nil, nil, nil, err + } + + // Help with unit testing where we might have the empty value (nil) for + // the extra data instead of the default that's returned by the + // constructor (empty slice). + if len(extraData) == 0 { + extraData = nil + } + + return customRecords, parsedKnownRecords, extraData, nil +} + +// MergeAndEncode merges the known records with the extra data and custom +// records, then encodes the merged records into raw bytes. +func MergeAndEncode(knownRecords []tlv.RecordProducer, + extraData ExtraOpaqueData, customRecords CustomRecords) ([]byte, + error) { + + // Construct a slice of all the records that we should include in the + // message extra data field. We will start by including any records from + // the extra data field. + mergedRecords, err := extraData.RecordProducers() + if err != nil { + return nil, err + } + + // Merge the known and extra data records. + mergedRecords = append(mergedRecords, knownRecords...) + + // Include custom records in the extra data wire field if they are + // present. Ensure that the custom records are validated before encoding + // them. + if err := customRecords.Validate(); err != nil { + return nil, fmt.Errorf("custom records validation error: %w", + err) + } + + // Extend the message extra data records slice with TLV records from the + // custom records field. + mergedRecords = append( + mergedRecords, customRecords.RecordProducers()..., + ) + + // Now we can sort the records and make sure there are no records with + // the same type that would collide when encoding. + sortedRecords := ProduceRecordsSorted(mergedRecords...) + if err := AssertUniqueTypes(sortedRecords); err != nil { + return nil, err + } + + return EncodeRecords(sortedRecords) +} diff --git a/lnwire/extra_bytes_test.go b/lnwire/extra_bytes_test.go index 1ecc7adf5f..79c913362e 100644 --- a/lnwire/extra_bytes_test.go +++ b/lnwire/extra_bytes_test.go @@ -11,6 +11,12 @@ import ( "github.com/stretchr/testify/require" ) +var ( + tlvType1 tlv.TlvType1 + tlvType2 tlv.TlvType2 + tlvType3 tlv.TlvType3 +) + // TestExtraOpaqueDataEncodeDecode tests that we're able to encode/decode // arbitrary payloads. func TestExtraOpaqueDataEncodeDecode(t *testing.T) { @@ -153,21 +159,18 @@ func TestPackRecords(t *testing.T) { var ( // Record type 1. - tlvType1 tlv.TlvType1 recordBytes1 = []byte("recordBytes1") tlvRecord1 = tlv.NewPrimitiveRecord[tlv.TlvType1]( recordBytes1, ) // Record type 2. - tlvType2 tlv.TlvType2 recordBytes2 = []byte("recordBytes2") tlvRecord2 = tlv.NewPrimitiveRecord[tlv.TlvType2]( recordBytes2, ) // Record type 3. - tlvType3 tlv.TlvType3 recordBytes3 = []byte("recordBytes3") tlvRecord3 = tlv.NewPrimitiveRecord[tlv.TlvType3]( recordBytes3, @@ -203,3 +206,308 @@ func TestPackRecords(t *testing.T) { require.Equal(t, recordBytes2, extractedRecords[tlvType2.TypeVal()]) require.Equal(t, recordBytes3, extractedRecords[tlvType3.TypeVal()]) } + +type dummyRecordProducer struct { + typ tlv.Type + scratchValue []byte + expectedValue []byte +} + +func (d *dummyRecordProducer) Record() tlv.Record { + return tlv.MakePrimitiveRecord(d.typ, &d.scratchValue) +} + +// TestExtraOpaqueData tests that we're able to properly encode/decode an +// ExtraOpaqueData instance. +func TestExtraOpaqueData(t *testing.T) { + t.Parallel() + + testCases := []struct { + name string + types tlv.TypeMap + expectedData ExtraOpaqueData + expectedTypes tlv.TypeMap + decoders []tlv.RecordProducer + }{ + { + name: "empty map", + expectedTypes: tlv.TypeMap{}, + expectedData: make([]byte, 0), + }, + { + name: "single record", + types: tlv.TypeMap{ + tlvType1.TypeVal(): []byte{1, 2, 3}, + }, + expectedData: ExtraOpaqueData{ + 0x01, 0x03, 1, 2, 3, + }, + expectedTypes: tlv.TypeMap{ + tlvType1.TypeVal(): []byte{1, 2, 3}, + }, + decoders: []tlv.RecordProducer{ + &dummyRecordProducer{ + typ: tlvType1.TypeVal(), + expectedValue: []byte{1, 2, 3}, + }, + }, + }, + { + name: "multiple records", + types: tlv.TypeMap{ + tlvType2.TypeVal(): []byte{4, 5, 6}, + tlvType1.TypeVal(): []byte{1, 2, 3}, + }, + expectedData: ExtraOpaqueData{ + 0x01, 0x03, 1, 2, 3, + 0x02, 0x03, 4, 5, 6, + }, + expectedTypes: tlv.TypeMap{ + tlvType1.TypeVal(): []byte{1, 2, 3}, + tlvType2.TypeVal(): []byte{4, 5, 6}, + }, + decoders: []tlv.RecordProducer{ + &dummyRecordProducer{ + typ: tlvType1.TypeVal(), + expectedValue: []byte{1, 2, 3}, + }, + &dummyRecordProducer{ + typ: tlvType2.TypeVal(), + expectedValue: []byte{4, 5, 6}, + }, + }, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + // First, test the constructor. + opaqueData, err := NewExtraOpaqueData(tc.types) + require.NoError(t, err) + + require.Equal(t, tc.expectedData, opaqueData) + + // Now encode/decode. + var b bytes.Buffer + err = opaqueData.Encode(&b) + require.NoError(t, err) + + var decoded ExtraOpaqueData + err = decoded.Decode(&b) + require.NoError(t, err) + + require.Equal(t, opaqueData, decoded) + + // Now RecordProducers/PackRecords. + producers, err := opaqueData.RecordProducers() + require.NoError(t, err) + + var packed ExtraOpaqueData + err = packed.PackRecords(producers...) + require.NoError(t, err) + + // PackRecords returns nil vs. an empty slice if there + // are no records. We need to handle this case + // separately. + if len(producers) == 0 { + // Make sure the packed data is empty. + require.Empty(t, packed) + + // Now change it to an empty slice for the + // comparison below. + packed = make([]byte, 0) + } + require.Equal(t, opaqueData, packed) + + // ExtractRecords with an empty set of record producers + // should return the original type map. + extracted, err := opaqueData.ExtractRecords() + require.NoError(t, err) + + require.Equal(t, tc.expectedTypes, extracted) + + if len(tc.decoders) == 0 { + return + } + + // ExtractRecords with a set of record producers should + // only return the types that weren't in the passed-in + // set of producers. + extracted, err = opaqueData.ExtractRecords( + tc.decoders..., + ) + require.NoError(t, err) + + for parsedType := range tc.expectedTypes { + remainder, ok := extracted[parsedType] + require.True(t, ok) + require.Nil(t, remainder) + } + + for _, dec := range tc.decoders { + //nolint:forcetypeassert + dec := dec.(*dummyRecordProducer) + require.Equal( + t, dec.expectedValue, dec.scratchValue, + ) + } + }) + } +} + +// TestExtractAndMerge tests that the ParseAndExtractCustomRecords and +// MergeAndEncode functions work as expected. +func TestExtractAndMerge(t *testing.T) { + t.Parallel() + + testCases := []struct { + name string + knownRecords []tlv.RecordProducer + extraData ExtraOpaqueData + customRecords CustomRecords + expectedErr string + expectEncoded []byte + }{ + { + name: "invalid custom record", + customRecords: CustomRecords{ + 123: []byte("invalid"), + }, + expectedErr: "custom records validation error", + }, + { + name: "empty everything", + }, + { + name: "just extra data", + extraData: ExtraOpaqueData{ + 0x01, 0x03, 1, 2, 3, + 0x02, 0x03, 4, 5, 6, + }, + expectEncoded: []byte{ + 0x01, 0x03, 1, 2, 3, + 0x02, 0x03, 4, 5, 6, + }, + }, + { + name: "extra data with known record", + extraData: ExtraOpaqueData{ + 0x04, 0x03, 4, 4, 4, + 0x05, 0x03, 5, 5, 5, + }, + knownRecords: []tlv.RecordProducer{ + &dummyRecordProducer{ + typ: tlvType1.TypeVal(), + scratchValue: []byte{1, 2, 3}, + expectedValue: []byte{1, 2, 3}, + }, + &dummyRecordProducer{ + typ: tlvType2.TypeVal(), + scratchValue: []byte{4, 5, 6}, + expectedValue: []byte{4, 5, 6}, + }, + }, + expectEncoded: []byte{ + 0x01, 0x03, 1, 2, 3, + 0x02, 0x03, 4, 5, 6, + 0x04, 0x03, 4, 4, 4, + 0x05, 0x03, 5, 5, 5, + }, + }, + { + name: "extra data and custom records with known record", + extraData: ExtraOpaqueData{ + 0x04, 0x03, 4, 4, 4, + 0x05, 0x03, 5, 5, 5, + }, + customRecords: CustomRecords{ + MinCustomRecordsTlvType + 1: []byte{99, 99, 99}, + }, + knownRecords: []tlv.RecordProducer{ + &dummyRecordProducer{ + typ: tlvType1.TypeVal(), + scratchValue: []byte{1, 2, 3}, + expectedValue: []byte{1, 2, 3}, + }, + &dummyRecordProducer{ + typ: tlvType2.TypeVal(), + scratchValue: []byte{4, 5, 6}, + expectedValue: []byte{4, 5, 6}, + }, + }, + expectEncoded: []byte{ + 0x01, 0x03, 1, 2, 3, + 0x02, 0x03, 4, 5, 6, + 0x04, 0x03, 4, 4, 4, + 0x05, 0x03, 5, 5, 5, + 0xfe, 0x0, 0x1, 0x0, 0x1, 0x3, 0x63, 0x63, 0x63, + }, + }, + { + name: "duplicate records", + extraData: ExtraOpaqueData{ + 0x01, 0x03, 4, 4, 4, + 0x05, 0x03, 5, 5, 5, + }, + customRecords: CustomRecords{ + MinCustomRecordsTlvType + 1: []byte{99, 99, 99}, + }, + knownRecords: []tlv.RecordProducer{ + &dummyRecordProducer{ + typ: tlvType1.TypeVal(), + scratchValue: []byte{1, 2, 3}, + expectedValue: []byte{1, 2, 3}, + }, + &dummyRecordProducer{ + typ: tlvType2.TypeVal(), + scratchValue: []byte{4, 5, 6}, + expectedValue: []byte{4, 5, 6}, + }, + }, + expectedErr: "duplicate record type: 1", + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + encoded, err := MergeAndEncode( + tc.knownRecords, tc.extraData, tc.customRecords, + ) + + if tc.expectedErr != "" { + require.ErrorContains(t, err, tc.expectedErr) + + return + } + + require.NoError(t, err) + require.Equal(t, tc.expectEncoded, encoded) + + // Clear all the scratch values, to make sure they're + // decoded from the data again. + for _, dec := range tc.knownRecords { + //nolint:forcetypeassert + dec := dec.(*dummyRecordProducer) + dec.scratchValue = nil + } + + pCR, pKR, pED, err := ParseAndExtractCustomRecords( + encoded, tc.knownRecords..., + ) + require.NoError(t, err) + + require.Equal(t, tc.customRecords, pCR) + require.Equal(t, tc.extraData, pED) + + for _, dec := range tc.knownRecords { + //nolint:forcetypeassert + dec := dec.(*dummyRecordProducer) + require.Equal( + t, dec.expectedValue, dec.scratchValue, + ) + + require.Contains(t, pKR, dec.typ) + } + }) + } +} From 2cfa89c719d98048bd8745b7dc54bf0362484dcf Mon Sep 17 00:00:00 2001 From: ffranr Date: Sat, 13 Apr 2024 12:29:41 +0100 Subject: [PATCH 060/218] lnwire: add custom records field to type `UpdateAddHtlc` - Introduce the field `CustomRecords` to the type `UpdateAddHtlc`. - Encode and decode the new field into the `ExtraData` field of the `update_add_htlc` wire message. --- lnwire/lnwire_test.go | 55 ++++++++++ lnwire/update_add_htlc.go | 42 +++++--- lnwire/update_add_htlc_test.go | 188 +++++++++++++++++++++++++++++++++ peer/brontide.go | 6 +- 4 files changed, 272 insertions(+), 19 deletions(-) create mode 100644 lnwire/update_add_htlc_test.go diff --git a/lnwire/lnwire_test.go b/lnwire/lnwire_test.go index 122a996601..e7e765248b 100644 --- a/lnwire/lnwire_test.go +++ b/lnwire/lnwire_test.go @@ -2,6 +2,7 @@ package lnwire import ( "bytes" + crand "crypto/rand" "encoding/binary" "encoding/hex" "fmt" @@ -134,6 +135,27 @@ func randPubKey() (*btcec.PublicKey, error) { return priv.PubKey(), nil } +// pubkeyFromHex parses a Bitcoin public key from a hex encoded string. +func pubkeyFromHex(keyHex string) (*btcec.PublicKey, error) { + pubKeyBytes, err := hex.DecodeString(keyHex) + if err != nil { + return nil, err + } + + return btcec.ParsePubKey(pubKeyBytes) +} + +// generateRandomBytes returns a slice of n random bytes. +func generateRandomBytes(n int) ([]byte, error) { + b := make([]byte, n) + _, err := crand.Read(b) + if err != nil { + return nil, err + } + + return b, nil +} + func randRawKey() ([33]byte, error) { var n [33]byte @@ -389,6 +411,37 @@ func TestEmptyMessageUnknownType(t *testing.T) { } } +// randCustomRecords generates a random set of custom records for testing. +func randCustomRecords(t *testing.T, r *rand.Rand) CustomRecords { + var ( + customRecords = CustomRecords{} + + // We'll generate a random number of records, between 1 and 10. + numRecords = r.Intn(9) + 1 + ) + + // For each record, we'll generate a random key and value. + for i := 0; i < numRecords; i++ { + // Keys must be equal to or greater than + // MinCustomRecordsTlvType. + keyOffset := uint64(r.Intn(100)) + key := MinCustomRecordsTlvType + keyOffset + + // Values are byte slices of any length. + value := make([]byte, r.Intn(100)) + _, err := r.Read(value) + require.NoError(t, err) + + customRecords[key] = value + } + + // Validate the custom records as a sanity check. + err := customRecords.Validate() + require.NoError(t, err) + + return customRecords +} + // TestLightningWireProtocol uses the testing/quick package to create a series // of fuzz tests to attempt to break a primary scenario which is implemented as // property based testing scenario. @@ -1369,6 +1422,8 @@ func TestLightningWireProtocol(t *testing.T) { _, err = r.Read(req.OnionBlob[:]) require.NoError(t, err) + req.CustomRecords = randCustomRecords(t, r) + // Generate a blinding point 50% of the time, since not // all update adds will use route blinding. if r.Int31()%2 == 0 { diff --git a/lnwire/update_add_htlc.go b/lnwire/update_add_htlc.go index 8a40710e82..0a377e710f 100644 --- a/lnwire/update_add_htlc.go +++ b/lnwire/update_add_htlc.go @@ -72,6 +72,11 @@ type UpdateAddHTLC struct { // next hop for this htlc. BlindingPoint BlindingPointRecord + // CustomRecords maps TLV types to byte slices, storing arbitrary data + // intended for inclusion in the ExtraData field of the UpdateAddHTLC + // message. + CustomRecords CustomRecords + // ExtraData is the set of data that was appended to this message to // fill out the full maximum transport message size. These fields can // be used to specify optional data such as custom TLV fields. @@ -92,6 +97,10 @@ var _ Message = (*UpdateAddHTLC)(nil) // // This is part of the lnwire.Message interface. func (c *UpdateAddHTLC) Decode(r io.Reader, pver uint32) error { + // msgExtraData is a temporary variable used to read the message extra + // data field from the reader. + var msgExtraData ExtraOpaqueData + if err := ReadElements(r, &c.ChanID, &c.ID, @@ -99,26 +108,28 @@ func (c *UpdateAddHTLC) Decode(r io.Reader, pver uint32) error { c.PaymentHash[:], &c.Expiry, c.OnionBlob[:], - &c.ExtraData, + &msgExtraData, ); err != nil { return err } + // Extract TLV records from the extra data field. blindingRecord := c.BlindingPoint.Zero() - tlvMap, err := c.ExtraData.ExtractRecords(&blindingRecord) + + customRecords, parsed, extraData, err := ParseAndExtractCustomRecords( + msgExtraData, &blindingRecord, + ) if err != nil { return err } - if val, ok := tlvMap[c.BlindingPoint.TlvType()]; ok && val == nil { + // Assign the parsed records back to the message. + if parsed.Contains(blindingRecord.TlvType()) { c.BlindingPoint = tlv.SomeRecordT(blindingRecord) } - // Set extra data to nil if we didn't parse anything out of it so that - // we can use assert.Equal in tests. - if len(tlvMap) == 0 { - c.ExtraData = nil - } + c.CustomRecords = customRecords + c.ExtraData = extraData return nil } @@ -154,19 +165,18 @@ func (c *UpdateAddHTLC) Encode(w *bytes.Buffer, pver uint32) error { // Only include blinding point in extra data if present. var records []tlv.RecordProducer + c.BlindingPoint.WhenSome( + func(b tlv.RecordT[BlindingPointTlvType, *btcec.PublicKey]) { + records = append(records, &b) + }, + ) - c.BlindingPoint.WhenSome(func(b tlv.RecordT[BlindingPointTlvType, - *btcec.PublicKey]) { - - records = append(records, &b) - }) - - err := EncodeMessageExtraData(&c.ExtraData, records...) + extraData, err := MergeAndEncode(records, c.ExtraData, c.CustomRecords) if err != nil { return err } - return WriteBytes(w, c.ExtraData) + return WriteBytes(w, extraData) } // MsgType returns the integer uniquely identifying this message type on the diff --git a/lnwire/update_add_htlc_test.go b/lnwire/update_add_htlc_test.go new file mode 100644 index 0000000000..53f8921bd9 --- /dev/null +++ b/lnwire/update_add_htlc_test.go @@ -0,0 +1,188 @@ +package lnwire + +import ( + "bytes" + "fmt" + "testing" + + "github.com/lightningnetwork/lnd/tlv" + "github.com/stretchr/testify/require" +) + +// testCase is a test case for the UpdateAddHTLC message. +type testCase struct { + // Msg is the message to be encoded and decoded. + Msg UpdateAddHTLC + + // ExpectEncodeError is a flag that indicates whether we expect the + // encoding of the message to fail. + ExpectEncodeError bool +} + +// generateTestCases generates a set of UpdateAddHTLC message test cases. +func generateTestCases(t *testing.T) []testCase { + // Firstly, we'll set basic values for the message fields. + // + // Generate random channel ID. + chanIDBytes, err := generateRandomBytes(32) + require.NoError(t, err) + + var chanID ChannelID + copy(chanID[:], chanIDBytes) + + // Generate random payment hash. + paymentHashBytes, err := generateRandomBytes(32) + require.NoError(t, err) + + var paymentHash [32]byte + copy(paymentHash[:], paymentHashBytes) + + // Generate random onion blob. + onionBlobBytes, err := generateRandomBytes(OnionPacketSize) + require.NoError(t, err) + + var onionBlob [OnionPacketSize]byte + copy(onionBlob[:], onionBlobBytes) + + // Define the blinding point. + blinding, err := pubkeyFromHex( + "0228f2af0abe322403480fb3ee172f7f1601e67d1da6cad40b54c4468d4" + + "8236c39", + ) + require.NoError(t, err) + + blindingPoint := tlv.SomeRecordT( + tlv.NewPrimitiveRecord[BlindingPointTlvType](blinding), + ) + + // Define custom records. + recordKey1 := uint64(MinCustomRecordsTlvType + 1) + recordValue1, err := generateRandomBytes(10) + require.NoError(t, err) + + recordKey2 := uint64(MinCustomRecordsTlvType + 2) + recordValue2, err := generateRandomBytes(10) + require.NoError(t, err) + + customRecords := CustomRecords{ + recordKey1: recordValue1, + recordKey2: recordValue2, + } + + // Construct an instance of extra data that contains records with TLV + // types below the minimum custom records threshold and that lack + // corresponding fields in the message struct. Content should persist in + // the extra data field after encoding and decoding. + var ( + recordBytes45 = []byte("recordBytes45") + tlvRecord45 = tlv.NewPrimitiveRecord[tlv.TlvType45]( + recordBytes45, + ) + + recordBytes55 = []byte("recordBytes55") + tlvRecord55 = tlv.NewPrimitiveRecord[tlv.TlvType55]( + recordBytes55, + ) + ) + + var extraData ExtraOpaqueData + err = extraData.PackRecords( + []tlv.RecordProducer{&tlvRecord45, &tlvRecord55}..., + ) + require.NoError(t, err) + + invalidCustomRecords := CustomRecords{ + MinCustomRecordsTlvType - 1: recordValue1, + } + + return []testCase{ + { + Msg: UpdateAddHTLC{ + ChanID: chanID, + ID: 42, + Amount: MilliSatoshi(1000), + PaymentHash: paymentHash, + Expiry: 43, + OnionBlob: onionBlob, + BlindingPoint: blindingPoint, + CustomRecords: customRecords, + ExtraData: extraData, + }, + }, + // Add a test case where the blinding point field is not + // populated. + { + Msg: UpdateAddHTLC{ + ChanID: chanID, + ID: 42, + Amount: MilliSatoshi(1000), + PaymentHash: paymentHash, + Expiry: 43, + OnionBlob: onionBlob, + CustomRecords: customRecords, + }, + }, + // Add a test case where the custom records field is not + // populated. + { + Msg: UpdateAddHTLC{ + ChanID: chanID, + ID: 42, + Amount: MilliSatoshi(1000), + PaymentHash: paymentHash, + Expiry: 43, + OnionBlob: onionBlob, + BlindingPoint: blindingPoint, + }, + }, + // Add a case where the custom records are invalid. + { + Msg: UpdateAddHTLC{ + ChanID: chanID, + ID: 42, + Amount: MilliSatoshi(1000), + PaymentHash: paymentHash, + Expiry: 43, + OnionBlob: onionBlob, + BlindingPoint: blindingPoint, + CustomRecords: invalidCustomRecords, + }, + ExpectEncodeError: true, + }, + } +} + +// TestUpdateAddHtlcEncodeDecode tests UpdateAddHTLC message encoding and +// decoding for all supported field values. +func TestUpdateAddHtlcEncodeDecode(t *testing.T) { + t.Parallel() + + // Generate test cases. + testCases := generateTestCases(t) + + // Execute test cases. + for tcIdx, tc := range testCases { + t.Run(fmt.Sprintf("testcase-%d", tcIdx), func(t *testing.T) { + // Encode test case message. + var buf bytes.Buffer + err := tc.Msg.Encode(&buf, 0) + + // Check if we expect an encoding error. + if tc.ExpectEncodeError { + require.Error(t, err) + return + } + + require.NoError(t, err) + + // Decode the encoded message bytes message. + var actualMsg UpdateAddHTLC + decodeReader := bytes.NewReader(buf.Bytes()) + err = actualMsg.Decode(decodeReader, 0) + require.NoError(t, err) + + // Compare the two messages to ensure equality. + require.Equal(t, tc.Msg, actualMsg) + }) + } +} diff --git a/peer/brontide.go b/peer/brontide.go index bbb4aa287d..2e6b8c07a3 100644 --- a/peer/brontide.go +++ b/peer/brontide.go @@ -2193,9 +2193,9 @@ func messageSummary(msg lnwire.Message) string { ) return fmt.Sprintf("chan_id=%v, id=%v, amt=%v, expiry=%v, "+ - "hash=%x, blinding_point=%x", msg.ChanID, msg.ID, - msg.Amount, msg.Expiry, msg.PaymentHash[:], - blindingPoint) + "hash=%x, blinding_point=%x, custom_records=%v", + msg.ChanID, msg.ID, msg.Amount, msg.Expiry, + msg.PaymentHash[:], blindingPoint, msg.CustomRecords) case *lnwire.UpdateFailHTLC: return fmt.Sprintf("chan_id=%v, id=%v, reason=%x", msg.ChanID, From cb1529785364e2622e0f0f4b4d53f17248e04779 Mon Sep 17 00:00:00 2001 From: ffranr Date: Fri, 3 May 2024 16:22:05 +0100 Subject: [PATCH 061/218] lnwire: add custom records field to type `UpdateFulfillHtlc` - Introduce the field `CustomRecords` to the type `UpdateFulfillHtlc`. - Encode and decode the new field into the `ExtraData` field of the `update_fulfill_htlc` wire message. - Empty `ExtraData` field is set to `nil`. --- htlcswitch/payment_result_test.go | 1 - lnwire/lnwire_test.go | 23 +++++ lnwire/update_fulfill_htlc.go | 36 +++++++- lnwire/update_fulfill_htlc_test.go | 129 +++++++++++++++++++++++++++++ peer/brontide.go | 5 +- 5 files changed, 188 insertions(+), 6 deletions(-) create mode 100644 lnwire/update_fulfill_htlc_test.go diff --git a/htlcswitch/payment_result_test.go b/htlcswitch/payment_result_test.go index 99e8074e58..664197f765 100644 --- a/htlcswitch/payment_result_test.go +++ b/htlcswitch/payment_result_test.go @@ -38,7 +38,6 @@ func TestNetworkResultSerialization(t *testing.T) { ChanID: chanID, ID: 2, PaymentPreimage: preimage, - ExtraData: make([]byte, 0), } fail := &lnwire.UpdateFailHTLC{ diff --git a/lnwire/lnwire_test.go b/lnwire/lnwire_test.go index e7e765248b..7eb434f454 100644 --- a/lnwire/lnwire_test.go +++ b/lnwire/lnwire_test.go @@ -1442,6 +1442,29 @@ func TestLightningWireProtocol(t *testing.T) { ) } + v[0] = reflect.ValueOf(*req) + }, + MsgUpdateFulfillHTLC: func(v []reflect.Value, r *rand.Rand) { + req := &UpdateFulfillHTLC{ + ID: r.Uint64(), + } + + _, err := r.Read(req.ChanID[:]) + require.NoError(t, err) + + _, err = r.Read(req.PaymentPreimage[:]) + require.NoError(t, err) + + req.CustomRecords = randCustomRecords(t, r) + + // Generate some random TLV records 50% of the time. + if r.Int31()%2 == 0 { + req.ExtraData = []byte{ + 0x01, 0x03, 1, 2, 3, + 0x02, 0x03, 4, 5, 6, + } + } + v[0] = reflect.ValueOf(*req) }, } diff --git a/lnwire/update_fulfill_htlc.go b/lnwire/update_fulfill_htlc.go index 275a37c87c..35aaa2ff54 100644 --- a/lnwire/update_fulfill_htlc.go +++ b/lnwire/update_fulfill_htlc.go @@ -23,6 +23,10 @@ type UpdateFulfillHTLC struct { // HTLC. PaymentPreimage [32]byte + // CustomRecords maps TLV types to byte slices, storing arbitrary data + // intended for inclusion in the ExtraData field. + CustomRecords CustomRecords + // ExtraData is the set of data that was appended to this message to // fill out the full maximum transport message size. These fields can // be used to specify optional data such as custom TLV fields. @@ -49,12 +53,31 @@ var _ Message = (*UpdateFulfillHTLC)(nil) // // This is part of the lnwire.Message interface. func (c *UpdateFulfillHTLC) Decode(r io.Reader, pver uint32) error { - return ReadElements(r, + // msgExtraData is a temporary variable used to read the message extra + // data field from the reader. + var msgExtraData ExtraOpaqueData + + if err := ReadElements(r, &c.ChanID, &c.ID, c.PaymentPreimage[:], - &c.ExtraData, + &msgExtraData, + ); err != nil { + return err + } + + // Extract custom records from the extra data field. + customRecords, _, extraData, err := ParseAndExtractCustomRecords( + msgExtraData, ) + if err != nil { + return err + } + + c.CustomRecords = customRecords + c.ExtraData = extraData + + return nil } // Encode serializes the target UpdateFulfillHTLC into the passed io.Writer @@ -74,7 +97,14 @@ func (c *UpdateFulfillHTLC) Encode(w *bytes.Buffer, pver uint32) error { return err } - return WriteBytes(w, c.ExtraData) + // Combine the custom records and the extra data, then encode the + // result as a byte slice. + extraData, err := MergeAndEncode(nil, c.ExtraData, c.CustomRecords) + if err != nil { + return err + } + + return WriteBytes(w, extraData) } // MsgType returns the integer uniquely identifying this message type on the diff --git a/lnwire/update_fulfill_htlc_test.go b/lnwire/update_fulfill_htlc_test.go new file mode 100644 index 0000000000..e38b3a9b82 --- /dev/null +++ b/lnwire/update_fulfill_htlc_test.go @@ -0,0 +1,129 @@ +package lnwire + +import ( + "bytes" + "fmt" + "testing" + + "github.com/lightningnetwork/lnd/tlv" + "github.com/stretchr/testify/require" +) + +// testCaseUpdateFulfill is a test case for the UpdateFulfillHTLC message. +type testCaseUpdateFulfill struct { + // Msg is the message to be encoded and decoded. + Msg UpdateFulfillHTLC + + // ExpectEncodeError is a flag that indicates whether we expect the + // encoding of the message to fail. + ExpectEncodeError bool +} + +// generateTestCases generates a set of UpdateFulfillHTLC message test cases. +func generateUpdateFulfillTestCases(t *testing.T) []testCaseUpdateFulfill { + // Firstly, we'll set basic values for the message fields. + // + // Generate random channel ID. + chanIDBytes, err := generateRandomBytes(32) + require.NoError(t, err) + + var chanID ChannelID + copy(chanID[:], chanIDBytes) + + // Generate random payment preimage. + paymentPreimageBytes, err := generateRandomBytes(32) + require.NoError(t, err) + + var paymentPreimage [32]byte + copy(paymentPreimage[:], paymentPreimageBytes) + + // Define custom records. + recordKey1 := uint64(MinCustomRecordsTlvType + 1) + recordValue1, err := generateRandomBytes(10) + require.NoError(t, err) + + recordKey2 := uint64(MinCustomRecordsTlvType + 2) + recordValue2, err := generateRandomBytes(10) + require.NoError(t, err) + + customRecords := CustomRecords{ + recordKey1: recordValue1, + recordKey2: recordValue2, + } + + // Construct an instance of extra data that contains records with TLV + // types below the minimum custom records threshold and that lack + // corresponding fields in the message struct. Content should persist in + // the extra data field after encoding and decoding. + var ( + recordBytes45 = []byte("recordBytes45") + tlvRecord45 = tlv.NewPrimitiveRecord[tlv.TlvType45]( + recordBytes45, + ) + + recordBytes55 = []byte("recordBytes55") + tlvRecord55 = tlv.NewPrimitiveRecord[tlv.TlvType55]( + recordBytes55, + ) + ) + + var extraData ExtraOpaqueData + err = extraData.PackRecords( + []tlv.RecordProducer{&tlvRecord45, &tlvRecord55}..., + ) + require.NoError(t, err) + + return []testCaseUpdateFulfill{ + { + Msg: UpdateFulfillHTLC{ + ChanID: chanID, + ID: 42, + PaymentPreimage: paymentPreimage, + }, + }, + { + Msg: UpdateFulfillHTLC{ + ChanID: chanID, + ID: 42, + PaymentPreimage: paymentPreimage, + CustomRecords: customRecords, + ExtraData: extraData, + }, + }, + } +} + +// TestUpdateFulfillHtlcEncodeDecode tests UpdateFulfillHTLC message encoding +// and decoding for all supported field values. +func TestUpdateFulfillHtlcEncodeDecode(t *testing.T) { + t.Parallel() + + // Generate test cases. + testCases := generateUpdateFulfillTestCases(t) + + // Execute test cases. + for tcIdx, tc := range testCases { + t.Run(fmt.Sprintf("testcase-%d", tcIdx), func(t *testing.T) { + // Encode test case message. + var buf bytes.Buffer + err := tc.Msg.Encode(&buf, 0) + + // Check if we expect an encoding error. + if tc.ExpectEncodeError { + require.Error(t, err) + return + } + + require.NoError(t, err) + + // Decode the encoded message bytes message. + var actualMsg UpdateFulfillHTLC + decodeReader := bytes.NewReader(buf.Bytes()) + err = actualMsg.Decode(decodeReader, 0) + require.NoError(t, err) + + // Compare the two messages to ensure equality. + require.Equal(t, tc.Msg, actualMsg) + }) + } +} diff --git a/peer/brontide.go b/peer/brontide.go index 2e6b8c07a3..3223e7f4b7 100644 --- a/peer/brontide.go +++ b/peer/brontide.go @@ -2202,8 +2202,9 @@ func messageSummary(msg lnwire.Message) string { msg.ID, msg.Reason) case *lnwire.UpdateFulfillHTLC: - return fmt.Sprintf("chan_id=%v, id=%v, pre_image=%x", - msg.ChanID, msg.ID, msg.PaymentPreimage[:]) + return fmt.Sprintf("chan_id=%v, id=%v, pre_image=%x, "+ + "custom_records=%v", msg.ChanID, msg.ID, + msg.PaymentPreimage[:], msg.CustomRecords) case *lnwire.CommitSig: return fmt.Sprintf("chan_id=%v, num_htlcs=%v", msg.ChanID, From 49db662b8272562f4f993a654a40a68440f28855 Mon Sep 17 00:00:00 2001 From: ffranr Date: Sat, 13 Apr 2024 12:29:52 +0100 Subject: [PATCH 062/218] htlcswitch: add resume modified HTLC action to switch Introduce `ResumeModified` action to resume standard behavior of a p2p message with optional modifications as specified by the client during interception. --- htlcswitch/interceptable_switch.go | 76 ++++++++++++++++++++++++++++++ htlcswitch/interfaces.go | 5 ++ intercepted_forward.go | 9 ++++ invoices/invoices_test.go | 2 +- 4 files changed, 91 insertions(+), 1 deletion(-) diff --git a/htlcswitch/interceptable_switch.go b/htlcswitch/interceptable_switch.go index 5517eb82dd..5a1fd807ec 100644 --- a/htlcswitch/interceptable_switch.go +++ b/htlcswitch/interceptable_switch.go @@ -9,6 +9,7 @@ import ( "github.com/go-errors/errors" "github.com/lightningnetwork/lnd/chainntnfs" "github.com/lightningnetwork/lnd/channeldb/models" + "github.com/lightningnetwork/lnd/fn" "github.com/lightningnetwork/lnd/htlcswitch/hop" "github.com/lightningnetwork/lnd/lntypes" "github.com/lightningnetwork/lnd/lnwire" @@ -109,6 +110,10 @@ const ( // FwdActionFail fails the intercepted packet back to the sender. FwdActionFail + + // FwdActionResumeModified forwards the intercepted packet to the switch + // with modifications. + FwdActionResumeModified ) // FwdResolution defines the action to be taken on an intercepted packet. @@ -123,6 +128,14 @@ type FwdResolution struct { // FwdActionSettle. Preimage lntypes.Preimage + // OutAmountMsat is the amount that is to be used for forwarding if + // Action is FwdActionResumeModified. + OutAmountMsat fn.Option[lnwire.MilliSatoshi] + + // OutWireCustomRecords is the custom records that are to be used for + // forwarding if Action is FwdActionResumeModified. + OutWireCustomRecords fn.Option[lnwire.CustomRecords] + // FailureMessage is the encrypted failure message that is to be passed // back to the sender if action is FwdActionFail. FailureMessage []byte @@ -399,6 +412,11 @@ func (s *InterceptableSwitch) resolve(res *FwdResolution) error { case FwdActionResume: return intercepted.Resume() + case FwdActionResumeModified: + return intercepted.ResumeModified( + res.OutAmountMsat, res.OutWireCustomRecords, + ) + case FwdActionSettle: return intercepted.Settle(res.Preimage) @@ -641,6 +659,64 @@ func (f *interceptedForward) Resume() error { return f.htlcSwitch.ForwardPackets(nil, f.packet) } +// ResumeModified resumes the default behavior with field modifications. The +// input amount (if provided) specifies that the value of the inbound HTLC +// should be interpreted differently from the on-chain amount during further +// validation. The presence of an output amount and/or custom records indicates +// that those values should be modified on the outgoing HTLC. +func (f *interceptedForward) ResumeModified( + outAmountMsat fn.Option[lnwire.MilliSatoshi], + outWireCustomRecords fn.Option[lnwire.CustomRecords]) error { + + // Convert the optional custom records to the correct type and validate + // them. + validatedRecords, err := fn.MapOptionZ( + outWireCustomRecords, + func(cr lnwire.CustomRecords) fn.Result[lnwire.CustomRecords] { + if len(cr) == 0 { + return fn.Ok[lnwire.CustomRecords](nil) + } + + // Type cast and validate custom records. + err := cr.Validate() + if err != nil { + return fn.Err[lnwire.CustomRecords]( + fmt.Errorf("failed to validate "+ + "custom records: %w", err), + ) + } + + return fn.Ok(cr) + }, + ).Unpack() + if err != nil { + return fmt.Errorf("failed to encode custom records: %w", + err) + } + + // Modify the wire message contained in the packet. + switch htlc := f.packet.htlc.(type) { + case *lnwire.UpdateAddHTLC: + outAmountMsat.WhenSome(func(amount lnwire.MilliSatoshi) { + f.packet.amount = amount + htlc.Amount = amount + }) + + if len(validatedRecords) > 0 { + htlc.CustomRecords = validatedRecords + } + + case *lnwire.UpdateFulfillHTLC: + if len(validatedRecords) > 0 { + htlc.CustomRecords = validatedRecords + } + } + + // Forward to the switch. A link quit channel isn't needed, because we + // are on a different thread now. + return f.htlcSwitch.ForwardPackets(nil, f.packet) +} + // Fail notifies the intention to Fail an existing hold forward with an // encrypted failure reason. func (f *interceptedForward) Fail(reason []byte) error { diff --git a/htlcswitch/interfaces.go b/htlcswitch/interfaces.go index 246c76902a..99b3ce6737 100644 --- a/htlcswitch/interfaces.go +++ b/htlcswitch/interfaces.go @@ -383,6 +383,11 @@ type InterceptedForward interface { // this htlc which usually means forward it. Resume() error + // ResumeModified notifies the intention to resume an existing hold + // forward with modified fields. + ResumeModified(outgoingAmountMsat fn.Option[lnwire.MilliSatoshi], + customRecords fn.Option[lnwire.CustomRecords]) error + // Settle notifies the intention to settle an existing hold // forward with a given preimage. Settle(lntypes.Preimage) error diff --git a/intercepted_forward.go b/intercepted_forward.go index 70590d0e41..c3ffbb4fba 100644 --- a/intercepted_forward.go +++ b/intercepted_forward.go @@ -3,6 +3,7 @@ package lnd import ( "errors" + "github.com/lightningnetwork/lnd/fn" "github.com/lightningnetwork/lnd/htlcswitch" "github.com/lightningnetwork/lnd/lntypes" "github.com/lightningnetwork/lnd/lnwire" @@ -51,6 +52,14 @@ func (f *interceptedForward) Resume() error { return ErrCannotResume } +// ResumeModified notifies the intention to resume an existing hold forward with +// a modified htlc. +func (f *interceptedForward) ResumeModified(_ fn.Option[lnwire.MilliSatoshi], + _ fn.Option[lnwire.CustomRecords]) error { + + return ErrCannotResume +} + // Fail notifies the intention to fail an existing hold forward with an // encrypted failure reason. func (f *interceptedForward) Fail(_ []byte) error { diff --git a/invoices/invoices_test.go b/invoices/invoices_test.go index 8002613557..b6efd6e557 100644 --- a/invoices/invoices_test.go +++ b/invoices/invoices_test.go @@ -176,7 +176,7 @@ func TestInvoices(t *testing.T) { test: testQueryInvoices, }, { - name: "CustomRecords", + name: "OutWireCustomRecords", test: testCustomRecords, }, { From 533b9f2d451faa3611641d4e5bb37f005af0585d Mon Sep 17 00:00:00 2001 From: ffranr Date: Sat, 13 Apr 2024 11:42:20 +0100 Subject: [PATCH 063/218] routerrpc: extend HTLC forward interceptor resp with modification fields This commit extends the forward HTLC intercept response with fields that can be used in conjunction with a `ResumeModified` action to modify the intercepted HTLC p2p message. --- htlcswitch/interceptable_switch.go | 13 +- htlcswitch/interfaces.go | 5 +- intercepted_forward.go | 2 +- lnrpc/routerrpc/forward_interceptor.go | 45 +- lnrpc/routerrpc/router.pb.go | 590 ++++++++++++++----------- lnrpc/routerrpc/router.proto | 26 ++ lnrpc/routerrpc/router.swagger.json | 26 +- 7 files changed, 436 insertions(+), 271 deletions(-) diff --git a/htlcswitch/interceptable_switch.go b/htlcswitch/interceptable_switch.go index 5a1fd807ec..aa500e3aab 100644 --- a/htlcswitch/interceptable_switch.go +++ b/htlcswitch/interceptable_switch.go @@ -128,6 +128,10 @@ type FwdResolution struct { // FwdActionSettle. Preimage lntypes.Preimage + // InAmountMsat is the amount that is to be used for validating if + // Action is FwdActionResumeModified. + InAmountMsat fn.Option[lnwire.MilliSatoshi] + // OutAmountMsat is the amount that is to be used for forwarding if // Action is FwdActionResumeModified. OutAmountMsat fn.Option[lnwire.MilliSatoshi] @@ -414,7 +418,8 @@ func (s *InterceptableSwitch) resolve(res *FwdResolution) error { case FwdActionResumeModified: return intercepted.ResumeModified( - res.OutAmountMsat, res.OutWireCustomRecords, + res.InAmountMsat, res.OutAmountMsat, + res.OutWireCustomRecords, ) case FwdActionSettle: @@ -665,6 +670,7 @@ func (f *interceptedForward) Resume() error { // validation. The presence of an output amount and/or custom records indicates // that those values should be modified on the outgoing HTLC. func (f *interceptedForward) ResumeModified( + inAmountMsat fn.Option[lnwire.MilliSatoshi], outAmountMsat fn.Option[lnwire.MilliSatoshi], outWireCustomRecords fn.Option[lnwire.CustomRecords]) error { @@ -694,6 +700,11 @@ func (f *interceptedForward) ResumeModified( err) } + // Set the incoming amount, if it is provided, on the packet. + inAmountMsat.WhenSome(func(amount lnwire.MilliSatoshi) { + f.packet.incomingAmount = amount + }) + // Modify the wire message contained in the packet. switch htlc := f.packet.htlc.(type) { case *lnwire.UpdateAddHTLC: diff --git a/htlcswitch/interfaces.go b/htlcswitch/interfaces.go index 99b3ce6737..5e808f42a2 100644 --- a/htlcswitch/interfaces.go +++ b/htlcswitch/interfaces.go @@ -385,8 +385,9 @@ type InterceptedForward interface { // ResumeModified notifies the intention to resume an existing hold // forward with modified fields. - ResumeModified(outgoingAmountMsat fn.Option[lnwire.MilliSatoshi], - customRecords fn.Option[lnwire.CustomRecords]) error + ResumeModified(inAmountMsat, + outAmountMsat fn.Option[lnwire.MilliSatoshi], + outWireCustomRecords fn.Option[lnwire.CustomRecords]) error // Settle notifies the intention to settle an existing hold // forward with a given preimage. diff --git a/intercepted_forward.go b/intercepted_forward.go index c3ffbb4fba..791d4bd583 100644 --- a/intercepted_forward.go +++ b/intercepted_forward.go @@ -54,7 +54,7 @@ func (f *interceptedForward) Resume() error { // ResumeModified notifies the intention to resume an existing hold forward with // a modified htlc. -func (f *interceptedForward) ResumeModified(_ fn.Option[lnwire.MilliSatoshi], +func (f *interceptedForward) ResumeModified(_, _ fn.Option[lnwire.MilliSatoshi], _ fn.Option[lnwire.CustomRecords]) error { return ErrCannotResume diff --git a/lnrpc/routerrpc/forward_interceptor.go b/lnrpc/routerrpc/forward_interceptor.go index 55c77d6d9b..4ed497034f 100644 --- a/lnrpc/routerrpc/forward_interceptor.go +++ b/lnrpc/routerrpc/forward_interceptor.go @@ -4,6 +4,7 @@ import ( "errors" "github.com/lightningnetwork/lnd/channeldb/models" + "github.com/lightningnetwork/lnd/fn" "github.com/lightningnetwork/lnd/htlcswitch" "github.com/lightningnetwork/lnd/lnrpc" "github.com/lightningnetwork/lnd/lntypes" @@ -108,7 +109,9 @@ func (r *forwardInterceptor) resolveFromClient( log.Tracef("Resolving intercepted packet %v", in) circuitKey := models.CircuitKey{ - ChanID: lnwire.NewShortChanIDFromInt(in.IncomingCircuitKey.ChanId), + ChanID: lnwire.NewShortChanIDFromInt( + in.IncomingCircuitKey.ChanId, + ), HtlcID: in.IncomingCircuitKey.HtlcId, } @@ -119,6 +122,46 @@ func (r *forwardInterceptor) resolveFromClient( Action: htlcswitch.FwdActionResume, }) + case ResolveHoldForwardAction_RESUME_MODIFIED: + // Modify HTLC and resume forward. + inAmtMsat := fn.None[lnwire.MilliSatoshi]() + if in.InAmountMsat > 0 { + inAmtMsat = fn.Some(lnwire.MilliSatoshi( + in.InAmountMsat, + )) + } + + outAmtMsat := fn.None[lnwire.MilliSatoshi]() + if in.OutAmountMsat > 0 { + outAmtMsat = fn.Some(lnwire.MilliSatoshi( + in.OutAmountMsat, + )) + } + + outWireCustomRecords := fn.None[lnwire.CustomRecords]() + if len(in.OutWireCustomRecords) > 0 { + // Validate custom records. + cr := lnwire.CustomRecords(in.OutWireCustomRecords) + if err := cr.Validate(); err != nil { + return status.Errorf( + codes.InvalidArgument, + "failed to validate custom records: %v", + err, + ) + } + + outWireCustomRecords = fn.Some[lnwire.CustomRecords](cr) + } + + //nolint:lll + return r.htlcSwitch.Resolve(&htlcswitch.FwdResolution{ + Key: circuitKey, + Action: htlcswitch.FwdActionResumeModified, + InAmountMsat: inAmtMsat, + OutAmountMsat: outAmtMsat, + OutWireCustomRecords: outWireCustomRecords, + }) + case ResolveHoldForwardAction_FAIL: // Fail with an encrypted reason. if in.FailureMessage != nil { diff --git a/lnrpc/routerrpc/router.pb.go b/lnrpc/routerrpc/router.pb.go index c1517a595f..dc52041a0f 100644 --- a/lnrpc/routerrpc/router.pb.go +++ b/lnrpc/routerrpc/router.pb.go @@ -203,9 +203,16 @@ func (PaymentState) EnumDescriptor() ([]byte, []int) { type ResolveHoldForwardAction int32 const ( + // SETTLE is an action that is used to settle an HTLC instead of forwarding + // it. ResolveHoldForwardAction_SETTLE ResolveHoldForwardAction = 0 - ResolveHoldForwardAction_FAIL ResolveHoldForwardAction = 1 + // FAIL is an action that is used to fail an HTLC backwards. + ResolveHoldForwardAction_FAIL ResolveHoldForwardAction = 1 + // RESUME is an action that is used to resume a forward HTLC. ResolveHoldForwardAction_RESUME ResolveHoldForwardAction = 2 + // RESUME_MODIFIED is an action that is used to resume a hold forward HTLC + // with modifications specified during interception. + ResolveHoldForwardAction_RESUME_MODIFIED ResolveHoldForwardAction = 3 ) // Enum value maps for ResolveHoldForwardAction. @@ -214,11 +221,13 @@ var ( 0: "SETTLE", 1: "FAIL", 2: "RESUME", + 3: "RESUME_MODIFIED", } ResolveHoldForwardAction_value = map[string]int32{ - "SETTLE": 0, - "FAIL": 1, - "RESUME": 2, + "SETTLE": 0, + "FAIL": 1, + "RESUME": 2, + "RESUME_MODIFIED": 3, } ) @@ -3156,6 +3165,8 @@ func (x *ForwardHtlcInterceptRequest) GetAutoFailHeight() int32 { // ForwardHtlcInterceptResponse enables the caller to resolve a previously hold // forward. The caller can choose either to: // - `Resume`: Execute the default behavior (usually forward). +// - `ResumeModified`: Execute the default behavior (usually forward) with HTLC +// field modifications. // - `Reject`: Fail the htlc backwards. // - `Settle`: Settle this htlc with a given preimage. type ForwardHtlcInterceptResponse struct { @@ -3184,6 +3195,17 @@ type ForwardHtlcInterceptResponse struct { // For backwards-compatibility reasons, TEMPORARY_CHANNEL_FAILURE is the // default value for this field. FailureCode lnrpc.Failure_FailureCode `protobuf:"varint,5,opt,name=failure_code,json=failureCode,proto3,enum=lnrpc.Failure_FailureCode" json:"failure_code,omitempty"` + // The amount that was set on the p2p wire message of the incoming HTLC. + // This field is ignored if the action is not RESUME_MODIFIED or the amount + // is zero. + InAmountMsat uint64 `protobuf:"varint,6,opt,name=in_amount_msat,json=inAmountMsat,proto3" json:"in_amount_msat,omitempty"` + // The amount to set on the p2p wire message of the resumed HTLC. This field + // is ignored if the action is not RESUME_MODIFIED or the amount is zero. + OutAmountMsat uint64 `protobuf:"varint,7,opt,name=out_amount_msat,json=outAmountMsat,proto3" json:"out_amount_msat,omitempty"` + // Any custom records that should be set on the p2p wire message message of + // the resumed HTLC. This field is ignored if the action is not + // RESUME_MODIFIED. + OutWireCustomRecords map[uint64][]byte `protobuf:"bytes,8,rep,name=out_wire_custom_records,json=outWireCustomRecords,proto3" json:"out_wire_custom_records,omitempty" protobuf_key:"varint,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` } func (x *ForwardHtlcInterceptResponse) Reset() { @@ -3253,6 +3275,27 @@ func (x *ForwardHtlcInterceptResponse) GetFailureCode() lnrpc.Failure_FailureCod return lnrpc.Failure_FailureCode(0) } +func (x *ForwardHtlcInterceptResponse) GetInAmountMsat() uint64 { + if x != nil { + return x.InAmountMsat + } + return 0 +} + +func (x *ForwardHtlcInterceptResponse) GetOutAmountMsat() uint64 { + if x != nil { + return x.OutAmountMsat + } + return 0 +} + +func (x *ForwardHtlcInterceptResponse) GetOutWireCustomRecords() map[uint64][]byte { + if x != nil { + return x.OutWireCustomRecords + } + return nil +} + type UpdateChanStatusRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -3930,7 +3973,7 @@ var file_routerrpc_router_proto_rawDesc = []byte{ 0x6f, 0x72, 0x64, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, - 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xa8, 0x02, 0x0a, 0x1c, 0x46, 0x6f, 0x72, 0x77, 0x61, 0x72, + 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xb9, 0x04, 0x0a, 0x1c, 0x46, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x48, 0x74, 0x6c, 0x63, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x47, 0x0a, 0x14, 0x69, 0x6e, 0x63, 0x6f, 0x6d, 0x69, 0x6e, 0x67, 0x5f, 0x63, 0x69, 0x72, 0x63, 0x75, 0x69, 0x74, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, @@ -3949,199 +3992,218 @@ var file_routerrpc_router_proto_rawDesc = []byte{ 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x2e, 0x46, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x43, 0x6f, 0x64, 0x65, 0x52, 0x0b, 0x66, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x43, 0x6f, 0x64, 0x65, - 0x22, 0x82, 0x01, 0x0a, 0x17, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x53, - 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x32, 0x0a, 0x0a, - 0x63, 0x68, 0x61, 0x6e, 0x5f, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x13, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, - 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x09, 0x63, 0x68, 0x61, 0x6e, 0x50, 0x6f, 0x69, 0x6e, 0x74, - 0x12, 0x33, 0x0a, 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, - 0x32, 0x1b, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, - 0x6e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x06, 0x61, - 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x1a, 0x0a, 0x18, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x43, - 0x68, 0x61, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x43, 0x0a, 0x11, 0x41, 0x64, 0x64, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x65, 0x73, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2e, 0x0a, 0x0a, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x5f, - 0x6d, 0x61, 0x70, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x6c, 0x6e, 0x72, - 0x70, 0x63, 0x2e, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x4d, 0x61, 0x70, 0x52, 0x09, 0x61, 0x6c, 0x69, - 0x61, 0x73, 0x4d, 0x61, 0x70, 0x73, 0x22, 0x44, 0x0a, 0x12, 0x41, 0x64, 0x64, 0x41, 0x6c, 0x69, - 0x61, 0x73, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2e, 0x0a, 0x0a, - 0x61, 0x6c, 0x69, 0x61, 0x73, 0x5f, 0x6d, 0x61, 0x70, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x0f, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x4d, 0x61, - 0x70, 0x52, 0x09, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x4d, 0x61, 0x70, 0x73, 0x22, 0x46, 0x0a, 0x14, - 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x65, 0x73, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x12, 0x2e, 0x0a, 0x0a, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x5f, 0x6d, 0x61, - 0x70, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, - 0x2e, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x4d, 0x61, 0x70, 0x52, 0x09, 0x61, 0x6c, 0x69, 0x61, 0x73, - 0x4d, 0x61, 0x70, 0x73, 0x22, 0x47, 0x0a, 0x15, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x41, 0x6c, + 0x12, 0x24, 0x0a, 0x0e, 0x69, 0x6e, 0x5f, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x6d, 0x73, + 0x61, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0c, 0x69, 0x6e, 0x41, 0x6d, 0x6f, 0x75, + 0x6e, 0x74, 0x4d, 0x73, 0x61, 0x74, 0x12, 0x26, 0x0a, 0x0f, 0x6f, 0x75, 0x74, 0x5f, 0x61, 0x6d, + 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x04, 0x52, + 0x0d, 0x6f, 0x75, 0x74, 0x41, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x4d, 0x73, 0x61, 0x74, 0x12, 0x78, + 0x0a, 0x17, 0x6f, 0x75, 0x74, 0x5f, 0x77, 0x69, 0x72, 0x65, 0x5f, 0x63, 0x75, 0x73, 0x74, 0x6f, + 0x6d, 0x5f, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x18, 0x08, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x41, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x6f, 0x72, 0x77, + 0x61, 0x72, 0x64, 0x48, 0x74, 0x6c, 0x63, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x4f, 0x75, 0x74, 0x57, 0x69, 0x72, 0x65, + 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x45, 0x6e, 0x74, + 0x72, 0x79, 0x52, 0x14, 0x6f, 0x75, 0x74, 0x57, 0x69, 0x72, 0x65, 0x43, 0x75, 0x73, 0x74, 0x6f, + 0x6d, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x1a, 0x47, 0x0a, 0x19, 0x4f, 0x75, 0x74, 0x57, + 0x69, 0x72, 0x65, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, + 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x04, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, + 0x01, 0x22, 0x82, 0x01, 0x0a, 0x17, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x43, 0x68, 0x61, 0x6e, + 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x32, 0x0a, + 0x0a, 0x63, 0x68, 0x61, 0x6e, 0x5f, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x13, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, + 0x6c, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x09, 0x63, 0x68, 0x61, 0x6e, 0x50, 0x6f, 0x69, 0x6e, + 0x74, 0x12, 0x33, 0x0a, 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0e, 0x32, 0x1b, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, + 0x61, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x06, + 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x1a, 0x0a, 0x18, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, + 0x43, 0x68, 0x61, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x22, 0x43, 0x0a, 0x11, 0x41, 0x64, 0x64, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x65, 0x73, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2e, 0x0a, 0x0a, 0x61, 0x6c, 0x69, 0x61, 0x73, + 0x5f, 0x6d, 0x61, 0x70, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x6c, 0x6e, + 0x72, 0x70, 0x63, 0x2e, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x4d, 0x61, 0x70, 0x52, 0x09, 0x61, 0x6c, + 0x69, 0x61, 0x73, 0x4d, 0x61, 0x70, 0x73, 0x22, 0x44, 0x0a, 0x12, 0x41, 0x64, 0x64, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2e, 0x0a, 0x0a, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x5f, 0x6d, 0x61, 0x70, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x4d, - 0x61, 0x70, 0x52, 0x09, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x4d, 0x61, 0x70, 0x73, 0x2a, 0x81, 0x04, - 0x0a, 0x0d, 0x46, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x12, - 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, - 0x4e, 0x4f, 0x5f, 0x44, 0x45, 0x54, 0x41, 0x49, 0x4c, 0x10, 0x01, 0x12, 0x10, 0x0a, 0x0c, 0x4f, - 0x4e, 0x49, 0x4f, 0x4e, 0x5f, 0x44, 0x45, 0x43, 0x4f, 0x44, 0x45, 0x10, 0x02, 0x12, 0x15, 0x0a, - 0x11, 0x4c, 0x49, 0x4e, 0x4b, 0x5f, 0x4e, 0x4f, 0x54, 0x5f, 0x45, 0x4c, 0x49, 0x47, 0x49, 0x42, - 0x4c, 0x45, 0x10, 0x03, 0x12, 0x14, 0x0a, 0x10, 0x4f, 0x4e, 0x5f, 0x43, 0x48, 0x41, 0x49, 0x4e, - 0x5f, 0x54, 0x49, 0x4d, 0x45, 0x4f, 0x55, 0x54, 0x10, 0x04, 0x12, 0x14, 0x0a, 0x10, 0x48, 0x54, - 0x4c, 0x43, 0x5f, 0x45, 0x58, 0x43, 0x45, 0x45, 0x44, 0x53, 0x5f, 0x4d, 0x41, 0x58, 0x10, 0x05, - 0x12, 0x18, 0x0a, 0x14, 0x49, 0x4e, 0x53, 0x55, 0x46, 0x46, 0x49, 0x43, 0x49, 0x45, 0x4e, 0x54, - 0x5f, 0x42, 0x41, 0x4c, 0x41, 0x4e, 0x43, 0x45, 0x10, 0x06, 0x12, 0x16, 0x0a, 0x12, 0x49, 0x4e, - 0x43, 0x4f, 0x4d, 0x50, 0x4c, 0x45, 0x54, 0x45, 0x5f, 0x46, 0x4f, 0x52, 0x57, 0x41, 0x52, 0x44, - 0x10, 0x07, 0x12, 0x13, 0x0a, 0x0f, 0x48, 0x54, 0x4c, 0x43, 0x5f, 0x41, 0x44, 0x44, 0x5f, 0x46, - 0x41, 0x49, 0x4c, 0x45, 0x44, 0x10, 0x08, 0x12, 0x15, 0x0a, 0x11, 0x46, 0x4f, 0x52, 0x57, 0x41, - 0x52, 0x44, 0x53, 0x5f, 0x44, 0x49, 0x53, 0x41, 0x42, 0x4c, 0x45, 0x44, 0x10, 0x09, 0x12, 0x14, - 0x0a, 0x10, 0x49, 0x4e, 0x56, 0x4f, 0x49, 0x43, 0x45, 0x5f, 0x43, 0x41, 0x4e, 0x43, 0x45, 0x4c, - 0x45, 0x44, 0x10, 0x0a, 0x12, 0x15, 0x0a, 0x11, 0x49, 0x4e, 0x56, 0x4f, 0x49, 0x43, 0x45, 0x5f, - 0x55, 0x4e, 0x44, 0x45, 0x52, 0x50, 0x41, 0x49, 0x44, 0x10, 0x0b, 0x12, 0x1b, 0x0a, 0x17, 0x49, - 0x4e, 0x56, 0x4f, 0x49, 0x43, 0x45, 0x5f, 0x45, 0x58, 0x50, 0x49, 0x52, 0x59, 0x5f, 0x54, 0x4f, - 0x4f, 0x5f, 0x53, 0x4f, 0x4f, 0x4e, 0x10, 0x0c, 0x12, 0x14, 0x0a, 0x10, 0x49, 0x4e, 0x56, 0x4f, - 0x49, 0x43, 0x45, 0x5f, 0x4e, 0x4f, 0x54, 0x5f, 0x4f, 0x50, 0x45, 0x4e, 0x10, 0x0d, 0x12, 0x17, - 0x0a, 0x13, 0x4d, 0x50, 0x50, 0x5f, 0x49, 0x4e, 0x56, 0x4f, 0x49, 0x43, 0x45, 0x5f, 0x54, 0x49, - 0x4d, 0x45, 0x4f, 0x55, 0x54, 0x10, 0x0e, 0x12, 0x14, 0x0a, 0x10, 0x41, 0x44, 0x44, 0x52, 0x45, - 0x53, 0x53, 0x5f, 0x4d, 0x49, 0x53, 0x4d, 0x41, 0x54, 0x43, 0x48, 0x10, 0x0f, 0x12, 0x16, 0x0a, - 0x12, 0x53, 0x45, 0x54, 0x5f, 0x54, 0x4f, 0x54, 0x41, 0x4c, 0x5f, 0x4d, 0x49, 0x53, 0x4d, 0x41, - 0x54, 0x43, 0x48, 0x10, 0x10, 0x12, 0x15, 0x0a, 0x11, 0x53, 0x45, 0x54, 0x5f, 0x54, 0x4f, 0x54, - 0x41, 0x4c, 0x5f, 0x54, 0x4f, 0x4f, 0x5f, 0x4c, 0x4f, 0x57, 0x10, 0x11, 0x12, 0x10, 0x0a, 0x0c, - 0x53, 0x45, 0x54, 0x5f, 0x4f, 0x56, 0x45, 0x52, 0x50, 0x41, 0x49, 0x44, 0x10, 0x12, 0x12, 0x13, - 0x0a, 0x0f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x5f, 0x49, 0x4e, 0x56, 0x4f, 0x49, 0x43, - 0x45, 0x10, 0x13, 0x12, 0x13, 0x0a, 0x0f, 0x49, 0x4e, 0x56, 0x41, 0x4c, 0x49, 0x44, 0x5f, 0x4b, - 0x45, 0x59, 0x53, 0x45, 0x4e, 0x44, 0x10, 0x14, 0x12, 0x13, 0x0a, 0x0f, 0x4d, 0x50, 0x50, 0x5f, - 0x49, 0x4e, 0x5f, 0x50, 0x52, 0x4f, 0x47, 0x52, 0x45, 0x53, 0x53, 0x10, 0x15, 0x12, 0x12, 0x0a, - 0x0e, 0x43, 0x49, 0x52, 0x43, 0x55, 0x4c, 0x41, 0x52, 0x5f, 0x52, 0x4f, 0x55, 0x54, 0x45, 0x10, - 0x16, 0x2a, 0xae, 0x01, 0x0a, 0x0c, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x53, 0x74, 0x61, - 0x74, 0x65, 0x12, 0x0d, 0x0a, 0x09, 0x49, 0x4e, 0x5f, 0x46, 0x4c, 0x49, 0x47, 0x48, 0x54, 0x10, - 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x53, 0x55, 0x43, 0x43, 0x45, 0x45, 0x44, 0x45, 0x44, 0x10, 0x01, - 0x12, 0x12, 0x0a, 0x0e, 0x46, 0x41, 0x49, 0x4c, 0x45, 0x44, 0x5f, 0x54, 0x49, 0x4d, 0x45, 0x4f, - 0x55, 0x54, 0x10, 0x02, 0x12, 0x13, 0x0a, 0x0f, 0x46, 0x41, 0x49, 0x4c, 0x45, 0x44, 0x5f, 0x4e, - 0x4f, 0x5f, 0x52, 0x4f, 0x55, 0x54, 0x45, 0x10, 0x03, 0x12, 0x10, 0x0a, 0x0c, 0x46, 0x41, 0x49, - 0x4c, 0x45, 0x44, 0x5f, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, 0x04, 0x12, 0x24, 0x0a, 0x20, 0x46, - 0x41, 0x49, 0x4c, 0x45, 0x44, 0x5f, 0x49, 0x4e, 0x43, 0x4f, 0x52, 0x52, 0x45, 0x43, 0x54, 0x5f, - 0x50, 0x41, 0x59, 0x4d, 0x45, 0x4e, 0x54, 0x5f, 0x44, 0x45, 0x54, 0x41, 0x49, 0x4c, 0x53, 0x10, - 0x05, 0x12, 0x1f, 0x0a, 0x1b, 0x46, 0x41, 0x49, 0x4c, 0x45, 0x44, 0x5f, 0x49, 0x4e, 0x53, 0x55, - 0x46, 0x46, 0x49, 0x43, 0x49, 0x45, 0x4e, 0x54, 0x5f, 0x42, 0x41, 0x4c, 0x41, 0x4e, 0x43, 0x45, - 0x10, 0x06, 0x2a, 0x3c, 0x0a, 0x18, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x48, 0x6f, 0x6c, - 0x64, 0x46, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x0a, - 0x0a, 0x06, 0x53, 0x45, 0x54, 0x54, 0x4c, 0x45, 0x10, 0x00, 0x12, 0x08, 0x0a, 0x04, 0x46, 0x41, - 0x49, 0x4c, 0x10, 0x01, 0x12, 0x0a, 0x0a, 0x06, 0x52, 0x45, 0x53, 0x55, 0x4d, 0x45, 0x10, 0x02, - 0x2a, 0x35, 0x0a, 0x10, 0x43, 0x68, 0x61, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x41, 0x63, - 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x0a, 0x0a, 0x06, 0x45, 0x4e, 0x41, 0x42, 0x4c, 0x45, 0x10, 0x00, - 0x12, 0x0b, 0x0a, 0x07, 0x44, 0x49, 0x53, 0x41, 0x42, 0x4c, 0x45, 0x10, 0x01, 0x12, 0x08, 0x0a, - 0x04, 0x41, 0x55, 0x54, 0x4f, 0x10, 0x02, 0x32, 0xe8, 0x0d, 0x0a, 0x06, 0x52, 0x6f, 0x75, 0x74, - 0x65, 0x72, 0x12, 0x40, 0x0a, 0x0d, 0x53, 0x65, 0x6e, 0x64, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, - 0x74, 0x56, 0x32, 0x12, 0x1d, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, - 0x53, 0x65, 0x6e, 0x64, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x0e, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x61, 0x79, 0x6d, 0x65, - 0x6e, 0x74, 0x30, 0x01, 0x12, 0x42, 0x0a, 0x0e, 0x54, 0x72, 0x61, 0x63, 0x6b, 0x50, 0x61, 0x79, - 0x6d, 0x65, 0x6e, 0x74, 0x56, 0x32, 0x12, 0x1e, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, - 0x70, 0x63, 0x2e, 0x54, 0x72, 0x61, 0x63, 0x6b, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x0e, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x50, - 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x30, 0x01, 0x12, 0x42, 0x0a, 0x0d, 0x54, 0x72, 0x61, 0x63, - 0x6b, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x1f, 0x2e, 0x72, 0x6f, 0x75, 0x74, - 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x54, 0x72, 0x61, 0x63, 0x6b, 0x50, 0x61, 0x79, 0x6d, 0x65, - 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x0e, 0x2e, 0x6c, 0x6e, 0x72, - 0x70, 0x63, 0x2e, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x30, 0x01, 0x12, 0x4b, 0x0a, 0x10, - 0x45, 0x73, 0x74, 0x69, 0x6d, 0x61, 0x74, 0x65, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x46, 0x65, 0x65, - 0x12, 0x1a, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x6f, 0x75, - 0x74, 0x65, 0x46, 0x65, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x72, - 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x46, 0x65, - 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x51, 0x0a, 0x0b, 0x53, 0x65, 0x6e, - 0x64, 0x54, 0x6f, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x12, 0x1d, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, - 0x72, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x54, 0x6f, 0x52, 0x6f, 0x75, 0x74, 0x65, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, - 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x54, 0x6f, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x03, 0x88, 0x02, 0x01, 0x12, 0x42, 0x0a, 0x0d, - 0x53, 0x65, 0x6e, 0x64, 0x54, 0x6f, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x56, 0x32, 0x12, 0x1d, 0x2e, + 0x61, 0x70, 0x52, 0x09, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x4d, 0x61, 0x70, 0x73, 0x22, 0x46, 0x0a, + 0x14, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x65, 0x73, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2e, 0x0a, 0x0a, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x5f, 0x6d, + 0x61, 0x70, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x6c, 0x6e, 0x72, 0x70, + 0x63, 0x2e, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x4d, 0x61, 0x70, 0x52, 0x09, 0x61, 0x6c, 0x69, 0x61, + 0x73, 0x4d, 0x61, 0x70, 0x73, 0x22, 0x47, 0x0a, 0x15, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x41, + 0x6c, 0x69, 0x61, 0x73, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2e, + 0x0a, 0x0a, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x5f, 0x6d, 0x61, 0x70, 0x73, 0x18, 0x01, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x6c, 0x69, 0x61, 0x73, + 0x4d, 0x61, 0x70, 0x52, 0x09, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x4d, 0x61, 0x70, 0x73, 0x2a, 0x81, + 0x04, 0x0a, 0x0d, 0x46, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, + 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x0d, 0x0a, + 0x09, 0x4e, 0x4f, 0x5f, 0x44, 0x45, 0x54, 0x41, 0x49, 0x4c, 0x10, 0x01, 0x12, 0x10, 0x0a, 0x0c, + 0x4f, 0x4e, 0x49, 0x4f, 0x4e, 0x5f, 0x44, 0x45, 0x43, 0x4f, 0x44, 0x45, 0x10, 0x02, 0x12, 0x15, + 0x0a, 0x11, 0x4c, 0x49, 0x4e, 0x4b, 0x5f, 0x4e, 0x4f, 0x54, 0x5f, 0x45, 0x4c, 0x49, 0x47, 0x49, + 0x42, 0x4c, 0x45, 0x10, 0x03, 0x12, 0x14, 0x0a, 0x10, 0x4f, 0x4e, 0x5f, 0x43, 0x48, 0x41, 0x49, + 0x4e, 0x5f, 0x54, 0x49, 0x4d, 0x45, 0x4f, 0x55, 0x54, 0x10, 0x04, 0x12, 0x14, 0x0a, 0x10, 0x48, + 0x54, 0x4c, 0x43, 0x5f, 0x45, 0x58, 0x43, 0x45, 0x45, 0x44, 0x53, 0x5f, 0x4d, 0x41, 0x58, 0x10, + 0x05, 0x12, 0x18, 0x0a, 0x14, 0x49, 0x4e, 0x53, 0x55, 0x46, 0x46, 0x49, 0x43, 0x49, 0x45, 0x4e, + 0x54, 0x5f, 0x42, 0x41, 0x4c, 0x41, 0x4e, 0x43, 0x45, 0x10, 0x06, 0x12, 0x16, 0x0a, 0x12, 0x49, + 0x4e, 0x43, 0x4f, 0x4d, 0x50, 0x4c, 0x45, 0x54, 0x45, 0x5f, 0x46, 0x4f, 0x52, 0x57, 0x41, 0x52, + 0x44, 0x10, 0x07, 0x12, 0x13, 0x0a, 0x0f, 0x48, 0x54, 0x4c, 0x43, 0x5f, 0x41, 0x44, 0x44, 0x5f, + 0x46, 0x41, 0x49, 0x4c, 0x45, 0x44, 0x10, 0x08, 0x12, 0x15, 0x0a, 0x11, 0x46, 0x4f, 0x52, 0x57, + 0x41, 0x52, 0x44, 0x53, 0x5f, 0x44, 0x49, 0x53, 0x41, 0x42, 0x4c, 0x45, 0x44, 0x10, 0x09, 0x12, + 0x14, 0x0a, 0x10, 0x49, 0x4e, 0x56, 0x4f, 0x49, 0x43, 0x45, 0x5f, 0x43, 0x41, 0x4e, 0x43, 0x45, + 0x4c, 0x45, 0x44, 0x10, 0x0a, 0x12, 0x15, 0x0a, 0x11, 0x49, 0x4e, 0x56, 0x4f, 0x49, 0x43, 0x45, + 0x5f, 0x55, 0x4e, 0x44, 0x45, 0x52, 0x50, 0x41, 0x49, 0x44, 0x10, 0x0b, 0x12, 0x1b, 0x0a, 0x17, + 0x49, 0x4e, 0x56, 0x4f, 0x49, 0x43, 0x45, 0x5f, 0x45, 0x58, 0x50, 0x49, 0x52, 0x59, 0x5f, 0x54, + 0x4f, 0x4f, 0x5f, 0x53, 0x4f, 0x4f, 0x4e, 0x10, 0x0c, 0x12, 0x14, 0x0a, 0x10, 0x49, 0x4e, 0x56, + 0x4f, 0x49, 0x43, 0x45, 0x5f, 0x4e, 0x4f, 0x54, 0x5f, 0x4f, 0x50, 0x45, 0x4e, 0x10, 0x0d, 0x12, + 0x17, 0x0a, 0x13, 0x4d, 0x50, 0x50, 0x5f, 0x49, 0x4e, 0x56, 0x4f, 0x49, 0x43, 0x45, 0x5f, 0x54, + 0x49, 0x4d, 0x45, 0x4f, 0x55, 0x54, 0x10, 0x0e, 0x12, 0x14, 0x0a, 0x10, 0x41, 0x44, 0x44, 0x52, + 0x45, 0x53, 0x53, 0x5f, 0x4d, 0x49, 0x53, 0x4d, 0x41, 0x54, 0x43, 0x48, 0x10, 0x0f, 0x12, 0x16, + 0x0a, 0x12, 0x53, 0x45, 0x54, 0x5f, 0x54, 0x4f, 0x54, 0x41, 0x4c, 0x5f, 0x4d, 0x49, 0x53, 0x4d, + 0x41, 0x54, 0x43, 0x48, 0x10, 0x10, 0x12, 0x15, 0x0a, 0x11, 0x53, 0x45, 0x54, 0x5f, 0x54, 0x4f, + 0x54, 0x41, 0x4c, 0x5f, 0x54, 0x4f, 0x4f, 0x5f, 0x4c, 0x4f, 0x57, 0x10, 0x11, 0x12, 0x10, 0x0a, + 0x0c, 0x53, 0x45, 0x54, 0x5f, 0x4f, 0x56, 0x45, 0x52, 0x50, 0x41, 0x49, 0x44, 0x10, 0x12, 0x12, + 0x13, 0x0a, 0x0f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x5f, 0x49, 0x4e, 0x56, 0x4f, 0x49, + 0x43, 0x45, 0x10, 0x13, 0x12, 0x13, 0x0a, 0x0f, 0x49, 0x4e, 0x56, 0x41, 0x4c, 0x49, 0x44, 0x5f, + 0x4b, 0x45, 0x59, 0x53, 0x45, 0x4e, 0x44, 0x10, 0x14, 0x12, 0x13, 0x0a, 0x0f, 0x4d, 0x50, 0x50, + 0x5f, 0x49, 0x4e, 0x5f, 0x50, 0x52, 0x4f, 0x47, 0x52, 0x45, 0x53, 0x53, 0x10, 0x15, 0x12, 0x12, + 0x0a, 0x0e, 0x43, 0x49, 0x52, 0x43, 0x55, 0x4c, 0x41, 0x52, 0x5f, 0x52, 0x4f, 0x55, 0x54, 0x45, + 0x10, 0x16, 0x2a, 0xae, 0x01, 0x0a, 0x0c, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x53, 0x74, + 0x61, 0x74, 0x65, 0x12, 0x0d, 0x0a, 0x09, 0x49, 0x4e, 0x5f, 0x46, 0x4c, 0x49, 0x47, 0x48, 0x54, + 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x53, 0x55, 0x43, 0x43, 0x45, 0x45, 0x44, 0x45, 0x44, 0x10, + 0x01, 0x12, 0x12, 0x0a, 0x0e, 0x46, 0x41, 0x49, 0x4c, 0x45, 0x44, 0x5f, 0x54, 0x49, 0x4d, 0x45, + 0x4f, 0x55, 0x54, 0x10, 0x02, 0x12, 0x13, 0x0a, 0x0f, 0x46, 0x41, 0x49, 0x4c, 0x45, 0x44, 0x5f, + 0x4e, 0x4f, 0x5f, 0x52, 0x4f, 0x55, 0x54, 0x45, 0x10, 0x03, 0x12, 0x10, 0x0a, 0x0c, 0x46, 0x41, + 0x49, 0x4c, 0x45, 0x44, 0x5f, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, 0x04, 0x12, 0x24, 0x0a, 0x20, + 0x46, 0x41, 0x49, 0x4c, 0x45, 0x44, 0x5f, 0x49, 0x4e, 0x43, 0x4f, 0x52, 0x52, 0x45, 0x43, 0x54, + 0x5f, 0x50, 0x41, 0x59, 0x4d, 0x45, 0x4e, 0x54, 0x5f, 0x44, 0x45, 0x54, 0x41, 0x49, 0x4c, 0x53, + 0x10, 0x05, 0x12, 0x1f, 0x0a, 0x1b, 0x46, 0x41, 0x49, 0x4c, 0x45, 0x44, 0x5f, 0x49, 0x4e, 0x53, + 0x55, 0x46, 0x46, 0x49, 0x43, 0x49, 0x45, 0x4e, 0x54, 0x5f, 0x42, 0x41, 0x4c, 0x41, 0x4e, 0x43, + 0x45, 0x10, 0x06, 0x2a, 0x51, 0x0a, 0x18, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x48, 0x6f, + 0x6c, 0x64, 0x46, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, + 0x0a, 0x0a, 0x06, 0x53, 0x45, 0x54, 0x54, 0x4c, 0x45, 0x10, 0x00, 0x12, 0x08, 0x0a, 0x04, 0x46, + 0x41, 0x49, 0x4c, 0x10, 0x01, 0x12, 0x0a, 0x0a, 0x06, 0x52, 0x45, 0x53, 0x55, 0x4d, 0x45, 0x10, + 0x02, 0x12, 0x13, 0x0a, 0x0f, 0x52, 0x45, 0x53, 0x55, 0x4d, 0x45, 0x5f, 0x4d, 0x4f, 0x44, 0x49, + 0x46, 0x49, 0x45, 0x44, 0x10, 0x03, 0x2a, 0x35, 0x0a, 0x10, 0x43, 0x68, 0x61, 0x6e, 0x53, 0x74, + 0x61, 0x74, 0x75, 0x73, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x0a, 0x0a, 0x06, 0x45, 0x4e, + 0x41, 0x42, 0x4c, 0x45, 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x44, 0x49, 0x53, 0x41, 0x42, 0x4c, + 0x45, 0x10, 0x01, 0x12, 0x08, 0x0a, 0x04, 0x41, 0x55, 0x54, 0x4f, 0x10, 0x02, 0x32, 0xe8, 0x0d, + 0x0a, 0x06, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x12, 0x40, 0x0a, 0x0d, 0x53, 0x65, 0x6e, 0x64, + 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x56, 0x32, 0x12, 0x1d, 0x2e, 0x72, 0x6f, 0x75, 0x74, + 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, + 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x0e, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, + 0x2e, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x30, 0x01, 0x12, 0x42, 0x0a, 0x0e, 0x54, 0x72, + 0x61, 0x63, 0x6b, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x56, 0x32, 0x12, 0x1e, 0x2e, 0x72, + 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x54, 0x72, 0x61, 0x63, 0x6b, 0x50, 0x61, + 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x0e, 0x2e, 0x6c, + 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x30, 0x01, 0x12, 0x42, + 0x0a, 0x0d, 0x54, 0x72, 0x61, 0x63, 0x6b, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x12, + 0x1f, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x54, 0x72, 0x61, 0x63, + 0x6b, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x0e, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, + 0x30, 0x01, 0x12, 0x4b, 0x0a, 0x10, 0x45, 0x73, 0x74, 0x69, 0x6d, 0x61, 0x74, 0x65, 0x52, 0x6f, + 0x75, 0x74, 0x65, 0x46, 0x65, 0x65, 0x12, 0x1a, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, + 0x70, 0x63, 0x2e, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x46, 0x65, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x52, + 0x6f, 0x75, 0x74, 0x65, 0x46, 0x65, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x51, 0x0a, 0x0b, 0x53, 0x65, 0x6e, 0x64, 0x54, 0x6f, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x12, 0x1d, + 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x54, + 0x6f, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x54, 0x6f, - 0x52, 0x6f, 0x75, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x12, 0x2e, 0x6c, - 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x48, 0x54, 0x4c, 0x43, 0x41, 0x74, 0x74, 0x65, 0x6d, 0x70, 0x74, - 0x12, 0x64, 0x0a, 0x13, 0x52, 0x65, 0x73, 0x65, 0x74, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, - 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x12, 0x25, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, - 0x72, 0x70, 0x63, 0x2e, 0x52, 0x65, 0x73, 0x65, 0x74, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, - 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, - 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x65, 0x73, 0x65, 0x74, - 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x64, 0x0a, 0x13, 0x51, 0x75, 0x65, 0x72, 0x79, 0x4d, + 0x52, 0x6f, 0x75, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x03, 0x88, + 0x02, 0x01, 0x12, 0x42, 0x0a, 0x0d, 0x53, 0x65, 0x6e, 0x64, 0x54, 0x6f, 0x52, 0x6f, 0x75, 0x74, + 0x65, 0x56, 0x32, 0x12, 0x1d, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, + 0x53, 0x65, 0x6e, 0x64, 0x54, 0x6f, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x12, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x48, 0x54, 0x4c, 0x43, 0x41, + 0x74, 0x74, 0x65, 0x6d, 0x70, 0x74, 0x12, 0x64, 0x0a, 0x13, 0x52, 0x65, 0x73, 0x65, 0x74, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x12, 0x25, 0x2e, - 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x4d, + 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x65, 0x73, 0x65, 0x74, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, - 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x6e, - 0x74, 0x72, 0x6f, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x6a, 0x0a, 0x15, - 0x58, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x6f, - 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x12, 0x27, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, - 0x63, 0x2e, 0x58, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, - 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, - 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x58, 0x49, 0x6d, 0x70, 0x6f, - 0x72, 0x74, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x70, 0x0a, 0x17, 0x47, 0x65, 0x74, 0x4d, - 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x43, 0x6f, 0x6e, - 0x66, 0x69, 0x67, 0x12, 0x29, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, - 0x47, 0x65, 0x74, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, - 0x6c, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, - 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4d, 0x69, - 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x43, 0x6f, 0x6e, 0x66, - 0x69, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x70, 0x0a, 0x17, 0x53, 0x65, - 0x74, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x43, - 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x29, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, - 0x63, 0x2e, 0x53, 0x65, 0x74, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x6e, 0x74, - 0x72, 0x6f, 0x6c, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x2a, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x74, - 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x43, 0x6f, - 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5b, 0x0a, 0x10, - 0x51, 0x75, 0x65, 0x72, 0x79, 0x50, 0x72, 0x6f, 0x62, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, - 0x12, 0x22, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x51, 0x75, 0x65, - 0x72, 0x79, 0x50, 0x72, 0x6f, 0x62, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, - 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x50, 0x72, 0x6f, 0x62, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, - 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x49, 0x0a, 0x0a, 0x42, 0x75, 0x69, - 0x6c, 0x64, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x12, 0x1c, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, - 0x72, 0x70, 0x63, 0x2e, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, - 0x63, 0x2e, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x54, 0x0a, 0x13, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, - 0x65, 0x48, 0x74, 0x6c, 0x63, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x25, 0x2e, 0x72, 0x6f, - 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, - 0x65, 0x48, 0x74, 0x6c, 0x63, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x14, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x48, - 0x74, 0x6c, 0x63, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x30, 0x01, 0x12, 0x4d, 0x0a, 0x0b, 0x53, 0x65, - 0x6e, 0x64, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x1d, 0x2e, 0x72, 0x6f, 0x75, 0x74, - 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, - 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, - 0x72, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x53, 0x74, 0x61, 0x74, - 0x75, 0x73, 0x22, 0x03, 0x88, 0x02, 0x01, 0x30, 0x01, 0x12, 0x4f, 0x0a, 0x0c, 0x54, 0x72, 0x61, - 0x63, 0x6b, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x1e, 0x2e, 0x72, 0x6f, 0x75, 0x74, - 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x54, 0x72, 0x61, 0x63, 0x6b, 0x50, 0x61, 0x79, 0x6d, 0x65, - 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x72, 0x6f, 0x75, 0x74, - 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x53, 0x74, 0x61, - 0x74, 0x75, 0x73, 0x22, 0x03, 0x88, 0x02, 0x01, 0x30, 0x01, 0x12, 0x66, 0x0a, 0x0f, 0x48, 0x74, - 0x6c, 0x63, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x12, 0x27, 0x2e, - 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x6f, 0x72, 0x77, 0x61, 0x72, - 0x64, 0x48, 0x74, 0x6c, 0x63, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x1a, 0x26, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, - 0x70, 0x63, 0x2e, 0x46, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x48, 0x74, 0x6c, 0x63, 0x49, 0x6e, - 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x28, 0x01, - 0x30, 0x01, 0x12, 0x5b, 0x0a, 0x10, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x43, 0x68, 0x61, 0x6e, - 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x22, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, - 0x70, 0x63, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x53, 0x74, 0x61, - 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x72, 0x6f, 0x75, - 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x43, 0x68, 0x61, - 0x6e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, - 0x53, 0x0a, 0x14, 0x58, 0x41, 0x64, 0x64, 0x4c, 0x6f, 0x63, 0x61, 0x6c, 0x43, 0x68, 0x61, 0x6e, - 0x41, 0x6c, 0x69, 0x61, 0x73, 0x65, 0x73, 0x12, 0x1c, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, - 0x72, 0x70, 0x63, 0x2e, 0x41, 0x64, 0x64, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x65, 0x73, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, - 0x63, 0x2e, 0x41, 0x64, 0x64, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5c, 0x0a, 0x17, 0x58, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4c, - 0x6f, 0x63, 0x61, 0x6c, 0x43, 0x68, 0x61, 0x6e, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x65, 0x73, 0x12, - 0x1f, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x44, 0x65, 0x6c, 0x65, - 0x74, 0x65, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x20, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x44, 0x65, 0x6c, - 0x65, 0x74, 0x65, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x42, 0x31, 0x5a, 0x2f, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, - 0x2f, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x6e, 0x69, 0x6e, 0x67, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, - 0x6b, 0x2f, 0x6c, 0x6e, 0x64, 0x2f, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2f, 0x72, 0x6f, 0x75, 0x74, - 0x65, 0x72, 0x72, 0x70, 0x63, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x2e, 0x52, 0x65, 0x73, 0x65, 0x74, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x6e, + 0x74, 0x72, 0x6f, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x64, 0x0a, 0x13, + 0x51, 0x75, 0x65, 0x72, 0x79, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x6e, 0x74, + 0x72, 0x6f, 0x6c, 0x12, 0x25, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, + 0x51, 0x75, 0x65, 0x72, 0x79, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x6e, 0x74, + 0x72, 0x6f, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x72, 0x6f, 0x75, + 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x4d, 0x69, 0x73, 0x73, + 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x6a, 0x0a, 0x15, 0x58, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x4d, 0x69, 0x73, + 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x12, 0x27, 0x2e, 0x72, 0x6f, + 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x58, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x4d, + 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, + 0x2e, 0x58, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x43, + 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x70, + 0x0a, 0x17, 0x47, 0x65, 0x74, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x6e, 0x74, + 0x72, 0x6f, 0x6c, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x29, 0x2e, 0x72, 0x6f, 0x75, 0x74, + 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, + 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, + 0x2e, 0x47, 0x65, 0x74, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x6e, 0x74, 0x72, + 0x6f, 0x6c, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x70, 0x0a, 0x17, 0x53, 0x65, 0x74, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x6f, + 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x29, 0x2e, 0x72, 0x6f, + 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x74, 0x4d, 0x69, 0x73, 0x73, 0x69, + 0x6f, 0x6e, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, + 0x70, 0x63, 0x2e, 0x53, 0x65, 0x74, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x6e, + 0x74, 0x72, 0x6f, 0x6c, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x5b, 0x0a, 0x10, 0x51, 0x75, 0x65, 0x72, 0x79, 0x50, 0x72, 0x6f, 0x62, 0x61, + 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x12, 0x22, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, + 0x70, 0x63, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x50, 0x72, 0x6f, 0x62, 0x61, 0x62, 0x69, 0x6c, + 0x69, 0x74, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x72, 0x6f, 0x75, + 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x50, 0x72, 0x6f, 0x62, + 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x49, 0x0a, 0x0a, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x12, 0x1c, 0x2e, + 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x52, + 0x6f, 0x75, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x72, 0x6f, + 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x52, 0x6f, 0x75, + 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x54, 0x0a, 0x13, 0x53, 0x75, + 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x48, 0x74, 0x6c, 0x63, 0x45, 0x76, 0x65, 0x6e, 0x74, + 0x73, 0x12, 0x25, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x75, + 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x48, 0x74, 0x6c, 0x63, 0x45, 0x76, 0x65, 0x6e, 0x74, + 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x14, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, + 0x72, 0x72, 0x70, 0x63, 0x2e, 0x48, 0x74, 0x6c, 0x63, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x30, 0x01, + 0x12, 0x4d, 0x0a, 0x0b, 0x53, 0x65, 0x6e, 0x64, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x12, + 0x1d, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x6e, 0x64, + 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, + 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x61, 0x79, 0x6d, 0x65, + 0x6e, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x03, 0x88, 0x02, 0x01, 0x30, 0x01, 0x12, + 0x4f, 0x0a, 0x0c, 0x54, 0x72, 0x61, 0x63, 0x6b, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x12, + 0x1e, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x54, 0x72, 0x61, 0x63, + 0x6b, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x18, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x61, 0x79, 0x6d, + 0x65, 0x6e, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x03, 0x88, 0x02, 0x01, 0x30, 0x01, + 0x12, 0x66, 0x0a, 0x0f, 0x48, 0x74, 0x6c, 0x63, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, + 0x74, 0x6f, 0x72, 0x12, 0x27, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, + 0x46, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x48, 0x74, 0x6c, 0x63, 0x49, 0x6e, 0x74, 0x65, 0x72, + 0x63, 0x65, 0x70, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x1a, 0x26, 0x2e, 0x72, + 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, + 0x48, 0x74, 0x6c, 0x63, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x28, 0x01, 0x30, 0x01, 0x12, 0x5b, 0x0a, 0x10, 0x55, 0x70, 0x64, 0x61, + 0x74, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x22, 0x2e, 0x72, + 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x43, + 0x68, 0x61, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x23, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x55, 0x70, 0x64, + 0x61, 0x74, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x53, 0x0a, 0x14, 0x58, 0x41, 0x64, 0x64, 0x4c, 0x6f, 0x63, + 0x61, 0x6c, 0x43, 0x68, 0x61, 0x6e, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x65, 0x73, 0x12, 0x1c, 0x2e, + 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x64, 0x64, 0x41, 0x6c, 0x69, + 0x61, 0x73, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x72, 0x6f, + 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x64, 0x64, 0x41, 0x6c, 0x69, 0x61, 0x73, + 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5c, 0x0a, 0x17, 0x58, 0x44, + 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4c, 0x6f, 0x63, 0x61, 0x6c, 0x43, 0x68, 0x61, 0x6e, 0x41, 0x6c, + 0x69, 0x61, 0x73, 0x65, 0x73, 0x12, 0x1f, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, + 0x63, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x65, 0x73, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, + 0x70, 0x63, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x65, 0x73, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x31, 0x5a, 0x2f, 0x67, 0x69, 0x74, 0x68, + 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x6e, 0x69, 0x6e, 0x67, + 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x2f, 0x6c, 0x6e, 0x64, 0x2f, 0x6c, 0x6e, 0x72, 0x70, + 0x63, 0x2f, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x62, 0x06, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x33, } var ( @@ -4157,7 +4219,7 @@ func file_routerrpc_router_proto_rawDescGZIP() []byte { } var file_routerrpc_router_proto_enumTypes = make([]protoimpl.EnumInfo, 6) -var file_routerrpc_router_proto_msgTypes = make([]protoimpl.MessageInfo, 47) +var file_routerrpc_router_proto_msgTypes = make([]protoimpl.MessageInfo, 48) var file_routerrpc_router_proto_goTypes = []interface{}{ (FailureDetail)(0), // 0: routerrpc.FailureDetail (PaymentState)(0), // 1: routerrpc.PaymentState @@ -4212,24 +4274,25 @@ var file_routerrpc_router_proto_goTypes = []interface{}{ (*DeleteAliasesResponse)(nil), // 50: routerrpc.DeleteAliasesResponse nil, // 51: routerrpc.SendPaymentRequest.DestCustomRecordsEntry nil, // 52: routerrpc.ForwardHtlcInterceptRequest.CustomRecordsEntry - (*lnrpc.RouteHint)(nil), // 53: lnrpc.RouteHint - (lnrpc.FeatureBit)(0), // 54: lnrpc.FeatureBit - (lnrpc.PaymentFailureReason)(0), // 55: lnrpc.PaymentFailureReason - (*lnrpc.Route)(nil), // 56: lnrpc.Route - (*lnrpc.Failure)(nil), // 57: lnrpc.Failure - (lnrpc.Failure_FailureCode)(0), // 58: lnrpc.Failure.FailureCode - (*lnrpc.HTLCAttempt)(nil), // 59: lnrpc.HTLCAttempt - (*lnrpc.ChannelPoint)(nil), // 60: lnrpc.ChannelPoint - (*lnrpc.AliasMap)(nil), // 61: lnrpc.AliasMap - (*lnrpc.Payment)(nil), // 62: lnrpc.Payment + nil, // 53: routerrpc.ForwardHtlcInterceptResponse.OutWireCustomRecordsEntry + (*lnrpc.RouteHint)(nil), // 54: lnrpc.RouteHint + (lnrpc.FeatureBit)(0), // 55: lnrpc.FeatureBit + (lnrpc.PaymentFailureReason)(0), // 56: lnrpc.PaymentFailureReason + (*lnrpc.Route)(nil), // 57: lnrpc.Route + (*lnrpc.Failure)(nil), // 58: lnrpc.Failure + (lnrpc.Failure_FailureCode)(0), // 59: lnrpc.Failure.FailureCode + (*lnrpc.HTLCAttempt)(nil), // 60: lnrpc.HTLCAttempt + (*lnrpc.ChannelPoint)(nil), // 61: lnrpc.ChannelPoint + (*lnrpc.AliasMap)(nil), // 62: lnrpc.AliasMap + (*lnrpc.Payment)(nil), // 63: lnrpc.Payment } var file_routerrpc_router_proto_depIdxs = []int32{ - 53, // 0: routerrpc.SendPaymentRequest.route_hints:type_name -> lnrpc.RouteHint + 54, // 0: routerrpc.SendPaymentRequest.route_hints:type_name -> lnrpc.RouteHint 51, // 1: routerrpc.SendPaymentRequest.dest_custom_records:type_name -> routerrpc.SendPaymentRequest.DestCustomRecordsEntry - 54, // 2: routerrpc.SendPaymentRequest.dest_features:type_name -> lnrpc.FeatureBit - 55, // 3: routerrpc.RouteFeeResponse.failure_reason:type_name -> lnrpc.PaymentFailureReason - 56, // 4: routerrpc.SendToRouteRequest.route:type_name -> lnrpc.Route - 57, // 5: routerrpc.SendToRouteResponse.failure:type_name -> lnrpc.Failure + 55, // 2: routerrpc.SendPaymentRequest.dest_features:type_name -> lnrpc.FeatureBit + 56, // 3: routerrpc.RouteFeeResponse.failure_reason:type_name -> lnrpc.PaymentFailureReason + 57, // 4: routerrpc.SendToRouteRequest.route:type_name -> lnrpc.Route + 58, // 5: routerrpc.SendToRouteResponse.failure:type_name -> lnrpc.Failure 19, // 6: routerrpc.QueryMissionControlResponse.pairs:type_name -> routerrpc.PairHistory 19, // 7: routerrpc.XImportMissionControlRequest.pairs:type_name -> routerrpc.PairHistory 20, // 8: routerrpc.PairHistory.history:type_name -> routerrpc.PairData @@ -4239,7 +4302,7 @@ var file_routerrpc_router_proto_depIdxs = []int32{ 27, // 12: routerrpc.MissionControlConfig.apriori:type_name -> routerrpc.AprioriParameters 26, // 13: routerrpc.MissionControlConfig.bimodal:type_name -> routerrpc.BimodalParameters 20, // 14: routerrpc.QueryProbabilityResponse.history:type_name -> routerrpc.PairData - 56, // 15: routerrpc.BuildRouteResponse.route:type_name -> lnrpc.Route + 57, // 15: routerrpc.BuildRouteResponse.route:type_name -> lnrpc.Route 5, // 16: routerrpc.HtlcEvent.event_type:type_name -> routerrpc.HtlcEvent.EventType 35, // 17: routerrpc.HtlcEvent.forward_event:type_name -> routerrpc.ForwardEvent 36, // 18: routerrpc.HtlcEvent.forward_fail_event:type_name -> routerrpc.ForwardFailEvent @@ -4249,66 +4312,67 @@ var file_routerrpc_router_proto_depIdxs = []int32{ 38, // 22: routerrpc.HtlcEvent.final_htlc_event:type_name -> routerrpc.FinalHtlcEvent 34, // 23: routerrpc.ForwardEvent.info:type_name -> routerrpc.HtlcInfo 34, // 24: routerrpc.LinkFailEvent.info:type_name -> routerrpc.HtlcInfo - 58, // 25: routerrpc.LinkFailEvent.wire_failure:type_name -> lnrpc.Failure.FailureCode + 59, // 25: routerrpc.LinkFailEvent.wire_failure:type_name -> lnrpc.Failure.FailureCode 0, // 26: routerrpc.LinkFailEvent.failure_detail:type_name -> routerrpc.FailureDetail 1, // 27: routerrpc.PaymentStatus.state:type_name -> routerrpc.PaymentState - 59, // 28: routerrpc.PaymentStatus.htlcs:type_name -> lnrpc.HTLCAttempt + 60, // 28: routerrpc.PaymentStatus.htlcs:type_name -> lnrpc.HTLCAttempt 42, // 29: routerrpc.ForwardHtlcInterceptRequest.incoming_circuit_key:type_name -> routerrpc.CircuitKey 52, // 30: routerrpc.ForwardHtlcInterceptRequest.custom_records:type_name -> routerrpc.ForwardHtlcInterceptRequest.CustomRecordsEntry 42, // 31: routerrpc.ForwardHtlcInterceptResponse.incoming_circuit_key:type_name -> routerrpc.CircuitKey 2, // 32: routerrpc.ForwardHtlcInterceptResponse.action:type_name -> routerrpc.ResolveHoldForwardAction - 58, // 33: routerrpc.ForwardHtlcInterceptResponse.failure_code:type_name -> lnrpc.Failure.FailureCode - 60, // 34: routerrpc.UpdateChanStatusRequest.chan_point:type_name -> lnrpc.ChannelPoint - 3, // 35: routerrpc.UpdateChanStatusRequest.action:type_name -> routerrpc.ChanStatusAction - 61, // 36: routerrpc.AddAliasesRequest.alias_maps:type_name -> lnrpc.AliasMap - 61, // 37: routerrpc.AddAliasesResponse.alias_maps:type_name -> lnrpc.AliasMap - 61, // 38: routerrpc.DeleteAliasesRequest.alias_maps:type_name -> lnrpc.AliasMap - 61, // 39: routerrpc.DeleteAliasesResponse.alias_maps:type_name -> lnrpc.AliasMap - 6, // 40: routerrpc.Router.SendPaymentV2:input_type -> routerrpc.SendPaymentRequest - 7, // 41: routerrpc.Router.TrackPaymentV2:input_type -> routerrpc.TrackPaymentRequest - 8, // 42: routerrpc.Router.TrackPayments:input_type -> routerrpc.TrackPaymentsRequest - 9, // 43: routerrpc.Router.EstimateRouteFee:input_type -> routerrpc.RouteFeeRequest - 11, // 44: routerrpc.Router.SendToRoute:input_type -> routerrpc.SendToRouteRequest - 11, // 45: routerrpc.Router.SendToRouteV2:input_type -> routerrpc.SendToRouteRequest - 13, // 46: routerrpc.Router.ResetMissionControl:input_type -> routerrpc.ResetMissionControlRequest - 15, // 47: routerrpc.Router.QueryMissionControl:input_type -> routerrpc.QueryMissionControlRequest - 17, // 48: routerrpc.Router.XImportMissionControl:input_type -> routerrpc.XImportMissionControlRequest - 21, // 49: routerrpc.Router.GetMissionControlConfig:input_type -> routerrpc.GetMissionControlConfigRequest - 23, // 50: routerrpc.Router.SetMissionControlConfig:input_type -> routerrpc.SetMissionControlConfigRequest - 28, // 51: routerrpc.Router.QueryProbability:input_type -> routerrpc.QueryProbabilityRequest - 30, // 52: routerrpc.Router.BuildRoute:input_type -> routerrpc.BuildRouteRequest - 32, // 53: routerrpc.Router.SubscribeHtlcEvents:input_type -> routerrpc.SubscribeHtlcEventsRequest - 6, // 54: routerrpc.Router.SendPayment:input_type -> routerrpc.SendPaymentRequest - 7, // 55: routerrpc.Router.TrackPayment:input_type -> routerrpc.TrackPaymentRequest - 44, // 56: routerrpc.Router.HtlcInterceptor:input_type -> routerrpc.ForwardHtlcInterceptResponse - 45, // 57: routerrpc.Router.UpdateChanStatus:input_type -> routerrpc.UpdateChanStatusRequest - 47, // 58: routerrpc.Router.XAddLocalChanAliases:input_type -> routerrpc.AddAliasesRequest - 49, // 59: routerrpc.Router.XDeleteLocalChanAliases:input_type -> routerrpc.DeleteAliasesRequest - 62, // 60: routerrpc.Router.SendPaymentV2:output_type -> lnrpc.Payment - 62, // 61: routerrpc.Router.TrackPaymentV2:output_type -> lnrpc.Payment - 62, // 62: routerrpc.Router.TrackPayments:output_type -> lnrpc.Payment - 10, // 63: routerrpc.Router.EstimateRouteFee:output_type -> routerrpc.RouteFeeResponse - 12, // 64: routerrpc.Router.SendToRoute:output_type -> routerrpc.SendToRouteResponse - 59, // 65: routerrpc.Router.SendToRouteV2:output_type -> lnrpc.HTLCAttempt - 14, // 66: routerrpc.Router.ResetMissionControl:output_type -> routerrpc.ResetMissionControlResponse - 16, // 67: routerrpc.Router.QueryMissionControl:output_type -> routerrpc.QueryMissionControlResponse - 18, // 68: routerrpc.Router.XImportMissionControl:output_type -> routerrpc.XImportMissionControlResponse - 22, // 69: routerrpc.Router.GetMissionControlConfig:output_type -> routerrpc.GetMissionControlConfigResponse - 24, // 70: routerrpc.Router.SetMissionControlConfig:output_type -> routerrpc.SetMissionControlConfigResponse - 29, // 71: routerrpc.Router.QueryProbability:output_type -> routerrpc.QueryProbabilityResponse - 31, // 72: routerrpc.Router.BuildRoute:output_type -> routerrpc.BuildRouteResponse - 33, // 73: routerrpc.Router.SubscribeHtlcEvents:output_type -> routerrpc.HtlcEvent - 41, // 74: routerrpc.Router.SendPayment:output_type -> routerrpc.PaymentStatus - 41, // 75: routerrpc.Router.TrackPayment:output_type -> routerrpc.PaymentStatus - 43, // 76: routerrpc.Router.HtlcInterceptor:output_type -> routerrpc.ForwardHtlcInterceptRequest - 46, // 77: routerrpc.Router.UpdateChanStatus:output_type -> routerrpc.UpdateChanStatusResponse - 48, // 78: routerrpc.Router.XAddLocalChanAliases:output_type -> routerrpc.AddAliasesResponse - 50, // 79: routerrpc.Router.XDeleteLocalChanAliases:output_type -> routerrpc.DeleteAliasesResponse - 60, // [60:80] is the sub-list for method output_type - 40, // [40:60] is the sub-list for method input_type - 40, // [40:40] is the sub-list for extension type_name - 40, // [40:40] is the sub-list for extension extendee - 0, // [0:40] is the sub-list for field type_name + 59, // 33: routerrpc.ForwardHtlcInterceptResponse.failure_code:type_name -> lnrpc.Failure.FailureCode + 53, // 34: routerrpc.ForwardHtlcInterceptResponse.out_wire_custom_records:type_name -> routerrpc.ForwardHtlcInterceptResponse.OutWireCustomRecordsEntry + 61, // 35: routerrpc.UpdateChanStatusRequest.chan_point:type_name -> lnrpc.ChannelPoint + 3, // 36: routerrpc.UpdateChanStatusRequest.action:type_name -> routerrpc.ChanStatusAction + 62, // 37: routerrpc.AddAliasesRequest.alias_maps:type_name -> lnrpc.AliasMap + 62, // 38: routerrpc.AddAliasesResponse.alias_maps:type_name -> lnrpc.AliasMap + 62, // 39: routerrpc.DeleteAliasesRequest.alias_maps:type_name -> lnrpc.AliasMap + 62, // 40: routerrpc.DeleteAliasesResponse.alias_maps:type_name -> lnrpc.AliasMap + 6, // 41: routerrpc.Router.SendPaymentV2:input_type -> routerrpc.SendPaymentRequest + 7, // 42: routerrpc.Router.TrackPaymentV2:input_type -> routerrpc.TrackPaymentRequest + 8, // 43: routerrpc.Router.TrackPayments:input_type -> routerrpc.TrackPaymentsRequest + 9, // 44: routerrpc.Router.EstimateRouteFee:input_type -> routerrpc.RouteFeeRequest + 11, // 45: routerrpc.Router.SendToRoute:input_type -> routerrpc.SendToRouteRequest + 11, // 46: routerrpc.Router.SendToRouteV2:input_type -> routerrpc.SendToRouteRequest + 13, // 47: routerrpc.Router.ResetMissionControl:input_type -> routerrpc.ResetMissionControlRequest + 15, // 48: routerrpc.Router.QueryMissionControl:input_type -> routerrpc.QueryMissionControlRequest + 17, // 49: routerrpc.Router.XImportMissionControl:input_type -> routerrpc.XImportMissionControlRequest + 21, // 50: routerrpc.Router.GetMissionControlConfig:input_type -> routerrpc.GetMissionControlConfigRequest + 23, // 51: routerrpc.Router.SetMissionControlConfig:input_type -> routerrpc.SetMissionControlConfigRequest + 28, // 52: routerrpc.Router.QueryProbability:input_type -> routerrpc.QueryProbabilityRequest + 30, // 53: routerrpc.Router.BuildRoute:input_type -> routerrpc.BuildRouteRequest + 32, // 54: routerrpc.Router.SubscribeHtlcEvents:input_type -> routerrpc.SubscribeHtlcEventsRequest + 6, // 55: routerrpc.Router.SendPayment:input_type -> routerrpc.SendPaymentRequest + 7, // 56: routerrpc.Router.TrackPayment:input_type -> routerrpc.TrackPaymentRequest + 44, // 57: routerrpc.Router.HtlcInterceptor:input_type -> routerrpc.ForwardHtlcInterceptResponse + 45, // 58: routerrpc.Router.UpdateChanStatus:input_type -> routerrpc.UpdateChanStatusRequest + 47, // 59: routerrpc.Router.XAddLocalChanAliases:input_type -> routerrpc.AddAliasesRequest + 49, // 60: routerrpc.Router.XDeleteLocalChanAliases:input_type -> routerrpc.DeleteAliasesRequest + 63, // 61: routerrpc.Router.SendPaymentV2:output_type -> lnrpc.Payment + 63, // 62: routerrpc.Router.TrackPaymentV2:output_type -> lnrpc.Payment + 63, // 63: routerrpc.Router.TrackPayments:output_type -> lnrpc.Payment + 10, // 64: routerrpc.Router.EstimateRouteFee:output_type -> routerrpc.RouteFeeResponse + 12, // 65: routerrpc.Router.SendToRoute:output_type -> routerrpc.SendToRouteResponse + 60, // 66: routerrpc.Router.SendToRouteV2:output_type -> lnrpc.HTLCAttempt + 14, // 67: routerrpc.Router.ResetMissionControl:output_type -> routerrpc.ResetMissionControlResponse + 16, // 68: routerrpc.Router.QueryMissionControl:output_type -> routerrpc.QueryMissionControlResponse + 18, // 69: routerrpc.Router.XImportMissionControl:output_type -> routerrpc.XImportMissionControlResponse + 22, // 70: routerrpc.Router.GetMissionControlConfig:output_type -> routerrpc.GetMissionControlConfigResponse + 24, // 71: routerrpc.Router.SetMissionControlConfig:output_type -> routerrpc.SetMissionControlConfigResponse + 29, // 72: routerrpc.Router.QueryProbability:output_type -> routerrpc.QueryProbabilityResponse + 31, // 73: routerrpc.Router.BuildRoute:output_type -> routerrpc.BuildRouteResponse + 33, // 74: routerrpc.Router.SubscribeHtlcEvents:output_type -> routerrpc.HtlcEvent + 41, // 75: routerrpc.Router.SendPayment:output_type -> routerrpc.PaymentStatus + 41, // 76: routerrpc.Router.TrackPayment:output_type -> routerrpc.PaymentStatus + 43, // 77: routerrpc.Router.HtlcInterceptor:output_type -> routerrpc.ForwardHtlcInterceptRequest + 46, // 78: routerrpc.Router.UpdateChanStatus:output_type -> routerrpc.UpdateChanStatusResponse + 48, // 79: routerrpc.Router.XAddLocalChanAliases:output_type -> routerrpc.AddAliasesResponse + 50, // 80: routerrpc.Router.XDeleteLocalChanAliases:output_type -> routerrpc.DeleteAliasesResponse + 61, // [61:81] is the sub-list for method output_type + 41, // [41:61] is the sub-list for method input_type + 41, // [41:41] is the sub-list for extension type_name + 41, // [41:41] is the sub-list for extension extendee + 0, // [0:41] is the sub-list for field type_name } func init() { file_routerrpc_router_proto_init() } @@ -4876,7 +4940,7 @@ func file_routerrpc_router_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_routerrpc_router_proto_rawDesc, NumEnums: 6, - NumMessages: 47, + NumMessages: 48, NumExtensions: 0, NumServices: 1, }, diff --git a/lnrpc/routerrpc/router.proto b/lnrpc/routerrpc/router.proto index 03a481b97c..b9054efa06 100644 --- a/lnrpc/routerrpc/router.proto +++ b/lnrpc/routerrpc/router.proto @@ -988,6 +988,8 @@ message ForwardHtlcInterceptRequest { ForwardHtlcInterceptResponse enables the caller to resolve a previously hold forward. The caller can choose either to: - `Resume`: Execute the default behavior (usually forward). +- `ResumeModified`: Execute the default behavior (usually forward) with HTLC + field modifications. - `Reject`: Fail the htlc backwards. - `Settle`: Settle this htlc with a given preimage. */ @@ -1018,12 +1020,36 @@ message ForwardHtlcInterceptResponse { // For backwards-compatibility reasons, TEMPORARY_CHANNEL_FAILURE is the // default value for this field. lnrpc.Failure.FailureCode failure_code = 5; + + // The amount that was set on the p2p wire message of the incoming HTLC. + // This field is ignored if the action is not RESUME_MODIFIED or the amount + // is zero. + uint64 in_amount_msat = 6; + + // The amount to set on the p2p wire message of the resumed HTLC. This field + // is ignored if the action is not RESUME_MODIFIED or the amount is zero. + uint64 out_amount_msat = 7; + + // Any custom records that should be set on the p2p wire message message of + // the resumed HTLC. This field is ignored if the action is not + // RESUME_MODIFIED. + map out_wire_custom_records = 8; } enum ResolveHoldForwardAction { + // SETTLE is an action that is used to settle an HTLC instead of forwarding + // it. SETTLE = 0; + + // FAIL is an action that is used to fail an HTLC backwards. FAIL = 1; + + // RESUME is an action that is used to resume a forward HTLC. RESUME = 2; + + // RESUME_MODIFIED is an action that is used to resume a hold forward HTLC + // with modifications specified during interception. + RESUME_MODIFIED = 3; } message UpdateChanStatusRequest { diff --git a/lnrpc/routerrpc/router.swagger.json b/lnrpc/routerrpc/router.swagger.json index 5e9cc70c59..c469fd736c 100644 --- a/lnrpc/routerrpc/router.swagger.json +++ b/lnrpc/routerrpc/router.swagger.json @@ -1474,9 +1474,27 @@ "failure_code": { "$ref": "#/definitions/FailureFailureCode", "description": "Return the specified failure code in case the resolve action is Fail. The\nmessage data fields are populated automatically.\n\nIf a non-zero failure_code is specified, failure_message must not be set.\n\nFor backwards-compatibility reasons, TEMPORARY_CHANNEL_FAILURE is the\ndefault value for this field." + }, + "in_amount_msat": { + "type": "string", + "format": "uint64", + "description": "The amount that was set on the p2p wire message of the incoming HTLC.\nThis field is ignored if the action is not RESUME_MODIFIED or the amount\nis zero." + }, + "out_amount_msat": { + "type": "string", + "format": "uint64", + "description": "The amount to set on the p2p wire message of the resumed HTLC. This field\nis ignored if the action is not RESUME_MODIFIED or the amount is zero." + }, + "out_wire_custom_records": { + "type": "object", + "additionalProperties": { + "type": "string", + "format": "byte" + }, + "description": "Any custom records that should be set on the p2p wire message message of\nthe resumed HTLC. This field is ignored if the action is not\nRESUME_MODIFIED." } }, - "description": "*\nForwardHtlcInterceptResponse enables the caller to resolve a previously hold\nforward. The caller can choose either to:\n- `Resume`: Execute the default behavior (usually forward).\n- `Reject`: Fail the htlc backwards.\n- `Settle`: Settle this htlc with a given preimage." + "description": "*\nForwardHtlcInterceptResponse enables the caller to resolve a previously hold\nforward. The caller can choose either to:\n- `Resume`: Execute the default behavior (usually forward).\n- `ResumeModified`: Execute the default behavior (usually forward) with HTLC\nfield modifications.\n- `Reject`: Fail the htlc backwards.\n- `Settle`: Settle this htlc with a given preimage." }, "routerrpcGetMissionControlConfigResponse": { "type": "object", @@ -1763,9 +1781,11 @@ "enum": [ "SETTLE", "FAIL", - "RESUME" + "RESUME", + "RESUME_MODIFIED" ], - "default": "SETTLE" + "default": "SETTLE", + "description": " - SETTLE: SETTLE is an action that is used to settle an HTLC instead of forwarding\nit.\n - FAIL: FAIL is an action that is used to fail an HTLC backwards.\n - RESUME: RESUME is an action that is used to resume a forward HTLC.\n - RESUME_MODIFIED: RESUME_MODIFIED is an action that is used to resume a hold forward HTLC\nwith modifications specified during interception." }, "routerrpcRouteFeeRequest": { "type": "object", From 1b28f47c1d12c7b028b7ad6401deb67f87c0c37a Mon Sep 17 00:00:00 2001 From: ffranr Date: Sat, 13 Apr 2024 11:39:51 +0100 Subject: [PATCH 064/218] itest: add itest for field modification HTLC interception response Implement an integration test where an HTLC is intercepted and the interception response modifies fields in the resultant p2p message. --- itest/list_on_test.go | 4 ++ itest/lnd_forward_interceptor_test.go | 92 +++++++++++++++++++++++++++ 2 files changed, 96 insertions(+) diff --git a/itest/list_on_test.go b/itest/list_on_test.go index 147a0de0c0..9b385ebed2 100644 --- a/itest/list_on_test.go +++ b/itest/list_on_test.go @@ -454,6 +454,10 @@ var allTestCases = []*lntest.TestCase{ Name: "forward interceptor", TestFunc: testForwardInterceptorBasic, }, + { + Name: "forward interceptor modified htlc", + TestFunc: testForwardInterceptorModifiedHtlc, + }, { Name: "zero conf channel open", TestFunc: testZeroConfChannelOpen, diff --git a/itest/lnd_forward_interceptor_test.go b/itest/lnd_forward_interceptor_test.go index b5d08f5bf8..ecbfc523ca 100644 --- a/itest/lnd_forward_interceptor_test.go +++ b/itest/lnd_forward_interceptor_test.go @@ -22,6 +22,8 @@ import ( var ( customTestKey uint64 = 394829 customTestValue = []byte{1, 3, 5} + + actionResumeModify = routerrpc.ResolveHoldForwardAction_RESUME_MODIFIED ) type interceptorTestCase struct { @@ -344,6 +346,96 @@ func testForwardInterceptorBasic(ht *lntest.HarnessTest) { ht.CloseChannel(bob, cpBC) } +// testForwardInterceptorModifiedHtlc tests that the interceptor can modify the +// amount and custom records of an intercepted HTLC and resume it. +func testForwardInterceptorModifiedHtlc(ht *lntest.HarnessTest) { + // Initialize the test context with 3 connected nodes. + ts := newInterceptorTestScenario(ht) + + alice, bob, carol := ts.alice, ts.bob, ts.carol + + // Open and wait for channels. + const chanAmt = btcutil.Amount(300000) + p := lntest.OpenChannelParams{Amt: chanAmt} + reqs := []*lntest.OpenChannelRequest{ + {Local: alice, Remote: bob, Param: p}, + {Local: bob, Remote: carol, Param: p}, + } + resp := ht.OpenMultiChannelsAsync(reqs) + cpAB, cpBC := resp[0], resp[1] + + // Make sure Alice is aware of channel Bob=>Carol. + ht.AssertTopologyChannelOpen(alice, cpBC) + + // Connect an interceptor to Bob's node. + bobInterceptor, cancelBobInterceptor := bob.RPC.HtlcInterceptor() + + // Prepare the test cases. + invoiceValueAmtMsat := int64(1000) + req := &lnrpc.Invoice{ValueMsat: invoiceValueAmtMsat} + addResponse := carol.RPC.AddInvoice(req) + invoice := carol.RPC.LookupInvoice(addResponse.RHash) + tc := &interceptorTestCase{ + amountMsat: invoiceValueAmtMsat, + invoice: invoice, + payAddr: invoice.PaymentAddr, + } + + // We initiate a payment from Alice. + done := make(chan struct{}) + go func() { + // Signal that all the payments have been sent. + defer close(done) + + ts.sendPaymentAndAssertAction(tc) + }() + + // We start the htlc interceptor with a simple implementation that saves + // all intercepted packets. These packets are held to simulate a + // pending payment. + packet := ht.ReceiveHtlcInterceptor(bobInterceptor) + + // Resume the intercepted HTLC with a modified amount and custom + // records. + customRecords := make(map[uint64][]byte) + + // Add custom records entry. + crKey := uint64(65537) + crValue := []byte("custom-records-test-value") + customRecords[crKey] = crValue + + // TODO(guggero): Actually modify the amount once we have the invoice + // interceptor and can accept a lower amount. + newOutAmountMsat := packet.OutgoingAmountMsat + + err := bobInterceptor.Send(&routerrpc.ForwardHtlcInterceptResponse{ + IncomingCircuitKey: packet.IncomingCircuitKey, + OutAmountMsat: newOutAmountMsat, + OutWireCustomRecords: customRecords, + Action: actionResumeModify, + }) + require.NoError(ht, err, "failed to send request") + + // Cancel the context, which will disconnect Bob's interceptor. + cancelBobInterceptor() + + // Make sure all goroutines are finished. + select { + case <-done: + case <-time.After(defaultTimeout): + require.Fail(ht, "timeout waiting for sending payment") + } + + // Assert that the payment was successful. + var preimage lntypes.Preimage + copy(preimage[:], invoice.RPreimage) + ht.AssertPaymentStatus(alice, preimage, lnrpc.Payment_SUCCEEDED) + + // Finally, close channels. + ht.CloseChannel(alice, cpAB) + ht.CloseChannel(bob, cpBC) +} + // interceptorTestScenario is a helper struct to hold the test context and // provide the needed functionality. type interceptorTestScenario struct { From b5efc0ef3e340d838bb7216cff2227dfa5140912 Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Mon, 2 Sep 2024 11:01:07 +0200 Subject: [PATCH 065/218] lnwallet: extract diskCommit, remove unused error return value --- lnwallet/channel.go | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/lnwallet/channel.go b/lnwallet/channel.go index 8dba079736..2338f6c104 100644 --- a/lnwallet/channel.go +++ b/lnwallet/channel.go @@ -3167,11 +3167,11 @@ func genRemoteHtlcSigJobs(keyRing *CommitmentKeyRing, var err error cancelChan := make(chan struct{}) + diskCommit := remoteCommitView.toDiskCommit(lntypes.Remote) auxResult, err := fn.MapOptionZ( leafStore, func(s AuxLeafStore) fn.Result[CommitDiffAuxResult] { return s.FetchLeavesFromCommit( - NewAuxChanState(chanState), - *remoteCommitView.toDiskCommit(lntypes.Remote), + NewAuxChanState(chanState), *diskCommit, *keyRing, ) }, @@ -3351,8 +3351,7 @@ func genRemoteHtlcSigJobs(keyRing *CommitmentKeyRing, // new commitment to the remote party. The commit diff returned contains all // information necessary for retransmission. func (lc *LightningChannel) createCommitDiff(newCommit *commitment, - commitSig lnwire.Sig, htlcSigs []lnwire.Sig) (*channeldb.CommitDiff, - error) { + commitSig lnwire.Sig, htlcSigs []lnwire.Sig) *channeldb.CommitDiff { // First, we need to convert the funding outpoint into the ID that's // used on the wire to identify this channel. We'll use this shortly @@ -3489,7 +3488,7 @@ func (lc *LightningChannel) createCommitDiff(newCommit *commitment, ClosedCircuitKeys: closedCircuitKeys, AddAcks: ackAddRefs, SettleFailAcks: settleFailRefs, - }, nil + } } // getUnsignedAckedUpdates returns all remote log updates that we haven't @@ -4124,10 +4123,7 @@ func (lc *LightningChannel) SignNextCommitment() (*NewCommitState, error) { // As we're about to proposer a new commitment state for the remote // party, we'll write this pending state to disk before we exit, so we // can retransmit it if necessary. - commitDiff, err := lc.createCommitDiff(newCommitView, sig, htlcSigs) - if err != nil { - return nil, err - } + commitDiff := lc.createCommitDiff(newCommitView, sig, htlcSigs) err = lc.channelState.AppendRemoteCommitChain(commitDiff) if err != nil { return nil, err @@ -4682,13 +4678,12 @@ func genHtlcSigValidationJobs(chanState *channeldb.OpenChannel, len(localCommitmentView.outgoingHTLCs) verifyJobs := make([]VerifyJob, 0, numHtlcs) + diskCommit := localCommitmentView.toDiskCommit(lntypes.Local) auxResult, err := fn.MapOptionZ( leafStore, func(s AuxLeafStore) fn.Result[CommitDiffAuxResult] { return s.FetchLeavesFromCommit( - NewAuxChanState(chanState), - *localCommitmentView.toDiskCommit( - lntypes.Local, - ), *keyRing, + NewAuxChanState(chanState), *diskCommit, + *keyRing, ) }, ).Unpack() From ef2c9801fb08544e8d1c8d7c1847a0adf80ec8bc Mon Sep 17 00:00:00 2001 From: George Tsagkarelis Date: Tue, 16 Apr 2024 12:29:15 +0200 Subject: [PATCH 066/218] multi: use wire records on payment and intercept flows --- htlcswitch/interceptable_switch.go | 22 ++++++----- htlcswitch/interfaces.go | 10 +++-- htlcswitch/link.go | 54 ++++++++++++++------------ htlcswitch/packet.go | 10 +++-- lnrpc/routerrpc/forward_interceptor.go | 2 +- lnwallet/channel.go | 14 ++++--- routing/payment_lifecycle.go | 41 ++++++++++--------- routing/payment_lifecycle_test.go | 2 +- routing/router.go | 19 +++++++-- witness_beacon.go | 9 +++-- 10 files changed, 108 insertions(+), 75 deletions(-) diff --git a/htlcswitch/interceptable_switch.go b/htlcswitch/interceptable_switch.go index aa500e3aab..e06163d486 100644 --- a/htlcswitch/interceptable_switch.go +++ b/htlcswitch/interceptable_switch.go @@ -12,6 +12,7 @@ import ( "github.com/lightningnetwork/lnd/fn" "github.com/lightningnetwork/lnd/htlcswitch/hop" "github.com/lightningnetwork/lnd/lntypes" + "github.com/lightningnetwork/lnd/lnutils" "github.com/lightningnetwork/lnd/lnwire" ) @@ -645,15 +646,16 @@ func (f *interceptedForward) Packet() InterceptedPacket { ChanID: f.packet.incomingChanID, HtlcID: f.packet.incomingHTLCID, }, - OutgoingChanID: f.packet.outgoingChanID, - Hash: f.htlc.PaymentHash, - OutgoingExpiry: f.htlc.Expiry, - OutgoingAmount: f.htlc.Amount, - IncomingAmount: f.packet.incomingAmount, - IncomingExpiry: f.packet.incomingTimeout, - CustomRecords: f.packet.customRecords, - OnionBlob: f.htlc.OnionBlob, - AutoFailHeight: f.autoFailHeight, + OutgoingChanID: f.packet.outgoingChanID, + Hash: f.htlc.PaymentHash, + OutgoingExpiry: f.htlc.Expiry, + OutgoingAmount: f.htlc.Amount, + IncomingAmount: f.packet.incomingAmount, + IncomingExpiry: f.packet.incomingTimeout, + InOnionCustomRecords: f.packet.inOnionCustomRecords, + OnionBlob: f.htlc.OnionBlob, + AutoFailHeight: f.autoFailHeight, + InWireCustomRecords: f.packet.inWireCustomRecords, } } @@ -723,6 +725,8 @@ func (f *interceptedForward) ResumeModified( } } + log.Tracef("Forwarding packet %v", lnutils.SpewLogClosure(f.packet)) + // Forward to the switch. A link quit channel isn't needed, because we // are on a different thread now. return f.htlcSwitch.ForwardPackets(nil, f.packet) diff --git a/htlcswitch/interfaces.go b/htlcswitch/interfaces.go index 5e808f42a2..f5b3bbe98c 100644 --- a/htlcswitch/interfaces.go +++ b/htlcswitch/interfaces.go @@ -357,13 +357,17 @@ type InterceptedPacket struct { // IncomingAmount is the amount of the accepted htlc. IncomingAmount lnwire.MilliSatoshi - // CustomRecords are user-defined records in the custom type range that - // were included in the payload. - CustomRecords record.CustomSet + // InOnionCustomRecords are user-defined records in the custom type + // range that were included in the payload. + InOnionCustomRecords record.CustomSet // OnionBlob is the onion packet for the next hop OnionBlob [lnwire.OnionPacketSize]byte + // InWireCustomRecords are user-defined p2p wire message records that + // were defined by the peer that forwarded this HTLC to us. + InWireCustomRecords lnwire.CustomRecords + // AutoFailHeight is the block height at which this intercept will be // failed back automatically. AutoFailHeight int32 diff --git a/htlcswitch/link.go b/htlcswitch/link.go index 81c82cfa25..57d5319354 100644 --- a/htlcswitch/link.go +++ b/htlcswitch/link.go @@ -3630,7 +3630,7 @@ func (l *channelLink) processRemoteAdds(fwdPkg *channeldb.FwdPkg, } // Otherwise, it was already processed, we can - // can collect it and continue. + // collect it and continue. addMsg := &lnwire.UpdateAddHTLC{ Expiry: fwdInfo.OutgoingCTLV, Amount: fwdInfo.AmountToForward, @@ -3650,19 +3650,21 @@ func (l *channelLink) processRemoteAdds(fwdPkg *channeldb.FwdPkg, inboundFee := l.cfg.FwrdingPolicy.InboundFee + //nolint:lll updatePacket := &htlcPacket{ - incomingChanID: l.ShortChanID(), - incomingHTLCID: pd.HtlcIndex, - outgoingChanID: fwdInfo.NextHop, - sourceRef: pd.SourceRef, - incomingAmount: pd.Amount, - amount: addMsg.Amount, - htlc: addMsg, - obfuscator: obfuscator, - incomingTimeout: pd.Timeout, - outgoingTimeout: fwdInfo.OutgoingCTLV, - customRecords: pld.CustomRecords(), - inboundFee: inboundFee, + incomingChanID: l.ShortChanID(), + incomingHTLCID: pd.HtlcIndex, + outgoingChanID: fwdInfo.NextHop, + sourceRef: pd.SourceRef, + incomingAmount: pd.Amount, + amount: addMsg.Amount, + htlc: addMsg, + obfuscator: obfuscator, + incomingTimeout: pd.Timeout, + outgoingTimeout: fwdInfo.OutgoingCTLV, + inOnionCustomRecords: pld.CustomRecords(), + inboundFee: inboundFee, + inWireCustomRecords: pd.CustomRecords.Copy(), } switchPackets = append( switchPackets, updatePacket, @@ -3718,19 +3720,21 @@ func (l *channelLink) processRemoteAdds(fwdPkg *channeldb.FwdPkg, if fwdPkg.State == channeldb.FwdStateLockedIn { inboundFee := l.cfg.FwrdingPolicy.InboundFee + //nolint:lll updatePacket := &htlcPacket{ - incomingChanID: l.ShortChanID(), - incomingHTLCID: pd.HtlcIndex, - outgoingChanID: fwdInfo.NextHop, - sourceRef: pd.SourceRef, - incomingAmount: pd.Amount, - amount: addMsg.Amount, - htlc: addMsg, - obfuscator: obfuscator, - incomingTimeout: pd.Timeout, - outgoingTimeout: fwdInfo.OutgoingCTLV, - customRecords: pld.CustomRecords(), - inboundFee: inboundFee, + incomingChanID: l.ShortChanID(), + incomingHTLCID: pd.HtlcIndex, + outgoingChanID: fwdInfo.NextHop, + sourceRef: pd.SourceRef, + incomingAmount: pd.Amount, + amount: addMsg.Amount, + htlc: addMsg, + obfuscator: obfuscator, + incomingTimeout: pd.Timeout, + outgoingTimeout: fwdInfo.OutgoingCTLV, + inOnionCustomRecords: pld.CustomRecords(), + inboundFee: inboundFee, + inWireCustomRecords: pd.CustomRecords.Copy(), } fwdPkg.FwdFilter.Set(idx) diff --git a/htlcswitch/packet.go b/htlcswitch/packet.go index 45f4e465b6..31639dd5d1 100644 --- a/htlcswitch/packet.go +++ b/htlcswitch/packet.go @@ -94,9 +94,13 @@ type htlcPacket struct { // link. outgoingTimeout uint32 - // customRecords are user-defined records in the custom type range that - // were included in the payload. - customRecords record.CustomSet + // inOnionCustomRecords are user-defined records in the custom type + // range that were included in the onion payload. + inOnionCustomRecords record.CustomSet + + // inWireCustomRecords are custom type range TLVs that are included + // in the incoming update_add_htlc wire message. + inWireCustomRecords lnwire.CustomRecords // originalOutgoingChanID is used when sending back failure messages. // It is only used for forwarded Adds on option_scid_alias channels. diff --git a/lnrpc/routerrpc/forward_interceptor.go b/lnrpc/routerrpc/forward_interceptor.go index 4ed497034f..70b9146fd2 100644 --- a/lnrpc/routerrpc/forward_interceptor.go +++ b/lnrpc/routerrpc/forward_interceptor.go @@ -89,7 +89,7 @@ func (r *forwardInterceptor) onIntercept( OutgoingExpiry: htlc.OutgoingExpiry, IncomingAmountMsat: uint64(htlc.IncomingAmount), IncomingExpiry: htlc.IncomingExpiry, - CustomRecords: htlc.CustomRecords, + CustomRecords: htlc.InOnionCustomRecords, OnionBlob: htlc.OnionBlob[:], AutoFailHeight: htlc.AutoFailHeight, } diff --git a/lnwallet/channel.go b/lnwallet/channel.go index 2338f6c104..7af99f527b 100644 --- a/lnwallet/channel.go +++ b/lnwallet/channel.go @@ -207,6 +207,7 @@ func PayDescsFromRemoteLogUpdates(chanID lnwire.ShortChannelID, height uint64, Index: uint16(i), }, BlindingPoint: wireMsg.BlindingPoint, + CustomRecords: wireMsg.CustomRecords.Copy(), } pd.OnionBlob = make([]byte, len(wireMsg.OnionBlob)) copy(pd.OnionBlob[:], wireMsg.OnionBlob[:]) @@ -1154,6 +1155,7 @@ func (lc *LightningChannel) logUpdateToPayDesc(logUpdate *channeldb.LogUpdate, LogIndex: logUpdate.LogIndex, addCommitHeightRemote: commitHeight, BlindingPoint: wireMsg.BlindingPoint, + CustomRecords: wireMsg.CustomRecords.Copy(), } pd.OnionBlob = make([]byte, len(wireMsg.OnionBlob)) copy(pd.OnionBlob[:], wireMsg.OnionBlob[:]) @@ -1359,6 +1361,7 @@ func (lc *LightningChannel) remoteLogUpdateToPayDesc(logUpdate *channeldb.LogUpd LogIndex: logUpdate.LogIndex, addCommitHeightLocal: commitHeight, BlindingPoint: wireMsg.BlindingPoint, + CustomRecords: wireMsg.CustomRecords.Copy(), } pd.OnionBlob = make([]byte, len(wireMsg.OnionBlob)) copy(pd.OnionBlob, wireMsg.OnionBlob[:]) @@ -3403,6 +3406,7 @@ func (lc *LightningChannel) createCommitDiff(newCommit *commitment, Expiry: pd.Timeout, PaymentHash: pd.RHash, BlindingPoint: pd.BlindingPoint, + CustomRecords: pd.CustomRecords.Copy(), } copy(htlc.OnionBlob[:], pd.OnionBlob) logUpdate.UpdateMsg = htlc @@ -3543,6 +3547,7 @@ func (lc *LightningChannel) getUnsignedAckedUpdates() []channeldb.LogUpdate { Expiry: pd.Timeout, PaymentHash: pd.RHash, BlindingPoint: pd.BlindingPoint, + CustomRecords: pd.CustomRecords.Copy(), } copy(htlc.OnionBlob[:], pd.OnionBlob) logUpdate.UpdateMsg = htlc @@ -5620,6 +5625,7 @@ func (lc *LightningChannel) ReceiveRevocation(revMsg *lnwire.RevokeAndAck) ( Expiry: pd.Timeout, PaymentHash: pd.RHash, BlindingPoint: pd.BlindingPoint, + CustomRecords: pd.CustomRecords.Copy(), } copy(htlc.OnionBlob[:], pd.OnionBlob) logUpdate.UpdateMsg = htlc @@ -5965,9 +5971,7 @@ func (lc *LightningChannel) htlcAddDescriptor(htlc *lnwire.UpdateAddHTLC, OnionBlob: htlc.OnionBlob[:], OpenCircuitKey: openKey, BlindingPoint: htlc.BlindingPoint, - // TODO(guggero): Add custom records from HTLC here once we have - // the custom records in the HTLC struct (later commits in this - // PR). + CustomRecords: htlc.CustomRecords.Copy(), } } @@ -6028,9 +6032,7 @@ func (lc *LightningChannel) ReceiveHTLC(htlc *lnwire.UpdateAddHTLC) (uint64, HtlcIndex: lc.updateLogs.Remote.htlcCounter, OnionBlob: htlc.OnionBlob[:], BlindingPoint: htlc.BlindingPoint, - // TODO(guggero): Add custom records from HTLC here once we have - // the custom records in the HTLC struct (later commits in this - // PR). + CustomRecords: htlc.CustomRecords.Copy(), } localACKedIndex := lc.commitChains.Remote.tail().messageIndices.Local diff --git a/routing/payment_lifecycle.go b/routing/payment_lifecycle.go index 5244d4d636..2c7fd3b21d 100644 --- a/routing/payment_lifecycle.go +++ b/routing/payment_lifecycle.go @@ -25,12 +25,13 @@ var ErrPaymentLifecycleExiting = errors.New("payment lifecycle exiting") // paymentLifecycle holds all information about the current state of a payment // needed to resume if from any point. type paymentLifecycle struct { - router *ChannelRouter - feeLimit lnwire.MilliSatoshi - identifier lntypes.Hash - paySession PaymentSession - shardTracker shards.ShardTracker - currentHeight int32 + router *ChannelRouter + feeLimit lnwire.MilliSatoshi + identifier lntypes.Hash + paySession PaymentSession + shardTracker shards.ShardTracker + currentHeight int32 + firstHopCustomRecords lnwire.CustomRecords // quit is closed to signal the sub goroutines of the payment lifecycle // to stop. @@ -52,18 +53,19 @@ type paymentLifecycle struct { // newPaymentLifecycle initiates a new payment lifecycle and returns it. func newPaymentLifecycle(r *ChannelRouter, feeLimit lnwire.MilliSatoshi, identifier lntypes.Hash, paySession PaymentSession, - shardTracker shards.ShardTracker, - currentHeight int32) *paymentLifecycle { + shardTracker shards.ShardTracker, currentHeight int32, + firstHopCustomRecords lnwire.CustomRecords) *paymentLifecycle { p := &paymentLifecycle{ - router: r, - feeLimit: feeLimit, - identifier: identifier, - paySession: paySession, - shardTracker: shardTracker, - currentHeight: currentHeight, - quit: make(chan struct{}), - resultCollected: make(chan error, 1), + router: r, + feeLimit: feeLimit, + identifier: identifier, + paySession: paySession, + shardTracker: shardTracker, + currentHeight: currentHeight, + quit: make(chan struct{}), + resultCollected: make(chan error, 1), + firstHopCustomRecords: firstHopCustomRecords, } // Mount the result collector. @@ -677,9 +679,10 @@ func (p *paymentLifecycle) sendAttempt( // this packet will be used to route the payment through the network, // starting with the first-hop. htlcAdd := &lnwire.UpdateAddHTLC{ - Amount: rt.TotalAmount, - Expiry: rt.TotalTimeLock, - PaymentHash: *attempt.Hash, + Amount: rt.TotalAmount, + Expiry: rt.TotalTimeLock, + PaymentHash: *attempt.Hash, + CustomRecords: p.firstHopCustomRecords, } // Generate the raw encoded sphinx packet to be included along diff --git a/routing/payment_lifecycle_test.go b/routing/payment_lifecycle_test.go index 34c8d6c17a..4df27523f2 100644 --- a/routing/payment_lifecycle_test.go +++ b/routing/payment_lifecycle_test.go @@ -89,7 +89,7 @@ func newTestPaymentLifecycle(t *testing.T) (*paymentLifecycle, *mockers) { // Create a test payment lifecycle with no fee limit and no timeout. p := newPaymentLifecycle( rt, noFeeLimit, paymentHash, mockPaymentSession, - mockShardTracker, 0, + mockShardTracker, 0, nil, ) // Create a mock payment which is returned from mockControlTower. diff --git a/routing/router.go b/routing/router.go index 0b6a9beacd..0ec5976aeb 100644 --- a/routing/router.go +++ b/routing/router.go @@ -865,6 +865,11 @@ type LightningPayment struct { // fail. DestCustomRecords record.CustomSet + // FirstHopCustomRecords are the TLV records that are to be sent to the + // first hop of this payment. These records will be transmitted via the + // wire message and therefore do not affect the onion payload size. + FirstHopCustomRecords lnwire.CustomRecords + // MaxParts is the maximum number of partial payments that may be used // to complete the full amount. MaxParts uint32 @@ -948,6 +953,7 @@ func (r *ChannelRouter) SendPayment(payment *LightningPayment) ([32]byte, return r.sendPayment( context.Background(), payment.FeeLimit, payment.Identifier(), payment.PayAttemptTimeout, paySession, shardTracker, + payment.FirstHopCustomRecords, ) } @@ -968,6 +974,7 @@ func (r *ChannelRouter) SendPaymentAsync(ctx context.Context, _, _, err := r.sendPayment( ctx, payment.FeeLimit, payment.Identifier(), payment.PayAttemptTimeout, ps, st, + payment.FirstHopCustomRecords, ) if err != nil { log.Errorf("Payment %x failed: %v", @@ -1141,7 +1148,9 @@ func (r *ChannelRouter) sendToRoute(htlcHash lntypes.Hash, rt *route.Route, // - nil payment session (since we already have a route). // - no payment timeout. // - no current block height. - p := newPaymentLifecycle(r, 0, paymentIdentifier, nil, shardTracker, 0) + p := newPaymentLifecycle( + r, 0, paymentIdentifier, nil, shardTracker, 0, nil, + ) // We found a route to try, create a new HTLC attempt to try. // @@ -1237,7 +1246,9 @@ func (r *ChannelRouter) sendToRoute(htlcHash lntypes.Hash, rt *route.Route, func (r *ChannelRouter) sendPayment(ctx context.Context, feeLimit lnwire.MilliSatoshi, identifier lntypes.Hash, paymentAttemptTimeout time.Duration, paySession PaymentSession, - shardTracker shards.ShardTracker) ([32]byte, *route.Route, error) { + shardTracker shards.ShardTracker, + firstHopCustomRecords lnwire.CustomRecords) ([32]byte, *route.Route, + error) { // If the user provides a timeout, we will additionally wrap the context // in a deadline. @@ -1262,7 +1273,7 @@ func (r *ChannelRouter) sendPayment(ctx context.Context, // can resume the payment from the current state. p := newPaymentLifecycle( r, feeLimit, identifier, paySession, shardTracker, - currentHeight, + currentHeight, firstHopCustomRecords, ) return p.resumePayment(ctx) @@ -1465,7 +1476,7 @@ func (r *ChannelRouter) resumePayments() error { noTimeout := time.Duration(0) _, _, err := r.sendPayment( context.Background(), 0, payHash, noTimeout, paySession, - shardTracker, + shardTracker, nil, ) if err != nil { log.Errorf("Resuming payment %v failed: %v", payHash, diff --git a/witness_beacon.go b/witness_beacon.go index 4fd44e2837..2bc3c08509 100644 --- a/witness_beacon.go +++ b/witness_beacon.go @@ -101,10 +101,11 @@ func (p *preimageBeacon) SubscribeUpdates( ChanID: chanID, HtlcID: htlc.HtlcIndex, }, - OutgoingChanID: payload.FwdInfo.NextHop, - OutgoingExpiry: payload.FwdInfo.OutgoingCTLV, - OutgoingAmount: payload.FwdInfo.AmountToForward, - CustomRecords: payload.CustomRecords(), + OutgoingChanID: payload.FwdInfo.NextHop, + OutgoingExpiry: payload.FwdInfo.OutgoingCTLV, + OutgoingAmount: payload.FwdInfo.AmountToForward, + InOnionCustomRecords: payload.CustomRecords(), + InWireCustomRecords: htlc.CustomRecords, } copy(packet.OnionBlob[:], nextHopOnionBlob) From 857a16d838fbcd0f6beef757f35a13f545d06120 Mon Sep 17 00:00:00 2001 From: George Tsagkarelis Date: Tue, 16 Apr 2024 11:03:59 +0200 Subject: [PATCH 067/218] lnrpc: add wire records fields to payment+interceptor RPCs --- lnrpc/routerrpc/forward_interceptor.go | 1 + lnrpc/routerrpc/router.pb.go | 1407 +++++++++++++----------- lnrpc/routerrpc/router.proto | 30 + lnrpc/routerrpc/router.swagger.json | 32 + lnrpc/routerrpc/router_backend.go | 6 + lnrpc/routerrpc/router_server.go | 30 +- routing/router.go | 25 +- routing/router_test.go | 50 +- rpcserver.go | 2 +- 9 files changed, 902 insertions(+), 681 deletions(-) diff --git a/lnrpc/routerrpc/forward_interceptor.go b/lnrpc/routerrpc/forward_interceptor.go index 70b9146fd2..614a11888c 100644 --- a/lnrpc/routerrpc/forward_interceptor.go +++ b/lnrpc/routerrpc/forward_interceptor.go @@ -92,6 +92,7 @@ func (r *forwardInterceptor) onIntercept( CustomRecords: htlc.InOnionCustomRecords, OnionBlob: htlc.OnionBlob[:], AutoFailHeight: htlc.AutoFailHeight, + InWireCustomRecords: htlc.InWireCustomRecords, } return r.stream.Send(interceptionRequest) diff --git a/lnrpc/routerrpc/router.pb.go b/lnrpc/routerrpc/router.pb.go index dc52041a0f..f74fb9d3fc 100644 --- a/lnrpc/routerrpc/router.pb.go +++ b/lnrpc/routerrpc/router.pb.go @@ -507,6 +507,12 @@ type SendPaymentRequest struct { // still settle afterwards. Canceling will only prevent further attempts from // being sent. Cancelable bool `protobuf:"varint,24,opt,name=cancelable,proto3" json:"cancelable,omitempty"` + // An optional field that can be used to pass an arbitrary set of TLV records + // to the first hop peer of this payment. This can be used to pass application + // specific data during the payment attempt. Record types are required to be in + // the custom range >= 65536. When using REST, the values must be encoded as + // base64. + FirstHopCustomRecords map[uint64][]byte `protobuf:"bytes,25,rep,name=first_hop_custom_records,json=firstHopCustomRecords,proto3" json:"first_hop_custom_records,omitempty" protobuf_key:"varint,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` } func (x *SendPaymentRequest) Reset() { @@ -710,6 +716,13 @@ func (x *SendPaymentRequest) GetCancelable() bool { return false } +func (x *SendPaymentRequest) GetFirstHopCustomRecords() map[uint64][]byte { + if x != nil { + return x.FirstHopCustomRecords + } + return nil +} + type TrackPaymentRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -991,6 +1004,12 @@ type SendToRouteRequest struct { // failed unless a terminal error is occurred, such as payment timeout, no // routes, incorrect payment details, or insufficient funds. SkipTempErr bool `protobuf:"varint,3,opt,name=skip_temp_err,json=skipTempErr,proto3" json:"skip_temp_err,omitempty"` + // An optional field that can be used to pass an arbitrary set of TLV records + // to the first hop peer of this payment. This can be used to pass application + // specific data during the payment attempt. Record types are required to be in + // the custom range >= 65536. When using REST, the values must be encoded as + // base64. + FirstHopCustomRecords map[uint64][]byte `protobuf:"bytes,4,rep,name=first_hop_custom_records,json=firstHopCustomRecords,proto3" json:"first_hop_custom_records,omitempty" protobuf_key:"varint,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` } func (x *SendToRouteRequest) Reset() { @@ -1046,6 +1065,13 @@ func (x *SendToRouteRequest) GetSkipTempErr() bool { return false } +func (x *SendToRouteRequest) GetFirstHopCustomRecords() map[uint64][]byte { + if x != nil { + return x.FirstHopCustomRecords + } + return nil +} + type SendToRouteResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -2167,6 +2193,12 @@ type BuildRouteRequest struct { // An optional payment addr to be included within the last hop of the route. // This is also called payment secret in specifications (e.g. BOLT 11). PaymentAddr []byte `protobuf:"bytes,5,opt,name=payment_addr,json=paymentAddr,proto3" json:"payment_addr,omitempty"` + // An optional field that can be used to pass an arbitrary set of TLV records + // to the first hop peer of this payment. This can be used to pass application + // specific data during the payment attempt. Record types are required to be in + // the custom range >= 65536. When using REST, the values must be encoded as + // base64. + FirstHopCustomRecords map[uint64][]byte `protobuf:"bytes,6,rep,name=first_hop_custom_records,json=firstHopCustomRecords,proto3" json:"first_hop_custom_records,omitempty" protobuf_key:"varint,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` } func (x *BuildRouteRequest) Reset() { @@ -2236,6 +2268,13 @@ func (x *BuildRouteRequest) GetPaymentAddr() []byte { return nil } +func (x *BuildRouteRequest) GetFirstHopCustomRecords() map[uint64][]byte { + if x != nil { + return x.FirstHopCustomRecords + } + return nil +} + type BuildRouteResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -3057,6 +3096,8 @@ type ForwardHtlcInterceptRequest struct { // The block height at which this htlc will be auto-failed to prevent the // channel from force-closing. AutoFailHeight int32 `protobuf:"varint,10,opt,name=auto_fail_height,json=autoFailHeight,proto3" json:"auto_fail_height,omitempty"` + // The custom records of the peer's incoming p2p wire message. + InWireCustomRecords map[uint64][]byte `protobuf:"bytes,11,rep,name=in_wire_custom_records,json=inWireCustomRecords,proto3" json:"in_wire_custom_records,omitempty" protobuf_key:"varint,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` } func (x *ForwardHtlcInterceptRequest) Reset() { @@ -3161,6 +3202,13 @@ func (x *ForwardHtlcInterceptRequest) GetAutoFailHeight() int32 { return 0 } +func (x *ForwardHtlcInterceptRequest) GetInWireCustomRecords() map[uint64][]byte { + if x != nil { + return x.InWireCustomRecords + } + return nil +} + // * // ForwardHtlcInterceptResponse enables the caller to resolve a previously hold // forward. The caller can choose either to: @@ -3583,7 +3631,7 @@ var file_routerrpc_router_proto_rawDesc = []byte{ 0x0a, 0x16, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2f, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x09, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x1a, 0x0f, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x6e, 0x69, 0x6e, 0x67, 0x2e, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x94, 0x08, 0x0a, 0x12, 0x53, 0x65, 0x6e, 0x64, 0x50, 0x61, 0x79, + 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xd1, 0x09, 0x0a, 0x12, 0x53, 0x65, 0x6e, 0x64, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x65, 0x73, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x64, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x6d, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x03, 0x61, 0x6d, @@ -3644,566 +3692,613 @@ var file_routerrpc_router_proto_rawDesc = []byte{ 0x5f, 0x70, 0x72, 0x65, 0x66, 0x18, 0x17, 0x20, 0x01, 0x28, 0x01, 0x52, 0x08, 0x74, 0x69, 0x6d, 0x65, 0x50, 0x72, 0x65, 0x66, 0x12, 0x1e, 0x0a, 0x0a, 0x63, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x18, 0x18, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x63, 0x61, 0x6e, 0x63, 0x65, - 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x1a, 0x44, 0x0a, 0x16, 0x44, 0x65, 0x73, 0x74, 0x43, 0x75, 0x73, - 0x74, 0x6f, 0x6d, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, - 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x03, 0x6b, 0x65, - 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, - 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x68, 0x0a, 0x13, 0x54, - 0x72, 0x61, 0x63, 0x6b, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x68, 0x61, - 0x73, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, - 0x74, 0x48, 0x61, 0x73, 0x68, 0x12, 0x2e, 0x0a, 0x13, 0x6e, 0x6f, 0x5f, 0x69, 0x6e, 0x66, 0x6c, - 0x69, 0x67, 0x68, 0x74, 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x08, 0x52, 0x11, 0x6e, 0x6f, 0x49, 0x6e, 0x66, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x55, 0x70, - 0x64, 0x61, 0x74, 0x65, 0x73, 0x22, 0x46, 0x0a, 0x14, 0x54, 0x72, 0x61, 0x63, 0x6b, 0x50, 0x61, - 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2e, 0x0a, - 0x13, 0x6e, 0x6f, 0x5f, 0x69, 0x6e, 0x66, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x5f, 0x75, 0x70, 0x64, - 0x61, 0x74, 0x65, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x11, 0x6e, 0x6f, 0x49, 0x6e, - 0x66, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x22, 0x81, 0x01, - 0x0a, 0x0f, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x46, 0x65, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x65, 0x73, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, - 0x04, 0x64, 0x65, 0x73, 0x74, 0x12, 0x17, 0x0a, 0x07, 0x61, 0x6d, 0x74, 0x5f, 0x73, 0x61, 0x74, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x61, 0x6d, 0x74, 0x53, 0x61, 0x74, 0x12, 0x27, - 0x0a, 0x0f, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, - 0x75, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, - 0x74, 0x22, 0xa8, 0x01, 0x0a, 0x10, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x46, 0x65, 0x65, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x28, 0x0a, 0x10, 0x72, 0x6f, 0x75, 0x74, 0x69, 0x6e, - 0x67, 0x5f, 0x66, 0x65, 0x65, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, - 0x52, 0x0e, 0x72, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x46, 0x65, 0x65, 0x4d, 0x73, 0x61, 0x74, - 0x12, 0x26, 0x0a, 0x0f, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x64, 0x65, - 0x6c, 0x61, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0d, 0x74, 0x69, 0x6d, 0x65, 0x4c, - 0x6f, 0x63, 0x6b, 0x44, 0x65, 0x6c, 0x61, 0x79, 0x12, 0x42, 0x0a, 0x0e, 0x66, 0x61, 0x69, 0x6c, - 0x75, 0x72, 0x65, 0x5f, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0e, - 0x32, 0x1b, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, - 0x46, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x52, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x52, 0x0d, 0x66, - 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x52, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x22, 0x7f, 0x0a, 0x12, - 0x53, 0x65, 0x6e, 0x64, 0x54, 0x6f, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x68, 0x61, - 0x73, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, - 0x74, 0x48, 0x61, 0x73, 0x68, 0x12, 0x22, 0x0a, 0x05, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x6f, 0x75, - 0x74, 0x65, 0x52, 0x05, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x12, 0x22, 0x0a, 0x0d, 0x73, 0x6b, 0x69, - 0x70, 0x5f, 0x74, 0x65, 0x6d, 0x70, 0x5f, 0x65, 0x72, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, - 0x52, 0x0b, 0x73, 0x6b, 0x69, 0x70, 0x54, 0x65, 0x6d, 0x70, 0x45, 0x72, 0x72, 0x22, 0x5b, 0x0a, - 0x13, 0x53, 0x65, 0x6e, 0x64, 0x54, 0x6f, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x72, 0x65, 0x69, 0x6d, 0x61, 0x67, 0x65, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x70, 0x72, 0x65, 0x69, 0x6d, 0x61, 0x67, 0x65, - 0x12, 0x28, 0x0a, 0x07, 0x66, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x0e, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x61, 0x69, 0x6c, 0x75, 0x72, - 0x65, 0x52, 0x07, 0x66, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x22, 0x1c, 0x0a, 0x1a, 0x52, 0x65, - 0x73, 0x65, 0x74, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, - 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x1d, 0x0a, 0x1b, 0x52, 0x65, 0x73, 0x65, - 0x74, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1c, 0x0a, 0x1a, 0x51, 0x75, 0x65, 0x72, 0x79, - 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x51, 0x0a, 0x1b, 0x51, 0x75, 0x65, 0x72, 0x79, 0x4d, 0x69, - 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2c, 0x0a, 0x05, 0x70, 0x61, 0x69, 0x72, 0x73, 0x18, 0x02, 0x20, - 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, - 0x50, 0x61, 0x69, 0x72, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x52, 0x05, 0x70, 0x61, 0x69, - 0x72, 0x73, 0x4a, 0x04, 0x08, 0x01, 0x10, 0x02, 0x22, 0x62, 0x0a, 0x1c, 0x58, 0x49, 0x6d, 0x70, - 0x6f, 0x72, 0x74, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, - 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2c, 0x0a, 0x05, 0x70, 0x61, 0x69, 0x72, - 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, - 0x72, 0x70, 0x63, 0x2e, 0x50, 0x61, 0x69, 0x72, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x52, - 0x05, 0x70, 0x61, 0x69, 0x72, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x22, 0x1f, 0x0a, 0x1d, - 0x58, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x6f, - 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x8a, 0x01, - 0x0a, 0x0b, 0x50, 0x61, 0x69, 0x72, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x12, 0x1b, 0x0a, - 0x09, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x66, 0x72, 0x6f, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, - 0x52, 0x08, 0x6e, 0x6f, 0x64, 0x65, 0x46, 0x72, 0x6f, 0x6d, 0x12, 0x17, 0x0a, 0x07, 0x6e, 0x6f, - 0x64, 0x65, 0x5f, 0x74, 0x6f, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x6e, 0x6f, 0x64, - 0x65, 0x54, 0x6f, 0x12, 0x2d, 0x0a, 0x07, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x18, 0x07, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, - 0x2e, 0x50, 0x61, 0x69, 0x72, 0x44, 0x61, 0x74, 0x61, 0x52, 0x07, 0x68, 0x69, 0x73, 0x74, 0x6f, - 0x72, 0x79, 0x4a, 0x04, 0x08, 0x03, 0x10, 0x04, 0x4a, 0x04, 0x08, 0x04, 0x10, 0x05, 0x4a, 0x04, - 0x08, 0x05, 0x10, 0x06, 0x4a, 0x04, 0x08, 0x06, 0x10, 0x07, 0x22, 0xe8, 0x01, 0x0a, 0x08, 0x50, - 0x61, 0x69, 0x72, 0x44, 0x61, 0x74, 0x61, 0x12, 0x1b, 0x0a, 0x09, 0x66, 0x61, 0x69, 0x6c, 0x5f, - 0x74, 0x69, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x66, 0x61, 0x69, 0x6c, - 0x54, 0x69, 0x6d, 0x65, 0x12, 0x20, 0x0a, 0x0c, 0x66, 0x61, 0x69, 0x6c, 0x5f, 0x61, 0x6d, 0x74, - 0x5f, 0x73, 0x61, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x66, 0x61, 0x69, 0x6c, - 0x41, 0x6d, 0x74, 0x53, 0x61, 0x74, 0x12, 0x22, 0x0a, 0x0d, 0x66, 0x61, 0x69, 0x6c, 0x5f, 0x61, - 0x6d, 0x74, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b, 0x66, - 0x61, 0x69, 0x6c, 0x41, 0x6d, 0x74, 0x4d, 0x73, 0x61, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x73, 0x75, - 0x63, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, - 0x52, 0x0b, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x26, 0x0a, - 0x0f, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x61, 0x6d, 0x74, 0x5f, 0x73, 0x61, 0x74, - 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0d, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x41, - 0x6d, 0x74, 0x53, 0x61, 0x74, 0x12, 0x28, 0x0a, 0x10, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, - 0x5f, 0x61, 0x6d, 0x74, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x03, 0x52, - 0x0e, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x41, 0x6d, 0x74, 0x4d, 0x73, 0x61, 0x74, 0x4a, - 0x04, 0x08, 0x03, 0x10, 0x04, 0x22, 0x20, 0x0a, 0x1e, 0x47, 0x65, 0x74, 0x4d, 0x69, 0x73, 0x73, - 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x5a, 0x0a, 0x1f, 0x47, 0x65, 0x74, 0x4d, 0x69, - 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x43, 0x6f, 0x6e, 0x66, - 0x69, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x37, 0x0a, 0x06, 0x63, 0x6f, - 0x6e, 0x66, 0x69, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x72, 0x6f, 0x75, - 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x6f, - 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x06, 0x63, 0x6f, 0x6e, - 0x66, 0x69, 0x67, 0x22, 0x59, 0x0a, 0x1e, 0x53, 0x65, 0x74, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6f, - 0x6e, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x37, 0x0a, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, + 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x71, 0x0a, 0x18, 0x66, 0x69, 0x72, 0x73, 0x74, 0x5f, 0x68, + 0x6f, 0x70, 0x5f, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5f, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, + 0x73, 0x18, 0x19, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x38, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, + 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x46, 0x69, 0x72, 0x73, 0x74, 0x48, 0x6f, 0x70, 0x43, + 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x45, 0x6e, 0x74, 0x72, + 0x79, 0x52, 0x15, 0x66, 0x69, 0x72, 0x73, 0x74, 0x48, 0x6f, 0x70, 0x43, 0x75, 0x73, 0x74, 0x6f, + 0x6d, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x1a, 0x44, 0x0a, 0x16, 0x44, 0x65, 0x73, 0x74, + 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x45, 0x6e, 0x74, + 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, + 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0c, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x48, + 0x0a, 0x1a, 0x46, 0x69, 0x72, 0x73, 0x74, 0x48, 0x6f, 0x70, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, + 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, + 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, + 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x68, 0x0a, 0x13, 0x54, 0x72, 0x61, 0x63, + 0x6b, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, + 0x21, 0x0a, 0x0c, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x48, 0x61, + 0x73, 0x68, 0x12, 0x2e, 0x0a, 0x13, 0x6e, 0x6f, 0x5f, 0x69, 0x6e, 0x66, 0x6c, 0x69, 0x67, 0x68, + 0x74, 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, + 0x11, 0x6e, 0x6f, 0x49, 0x6e, 0x66, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x55, 0x70, 0x64, 0x61, 0x74, + 0x65, 0x73, 0x22, 0x46, 0x0a, 0x14, 0x54, 0x72, 0x61, 0x63, 0x6b, 0x50, 0x61, 0x79, 0x6d, 0x65, + 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2e, 0x0a, 0x13, 0x6e, 0x6f, + 0x5f, 0x69, 0x6e, 0x66, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, + 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x11, 0x6e, 0x6f, 0x49, 0x6e, 0x66, 0x6c, 0x69, + 0x67, 0x68, 0x74, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x22, 0x81, 0x01, 0x0a, 0x0f, 0x52, + 0x6f, 0x75, 0x74, 0x65, 0x46, 0x65, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, + 0x0a, 0x04, 0x64, 0x65, 0x73, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x64, 0x65, + 0x73, 0x74, 0x12, 0x17, 0x0a, 0x07, 0x61, 0x6d, 0x74, 0x5f, 0x73, 0x61, 0x74, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x03, 0x52, 0x06, 0x61, 0x6d, 0x74, 0x53, 0x61, 0x74, 0x12, 0x27, 0x0a, 0x0f, 0x70, + 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x22, 0xa8, + 0x01, 0x0a, 0x10, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x46, 0x65, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x28, 0x0a, 0x10, 0x72, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x5f, 0x66, + 0x65, 0x65, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0e, 0x72, + 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x46, 0x65, 0x65, 0x4d, 0x73, 0x61, 0x74, 0x12, 0x26, 0x0a, + 0x0f, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x64, 0x65, 0x6c, 0x61, 0x79, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0d, 0x74, 0x69, 0x6d, 0x65, 0x4c, 0x6f, 0x63, 0x6b, + 0x44, 0x65, 0x6c, 0x61, 0x79, 0x12, 0x42, 0x0a, 0x0e, 0x66, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, + 0x5f, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1b, 0x2e, + 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x46, 0x61, 0x69, + 0x6c, 0x75, 0x72, 0x65, 0x52, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x52, 0x0d, 0x66, 0x61, 0x69, 0x6c, + 0x75, 0x72, 0x65, 0x52, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x22, 0xbc, 0x02, 0x0a, 0x12, 0x53, 0x65, + 0x6e, 0x64, 0x54, 0x6f, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x68, 0x61, 0x73, 0x68, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x48, + 0x61, 0x73, 0x68, 0x12, 0x22, 0x0a, 0x05, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x6f, 0x75, 0x74, 0x65, + 0x52, 0x05, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x12, 0x22, 0x0a, 0x0d, 0x73, 0x6b, 0x69, 0x70, 0x5f, + 0x74, 0x65, 0x6d, 0x70, 0x5f, 0x65, 0x72, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, + 0x73, 0x6b, 0x69, 0x70, 0x54, 0x65, 0x6d, 0x70, 0x45, 0x72, 0x72, 0x12, 0x71, 0x0a, 0x18, 0x66, + 0x69, 0x72, 0x73, 0x74, 0x5f, 0x68, 0x6f, 0x70, 0x5f, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5f, + 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x38, 0x2e, + 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x54, 0x6f, + 0x52, 0x6f, 0x75, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x46, 0x69, 0x72, + 0x73, 0x74, 0x48, 0x6f, 0x70, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x52, 0x65, 0x63, 0x6f, 0x72, + 0x64, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x15, 0x66, 0x69, 0x72, 0x73, 0x74, 0x48, 0x6f, + 0x70, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x1a, 0x48, + 0x0a, 0x1a, 0x46, 0x69, 0x72, 0x73, 0x74, 0x48, 0x6f, 0x70, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, + 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, + 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, + 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x5b, 0x0a, 0x13, 0x53, 0x65, 0x6e, 0x64, + 0x54, 0x6f, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x1a, 0x0a, 0x08, 0x70, 0x72, 0x65, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0c, 0x52, 0x08, 0x70, 0x72, 0x65, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x12, 0x28, 0x0a, 0x07, 0x66, + 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x6c, + 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x52, 0x07, 0x66, 0x61, + 0x69, 0x6c, 0x75, 0x72, 0x65, 0x22, 0x1c, 0x0a, 0x1a, 0x52, 0x65, 0x73, 0x65, 0x74, 0x4d, 0x69, + 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x22, 0x1d, 0x0a, 0x1b, 0x52, 0x65, 0x73, 0x65, 0x74, 0x4d, 0x69, 0x73, 0x73, + 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x22, 0x1c, 0x0a, 0x1a, 0x51, 0x75, 0x65, 0x72, 0x79, 0x4d, 0x69, 0x73, 0x73, 0x69, + 0x6f, 0x6e, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x22, 0x51, 0x0a, 0x1b, 0x51, 0x75, 0x65, 0x72, 0x79, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, + 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x2c, 0x0a, 0x05, 0x70, 0x61, 0x69, 0x72, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, + 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x61, 0x69, 0x72, 0x48, + 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x52, 0x05, 0x70, 0x61, 0x69, 0x72, 0x73, 0x4a, 0x04, 0x08, + 0x01, 0x10, 0x02, 0x22, 0x62, 0x0a, 0x1c, 0x58, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x4d, 0x69, + 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x12, 0x2c, 0x0a, 0x05, 0x70, 0x61, 0x69, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x50, + 0x61, 0x69, 0x72, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x52, 0x05, 0x70, 0x61, 0x69, 0x72, + 0x73, 0x12, 0x14, 0x0a, 0x05, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, + 0x52, 0x05, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x22, 0x1f, 0x0a, 0x1d, 0x58, 0x49, 0x6d, 0x70, 0x6f, + 0x72, 0x74, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x8a, 0x01, 0x0a, 0x0b, 0x50, 0x61, 0x69, + 0x72, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x12, 0x1b, 0x0a, 0x09, 0x6e, 0x6f, 0x64, 0x65, + 0x5f, 0x66, 0x72, 0x6f, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x6e, 0x6f, 0x64, + 0x65, 0x46, 0x72, 0x6f, 0x6d, 0x12, 0x17, 0x0a, 0x07, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x74, 0x6f, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x6e, 0x6f, 0x64, 0x65, 0x54, 0x6f, 0x12, 0x2d, + 0x0a, 0x07, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x13, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x61, 0x69, 0x72, + 0x44, 0x61, 0x74, 0x61, 0x52, 0x07, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x04, 0x08, + 0x03, 0x10, 0x04, 0x4a, 0x04, 0x08, 0x04, 0x10, 0x05, 0x4a, 0x04, 0x08, 0x05, 0x10, 0x06, 0x4a, + 0x04, 0x08, 0x06, 0x10, 0x07, 0x22, 0xe8, 0x01, 0x0a, 0x08, 0x50, 0x61, 0x69, 0x72, 0x44, 0x61, + 0x74, 0x61, 0x12, 0x1b, 0x0a, 0x09, 0x66, 0x61, 0x69, 0x6c, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x66, 0x61, 0x69, 0x6c, 0x54, 0x69, 0x6d, 0x65, 0x12, + 0x20, 0x0a, 0x0c, 0x66, 0x61, 0x69, 0x6c, 0x5f, 0x61, 0x6d, 0x74, 0x5f, 0x73, 0x61, 0x74, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x66, 0x61, 0x69, 0x6c, 0x41, 0x6d, 0x74, 0x53, 0x61, + 0x74, 0x12, 0x22, 0x0a, 0x0d, 0x66, 0x61, 0x69, 0x6c, 0x5f, 0x61, 0x6d, 0x74, 0x5f, 0x6d, 0x73, + 0x61, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b, 0x66, 0x61, 0x69, 0x6c, 0x41, 0x6d, + 0x74, 0x4d, 0x73, 0x61, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, + 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b, 0x73, 0x75, 0x63, + 0x63, 0x65, 0x73, 0x73, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x26, 0x0a, 0x0f, 0x73, 0x75, 0x63, 0x63, + 0x65, 0x73, 0x73, 0x5f, 0x61, 0x6d, 0x74, 0x5f, 0x73, 0x61, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, + 0x03, 0x52, 0x0d, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x41, 0x6d, 0x74, 0x53, 0x61, 0x74, + 0x12, 0x28, 0x0a, 0x10, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x61, 0x6d, 0x74, 0x5f, + 0x6d, 0x73, 0x61, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0e, 0x73, 0x75, 0x63, 0x63, + 0x65, 0x73, 0x73, 0x41, 0x6d, 0x74, 0x4d, 0x73, 0x61, 0x74, 0x4a, 0x04, 0x08, 0x03, 0x10, 0x04, + 0x22, 0x20, 0x0a, 0x1e, 0x47, 0x65, 0x74, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x6f, + 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x22, 0x5a, 0x0a, 0x1f, 0x47, 0x65, 0x74, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, + 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x37, 0x0a, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, - 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x22, 0x21, - 0x0a, 0x1f, 0x53, 0x65, 0x74, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x6e, 0x74, - 0x72, 0x6f, 0x6c, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x89, 0x04, 0x0a, 0x14, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x6e, - 0x74, 0x72, 0x6f, 0x6c, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x2e, 0x0a, 0x11, 0x68, 0x61, - 0x6c, 0x66, 0x5f, 0x6c, 0x69, 0x66, 0x65, 0x5f, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x04, 0x42, 0x02, 0x18, 0x01, 0x52, 0x0f, 0x68, 0x61, 0x6c, 0x66, 0x4c, - 0x69, 0x66, 0x65, 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x12, 0x2b, 0x0a, 0x0f, 0x68, 0x6f, - 0x70, 0x5f, 0x70, 0x72, 0x6f, 0x62, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x02, 0x42, 0x02, 0x18, 0x01, 0x52, 0x0e, 0x68, 0x6f, 0x70, 0x50, 0x72, 0x6f, 0x62, - 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x12, 0x1a, 0x0a, 0x06, 0x77, 0x65, 0x69, 0x67, 0x68, - 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x02, 0x42, 0x02, 0x18, 0x01, 0x52, 0x06, 0x77, 0x65, 0x69, - 0x67, 0x68, 0x74, 0x12, 0x36, 0x0a, 0x17, 0x6d, 0x61, 0x78, 0x69, 0x6d, 0x75, 0x6d, 0x5f, 0x70, - 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x18, 0x04, - 0x20, 0x01, 0x28, 0x0d, 0x52, 0x15, 0x6d, 0x61, 0x78, 0x69, 0x6d, 0x75, 0x6d, 0x50, 0x61, 0x79, - 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x12, 0x43, 0x0a, 0x1e, 0x6d, - 0x69, 0x6e, 0x69, 0x6d, 0x75, 0x6d, 0x5f, 0x66, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x5f, 0x72, - 0x65, 0x6c, 0x61, 0x78, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x18, 0x05, 0x20, - 0x01, 0x28, 0x04, 0x52, 0x1b, 0x6d, 0x69, 0x6e, 0x69, 0x6d, 0x75, 0x6d, 0x46, 0x61, 0x69, 0x6c, - 0x75, 0x72, 0x65, 0x52, 0x65, 0x6c, 0x61, 0x78, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, - 0x12, 0x46, 0x0a, 0x05, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0e, 0x32, - 0x30, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x4d, 0x69, 0x73, 0x73, - 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, - 0x2e, 0x50, 0x72, 0x6f, 0x62, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x4d, 0x6f, 0x64, 0x65, - 0x6c, 0x52, 0x05, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x12, 0x38, 0x0a, 0x07, 0x61, 0x70, 0x72, 0x69, - 0x6f, 0x72, 0x69, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x72, 0x6f, 0x75, 0x74, - 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x50, 0x61, 0x72, - 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x48, 0x00, 0x52, 0x07, 0x61, 0x70, 0x72, 0x69, 0x6f, - 0x72, 0x69, 0x12, 0x38, 0x0a, 0x07, 0x62, 0x69, 0x6d, 0x6f, 0x64, 0x61, 0x6c, 0x18, 0x08, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, - 0x42, 0x69, 0x6d, 0x6f, 0x64, 0x61, 0x6c, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, - 0x73, 0x48, 0x00, 0x52, 0x07, 0x62, 0x69, 0x6d, 0x6f, 0x64, 0x61, 0x6c, 0x22, 0x2c, 0x0a, 0x10, - 0x50, 0x72, 0x6f, 0x62, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x4d, 0x6f, 0x64, 0x65, 0x6c, - 0x12, 0x0b, 0x0a, 0x07, 0x41, 0x50, 0x52, 0x49, 0x4f, 0x52, 0x49, 0x10, 0x00, 0x12, 0x0b, 0x0a, - 0x07, 0x42, 0x49, 0x4d, 0x4f, 0x44, 0x41, 0x4c, 0x10, 0x01, 0x42, 0x11, 0x0a, 0x0f, 0x45, 0x73, - 0x74, 0x69, 0x6d, 0x61, 0x74, 0x6f, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x22, 0x72, 0x0a, - 0x11, 0x42, 0x69, 0x6d, 0x6f, 0x64, 0x61, 0x6c, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, - 0x72, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x77, 0x65, 0x69, 0x67, 0x68, - 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x01, 0x52, 0x0a, 0x6e, 0x6f, 0x64, 0x65, 0x57, 0x65, 0x69, - 0x67, 0x68, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x5f, 0x6d, 0x73, 0x61, - 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x4d, 0x73, - 0x61, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x64, 0x65, 0x63, 0x61, 0x79, 0x5f, 0x74, 0x69, 0x6d, 0x65, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x64, 0x65, 0x63, 0x61, 0x79, 0x54, 0x69, 0x6d, - 0x65, 0x22, 0xad, 0x01, 0x0a, 0x11, 0x41, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x50, 0x61, 0x72, - 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x12, 0x2a, 0x0a, 0x11, 0x68, 0x61, 0x6c, 0x66, 0x5f, - 0x6c, 0x69, 0x66, 0x65, 0x5f, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x04, 0x52, 0x0f, 0x68, 0x61, 0x6c, 0x66, 0x4c, 0x69, 0x66, 0x65, 0x53, 0x65, 0x63, 0x6f, - 0x6e, 0x64, 0x73, 0x12, 0x27, 0x0a, 0x0f, 0x68, 0x6f, 0x70, 0x5f, 0x70, 0x72, 0x6f, 0x62, 0x61, - 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x01, 0x52, 0x0e, 0x68, 0x6f, - 0x70, 0x50, 0x72, 0x6f, 0x62, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x12, 0x16, 0x0a, 0x06, - 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x01, 0x52, 0x06, 0x77, 0x65, - 0x69, 0x67, 0x68, 0x74, 0x12, 0x2b, 0x0a, 0x11, 0x63, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, - 0x5f, 0x66, 0x72, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x01, 0x52, - 0x10, 0x63, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x46, 0x72, 0x61, 0x63, 0x74, 0x69, 0x6f, - 0x6e, 0x22, 0x6a, 0x0a, 0x17, 0x51, 0x75, 0x65, 0x72, 0x79, 0x50, 0x72, 0x6f, 0x62, 0x61, 0x62, - 0x69, 0x6c, 0x69, 0x74, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, - 0x66, 0x72, 0x6f, 0x6d, 0x5f, 0x6e, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, - 0x08, 0x66, 0x72, 0x6f, 0x6d, 0x4e, 0x6f, 0x64, 0x65, 0x12, 0x17, 0x0a, 0x07, 0x74, 0x6f, 0x5f, - 0x6e, 0x6f, 0x64, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x74, 0x6f, 0x4e, 0x6f, - 0x64, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x61, 0x6d, 0x74, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x61, 0x6d, 0x74, 0x4d, 0x73, 0x61, 0x74, 0x22, 0x6b, 0x0a, - 0x18, 0x51, 0x75, 0x65, 0x72, 0x79, 0x50, 0x72, 0x6f, 0x62, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, - 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x70, 0x72, 0x6f, - 0x62, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x01, 0x52, 0x0b, - 0x70, 0x72, 0x6f, 0x62, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x12, 0x2d, 0x0a, 0x07, 0x68, - 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x72, - 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x61, 0x69, 0x72, 0x44, 0x61, 0x74, - 0x61, 0x52, 0x07, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x22, 0xca, 0x01, 0x0a, 0x11, 0x42, - 0x75, 0x69, 0x6c, 0x64, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x12, 0x19, 0x0a, 0x08, 0x61, 0x6d, 0x74, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x03, 0x52, 0x07, 0x61, 0x6d, 0x74, 0x4d, 0x73, 0x61, 0x74, 0x12, 0x28, 0x0a, 0x10, 0x66, - 0x69, 0x6e, 0x61, 0x6c, 0x5f, 0x63, 0x6c, 0x74, 0x76, 0x5f, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0e, 0x66, 0x69, 0x6e, 0x61, 0x6c, 0x43, 0x6c, 0x74, 0x76, - 0x44, 0x65, 0x6c, 0x74, 0x61, 0x12, 0x2c, 0x0a, 0x10, 0x6f, 0x75, 0x74, 0x67, 0x6f, 0x69, 0x6e, - 0x67, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x42, - 0x02, 0x30, 0x01, 0x52, 0x0e, 0x6f, 0x75, 0x74, 0x67, 0x6f, 0x69, 0x6e, 0x67, 0x43, 0x68, 0x61, - 0x6e, 0x49, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x68, 0x6f, 0x70, 0x5f, 0x70, 0x75, 0x62, 0x6b, 0x65, - 0x79, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x0a, 0x68, 0x6f, 0x70, 0x50, 0x75, 0x62, - 0x6b, 0x65, 0x79, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x5f, - 0x61, 0x64, 0x64, 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x70, 0x61, 0x79, 0x6d, - 0x65, 0x6e, 0x74, 0x41, 0x64, 0x64, 0x72, 0x22, 0x38, 0x0a, 0x12, 0x42, 0x75, 0x69, 0x6c, 0x64, - 0x52, 0x6f, 0x75, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x22, 0x0a, - 0x05, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x6c, - 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x52, 0x05, 0x72, 0x6f, 0x75, 0x74, - 0x65, 0x22, 0x1c, 0x0a, 0x1a, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x48, 0x74, - 0x6c, 0x63, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, - 0x86, 0x06, 0x0a, 0x09, 0x48, 0x74, 0x6c, 0x63, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x2e, 0x0a, - 0x13, 0x69, 0x6e, 0x63, 0x6f, 0x6d, 0x69, 0x6e, 0x67, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, - 0x6c, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x11, 0x69, 0x6e, 0x63, 0x6f, - 0x6d, 0x69, 0x6e, 0x67, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x49, 0x64, 0x12, 0x2e, 0x0a, - 0x13, 0x6f, 0x75, 0x74, 0x67, 0x6f, 0x69, 0x6e, 0x67, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, - 0x6c, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x11, 0x6f, 0x75, 0x74, 0x67, - 0x6f, 0x69, 0x6e, 0x67, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x49, 0x64, 0x12, 0x28, 0x0a, - 0x10, 0x69, 0x6e, 0x63, 0x6f, 0x6d, 0x69, 0x6e, 0x67, 0x5f, 0x68, 0x74, 0x6c, 0x63, 0x5f, 0x69, - 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0e, 0x69, 0x6e, 0x63, 0x6f, 0x6d, 0x69, 0x6e, - 0x67, 0x48, 0x74, 0x6c, 0x63, 0x49, 0x64, 0x12, 0x28, 0x0a, 0x10, 0x6f, 0x75, 0x74, 0x67, 0x6f, - 0x69, 0x6e, 0x67, 0x5f, 0x68, 0x74, 0x6c, 0x63, 0x5f, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, - 0x04, 0x52, 0x0e, 0x6f, 0x75, 0x74, 0x67, 0x6f, 0x69, 0x6e, 0x67, 0x48, 0x74, 0x6c, 0x63, 0x49, - 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x5f, 0x6e, - 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, - 0x6d, 0x70, 0x4e, 0x73, 0x12, 0x3d, 0x0a, 0x0a, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x5f, 0x74, 0x79, - 0x70, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1e, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, - 0x72, 0x72, 0x70, 0x63, 0x2e, 0x48, 0x74, 0x6c, 0x63, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x2e, 0x45, - 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x52, 0x09, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x54, - 0x79, 0x70, 0x65, 0x12, 0x3e, 0x0a, 0x0d, 0x66, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x5f, 0x65, - 0x76, 0x65, 0x6e, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x72, 0x6f, 0x75, - 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x45, 0x76, - 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x0c, 0x66, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x45, 0x76, - 0x65, 0x6e, 0x74, 0x12, 0x4b, 0x0a, 0x12, 0x66, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x5f, 0x66, - 0x61, 0x69, 0x6c, 0x5f, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x1b, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x6f, 0x72, 0x77, - 0x61, 0x72, 0x64, 0x46, 0x61, 0x69, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x10, - 0x66, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x46, 0x61, 0x69, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, - 0x12, 0x3b, 0x0a, 0x0c, 0x73, 0x65, 0x74, 0x74, 0x6c, 0x65, 0x5f, 0x65, 0x76, 0x65, 0x6e, 0x74, - 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, - 0x70, 0x63, 0x2e, 0x53, 0x65, 0x74, 0x74, 0x6c, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x48, 0x00, - 0x52, 0x0b, 0x73, 0x65, 0x74, 0x74, 0x6c, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x42, 0x0a, - 0x0f, 0x6c, 0x69, 0x6e, 0x6b, 0x5f, 0x66, 0x61, 0x69, 0x6c, 0x5f, 0x65, 0x76, 0x65, 0x6e, 0x74, - 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, - 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x6e, 0x6b, 0x46, 0x61, 0x69, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, - 0x48, 0x00, 0x52, 0x0d, 0x6c, 0x69, 0x6e, 0x6b, 0x46, 0x61, 0x69, 0x6c, 0x45, 0x76, 0x65, 0x6e, - 0x74, 0x12, 0x47, 0x0a, 0x10, 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x64, 0x5f, - 0x65, 0x76, 0x65, 0x6e, 0x74, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x72, 0x6f, - 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, - 0x65, 0x64, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x0f, 0x73, 0x75, 0x62, 0x73, 0x63, - 0x72, 0x69, 0x62, 0x65, 0x64, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x45, 0x0a, 0x10, 0x66, 0x69, - 0x6e, 0x61, 0x6c, 0x5f, 0x68, 0x74, 0x6c, 0x63, 0x5f, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x18, 0x0c, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, - 0x2e, 0x46, 0x69, 0x6e, 0x61, 0x6c, 0x48, 0x74, 0x6c, 0x63, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x48, - 0x00, 0x52, 0x0e, 0x66, 0x69, 0x6e, 0x61, 0x6c, 0x48, 0x74, 0x6c, 0x63, 0x45, 0x76, 0x65, 0x6e, - 0x74, 0x22, 0x3c, 0x0a, 0x09, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0b, - 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x08, 0x0a, 0x04, 0x53, - 0x45, 0x4e, 0x44, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x52, 0x45, 0x43, 0x45, 0x49, 0x56, 0x45, - 0x10, 0x02, 0x12, 0x0b, 0x0a, 0x07, 0x46, 0x4f, 0x52, 0x57, 0x41, 0x52, 0x44, 0x10, 0x03, 0x42, - 0x07, 0x0a, 0x05, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x22, 0xbc, 0x01, 0x0a, 0x08, 0x48, 0x74, 0x6c, - 0x63, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x2b, 0x0a, 0x11, 0x69, 0x6e, 0x63, 0x6f, 0x6d, 0x69, 0x6e, - 0x67, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x6c, 0x6f, 0x63, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, - 0x52, 0x10, 0x69, 0x6e, 0x63, 0x6f, 0x6d, 0x69, 0x6e, 0x67, 0x54, 0x69, 0x6d, 0x65, 0x6c, 0x6f, - 0x63, 0x6b, 0x12, 0x2b, 0x0a, 0x11, 0x6f, 0x75, 0x74, 0x67, 0x6f, 0x69, 0x6e, 0x67, 0x5f, 0x74, - 0x69, 0x6d, 0x65, 0x6c, 0x6f, 0x63, 0x6b, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x10, 0x6f, - 0x75, 0x74, 0x67, 0x6f, 0x69, 0x6e, 0x67, 0x54, 0x69, 0x6d, 0x65, 0x6c, 0x6f, 0x63, 0x6b, 0x12, - 0x2a, 0x0a, 0x11, 0x69, 0x6e, 0x63, 0x6f, 0x6d, 0x69, 0x6e, 0x67, 0x5f, 0x61, 0x6d, 0x74, 0x5f, - 0x6d, 0x73, 0x61, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0f, 0x69, 0x6e, 0x63, 0x6f, - 0x6d, 0x69, 0x6e, 0x67, 0x41, 0x6d, 0x74, 0x4d, 0x73, 0x61, 0x74, 0x12, 0x2a, 0x0a, 0x11, 0x6f, - 0x75, 0x74, 0x67, 0x6f, 0x69, 0x6e, 0x67, 0x5f, 0x61, 0x6d, 0x74, 0x5f, 0x6d, 0x73, 0x61, 0x74, - 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0f, 0x6f, 0x75, 0x74, 0x67, 0x6f, 0x69, 0x6e, 0x67, - 0x41, 0x6d, 0x74, 0x4d, 0x73, 0x61, 0x74, 0x22, 0x37, 0x0a, 0x0c, 0x46, 0x6f, 0x72, 0x77, 0x61, - 0x72, 0x64, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x27, 0x0a, 0x04, 0x69, 0x6e, 0x66, 0x6f, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, - 0x63, 0x2e, 0x48, 0x74, 0x6c, 0x63, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x04, 0x69, 0x6e, 0x66, 0x6f, - 0x22, 0x12, 0x0a, 0x10, 0x46, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x46, 0x61, 0x69, 0x6c, 0x45, - 0x76, 0x65, 0x6e, 0x74, 0x22, 0x29, 0x0a, 0x0b, 0x53, 0x65, 0x74, 0x74, 0x6c, 0x65, 0x45, 0x76, - 0x65, 0x6e, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x72, 0x65, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x70, 0x72, 0x65, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x22, - 0x46, 0x0a, 0x0e, 0x46, 0x69, 0x6e, 0x61, 0x6c, 0x48, 0x74, 0x6c, 0x63, 0x45, 0x76, 0x65, 0x6e, - 0x74, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x65, 0x74, 0x74, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x08, 0x52, 0x07, 0x73, 0x65, 0x74, 0x74, 0x6c, 0x65, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x6f, - 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x6f, - 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x22, 0x11, 0x0a, 0x0f, 0x53, 0x75, 0x62, 0x73, 0x63, - 0x72, 0x69, 0x62, 0x65, 0x64, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x22, 0xdf, 0x01, 0x0a, 0x0d, 0x4c, - 0x69, 0x6e, 0x6b, 0x46, 0x61, 0x69, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x27, 0x0a, 0x04, - 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x72, 0x6f, 0x75, - 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x48, 0x74, 0x6c, 0x63, 0x49, 0x6e, 0x66, 0x6f, 0x52, - 0x04, 0x69, 0x6e, 0x66, 0x6f, 0x12, 0x3d, 0x0a, 0x0c, 0x77, 0x69, 0x72, 0x65, 0x5f, 0x66, 0x61, - 0x69, 0x6c, 0x75, 0x72, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1a, 0x2e, 0x6c, 0x6e, - 0x72, 0x70, 0x63, 0x2e, 0x46, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x2e, 0x46, 0x61, 0x69, 0x6c, - 0x75, 0x72, 0x65, 0x43, 0x6f, 0x64, 0x65, 0x52, 0x0b, 0x77, 0x69, 0x72, 0x65, 0x46, 0x61, 0x69, - 0x6c, 0x75, 0x72, 0x65, 0x12, 0x3f, 0x0a, 0x0e, 0x66, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x5f, - 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x18, 0x2e, 0x72, - 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, - 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x52, 0x0d, 0x66, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x44, - 0x65, 0x74, 0x61, 0x69, 0x6c, 0x12, 0x25, 0x0a, 0x0e, 0x66, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, - 0x5f, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x66, - 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x22, 0x8a, 0x01, 0x0a, - 0x0d, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x2d, - 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x17, 0x2e, - 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, - 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x1a, 0x0a, - 0x08, 0x70, 0x72, 0x65, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, - 0x08, 0x70, 0x72, 0x65, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x12, 0x28, 0x0a, 0x05, 0x68, 0x74, 0x6c, - 0x63, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, - 0x2e, 0x48, 0x54, 0x4c, 0x43, 0x41, 0x74, 0x74, 0x65, 0x6d, 0x70, 0x74, 0x52, 0x05, 0x68, 0x74, - 0x6c, 0x63, 0x73, 0x4a, 0x04, 0x08, 0x03, 0x10, 0x04, 0x22, 0x3e, 0x0a, 0x0a, 0x43, 0x69, 0x72, - 0x63, 0x75, 0x69, 0x74, 0x4b, 0x65, 0x79, 0x12, 0x17, 0x0a, 0x07, 0x63, 0x68, 0x61, 0x6e, 0x5f, - 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x63, 0x68, 0x61, 0x6e, 0x49, 0x64, - 0x12, 0x17, 0x0a, 0x07, 0x68, 0x74, 0x6c, 0x63, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x04, 0x52, 0x06, 0x68, 0x74, 0x6c, 0x63, 0x49, 0x64, 0x22, 0xe9, 0x04, 0x0a, 0x1b, 0x46, 0x6f, - 0x72, 0x77, 0x61, 0x72, 0x64, 0x48, 0x74, 0x6c, 0x63, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, - 0x70, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x47, 0x0a, 0x14, 0x69, 0x6e, 0x63, - 0x6f, 0x6d, 0x69, 0x6e, 0x67, 0x5f, 0x63, 0x69, 0x72, 0x63, 0x75, 0x69, 0x74, 0x5f, 0x6b, 0x65, - 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, - 0x72, 0x70, 0x63, 0x2e, 0x43, 0x69, 0x72, 0x63, 0x75, 0x69, 0x74, 0x4b, 0x65, 0x79, 0x52, 0x12, - 0x69, 0x6e, 0x63, 0x6f, 0x6d, 0x69, 0x6e, 0x67, 0x43, 0x69, 0x72, 0x63, 0x75, 0x69, 0x74, 0x4b, - 0x65, 0x79, 0x12, 0x30, 0x0a, 0x14, 0x69, 0x6e, 0x63, 0x6f, 0x6d, 0x69, 0x6e, 0x67, 0x5f, 0x61, - 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, - 0x52, 0x12, 0x69, 0x6e, 0x63, 0x6f, 0x6d, 0x69, 0x6e, 0x67, 0x41, 0x6d, 0x6f, 0x75, 0x6e, 0x74, - 0x4d, 0x73, 0x61, 0x74, 0x12, 0x27, 0x0a, 0x0f, 0x69, 0x6e, 0x63, 0x6f, 0x6d, 0x69, 0x6e, 0x67, - 0x5f, 0x65, 0x78, 0x70, 0x69, 0x72, 0x79, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0e, 0x69, - 0x6e, 0x63, 0x6f, 0x6d, 0x69, 0x6e, 0x67, 0x45, 0x78, 0x70, 0x69, 0x72, 0x79, 0x12, 0x21, 0x0a, - 0x0c, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x48, 0x61, 0x73, 0x68, - 0x12, 0x3b, 0x0a, 0x1a, 0x6f, 0x75, 0x74, 0x67, 0x6f, 0x69, 0x6e, 0x67, 0x5f, 0x72, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x65, 0x64, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x07, - 0x20, 0x01, 0x28, 0x04, 0x52, 0x17, 0x6f, 0x75, 0x74, 0x67, 0x6f, 0x69, 0x6e, 0x67, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x65, 0x64, 0x43, 0x68, 0x61, 0x6e, 0x49, 0x64, 0x12, 0x30, 0x0a, - 0x14, 0x6f, 0x75, 0x74, 0x67, 0x6f, 0x69, 0x6e, 0x67, 0x5f, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, - 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x12, 0x6f, 0x75, 0x74, - 0x67, 0x6f, 0x69, 0x6e, 0x67, 0x41, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x4d, 0x73, 0x61, 0x74, 0x12, - 0x27, 0x0a, 0x0f, 0x6f, 0x75, 0x74, 0x67, 0x6f, 0x69, 0x6e, 0x67, 0x5f, 0x65, 0x78, 0x70, 0x69, - 0x72, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0e, 0x6f, 0x75, 0x74, 0x67, 0x6f, 0x69, - 0x6e, 0x67, 0x45, 0x78, 0x70, 0x69, 0x72, 0x79, 0x12, 0x60, 0x0a, 0x0e, 0x63, 0x75, 0x73, 0x74, - 0x6f, 0x6d, 0x5f, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x18, 0x08, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x39, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x6f, 0x72, - 0x77, 0x61, 0x72, 0x64, 0x48, 0x74, 0x6c, 0x63, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, - 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x52, - 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0d, 0x63, 0x75, 0x73, - 0x74, 0x6f, 0x6d, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x6f, 0x6e, - 0x69, 0x6f, 0x6e, 0x5f, 0x62, 0x6c, 0x6f, 0x62, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, - 0x6f, 0x6e, 0x69, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x62, 0x12, 0x28, 0x0a, 0x10, 0x61, 0x75, 0x74, - 0x6f, 0x5f, 0x66, 0x61, 0x69, 0x6c, 0x5f, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x0a, 0x20, - 0x01, 0x28, 0x05, 0x52, 0x0e, 0x61, 0x75, 0x74, 0x6f, 0x46, 0x61, 0x69, 0x6c, 0x48, 0x65, 0x69, - 0x67, 0x68, 0x74, 0x1a, 0x40, 0x0a, 0x12, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x52, 0x65, 0x63, - 0x6f, 0x72, 0x64, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, - 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, - 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xb9, 0x04, 0x0a, 0x1c, 0x46, 0x6f, 0x72, 0x77, 0x61, 0x72, - 0x64, 0x48, 0x74, 0x6c, 0x63, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x47, 0x0a, 0x14, 0x69, 0x6e, 0x63, 0x6f, 0x6d, 0x69, - 0x6e, 0x67, 0x5f, 0x63, 0x69, 0x72, 0x63, 0x75, 0x69, 0x74, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, - 0x2e, 0x43, 0x69, 0x72, 0x63, 0x75, 0x69, 0x74, 0x4b, 0x65, 0x79, 0x52, 0x12, 0x69, 0x6e, 0x63, - 0x6f, 0x6d, 0x69, 0x6e, 0x67, 0x43, 0x69, 0x72, 0x63, 0x75, 0x69, 0x74, 0x4b, 0x65, 0x79, 0x12, - 0x3b, 0x0a, 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, - 0x23, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x65, 0x73, 0x6f, - 0x6c, 0x76, 0x65, 0x48, 0x6f, 0x6c, 0x64, 0x46, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x41, 0x63, - 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1a, 0x0a, 0x08, - 0x70, 0x72, 0x65, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, - 0x70, 0x72, 0x65, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x12, 0x27, 0x0a, 0x0f, 0x66, 0x61, 0x69, 0x6c, - 0x75, 0x72, 0x65, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, - 0x0c, 0x52, 0x0e, 0x66, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, - 0x65, 0x12, 0x3d, 0x0a, 0x0c, 0x66, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x5f, 0x63, 0x6f, 0x64, - 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, - 0x46, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x2e, 0x46, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x43, - 0x6f, 0x64, 0x65, 0x52, 0x0b, 0x66, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x43, 0x6f, 0x64, 0x65, - 0x12, 0x24, 0x0a, 0x0e, 0x69, 0x6e, 0x5f, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x6d, 0x73, - 0x61, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0c, 0x69, 0x6e, 0x41, 0x6d, 0x6f, 0x75, - 0x6e, 0x74, 0x4d, 0x73, 0x61, 0x74, 0x12, 0x26, 0x0a, 0x0f, 0x6f, 0x75, 0x74, 0x5f, 0x61, 0x6d, - 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x04, 0x52, - 0x0d, 0x6f, 0x75, 0x74, 0x41, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x4d, 0x73, 0x61, 0x74, 0x12, 0x78, - 0x0a, 0x17, 0x6f, 0x75, 0x74, 0x5f, 0x77, 0x69, 0x72, 0x65, 0x5f, 0x63, 0x75, 0x73, 0x74, 0x6f, - 0x6d, 0x5f, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x18, 0x08, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x41, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x6f, 0x72, 0x77, - 0x61, 0x72, 0x64, 0x48, 0x74, 0x6c, 0x63, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x4f, 0x75, 0x74, 0x57, 0x69, 0x72, 0x65, + 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x22, 0x59, + 0x0a, 0x1e, 0x53, 0x65, 0x74, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x6e, 0x74, + 0x72, 0x6f, 0x6c, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x12, 0x37, 0x0a, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x1f, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x4d, 0x69, 0x73, + 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x43, 0x6f, 0x6e, 0x66, 0x69, + 0x67, 0x52, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x22, 0x21, 0x0a, 0x1f, 0x53, 0x65, 0x74, + 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x43, 0x6f, + 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x89, 0x04, 0x0a, + 0x14, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x43, + 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x2e, 0x0a, 0x11, 0x68, 0x61, 0x6c, 0x66, 0x5f, 0x6c, 0x69, + 0x66, 0x65, 0x5f, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, + 0x42, 0x02, 0x18, 0x01, 0x52, 0x0f, 0x68, 0x61, 0x6c, 0x66, 0x4c, 0x69, 0x66, 0x65, 0x53, 0x65, + 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x12, 0x2b, 0x0a, 0x0f, 0x68, 0x6f, 0x70, 0x5f, 0x70, 0x72, 0x6f, + 0x62, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x02, 0x42, 0x02, + 0x18, 0x01, 0x52, 0x0e, 0x68, 0x6f, 0x70, 0x50, 0x72, 0x6f, 0x62, 0x61, 0x62, 0x69, 0x6c, 0x69, + 0x74, 0x79, 0x12, 0x1a, 0x0a, 0x06, 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x02, 0x42, 0x02, 0x18, 0x01, 0x52, 0x06, 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x12, 0x36, + 0x0a, 0x17, 0x6d, 0x61, 0x78, 0x69, 0x6d, 0x75, 0x6d, 0x5f, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, + 0x74, 0x5f, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, + 0x15, 0x6d, 0x61, 0x78, 0x69, 0x6d, 0x75, 0x6d, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x52, + 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x12, 0x43, 0x0a, 0x1e, 0x6d, 0x69, 0x6e, 0x69, 0x6d, 0x75, + 0x6d, 0x5f, 0x66, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x5f, 0x72, 0x65, 0x6c, 0x61, 0x78, 0x5f, + 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x1b, + 0x6d, 0x69, 0x6e, 0x69, 0x6d, 0x75, 0x6d, 0x46, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x52, 0x65, + 0x6c, 0x61, 0x78, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x12, 0x46, 0x0a, 0x05, 0x6d, + 0x6f, 0x64, 0x65, 0x6c, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x30, 0x2e, 0x72, 0x6f, 0x75, + 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x6f, + 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x50, 0x72, 0x6f, 0x62, + 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x4d, 0x6f, 0x64, 0x65, 0x6c, 0x52, 0x05, 0x6d, 0x6f, + 0x64, 0x65, 0x6c, 0x12, 0x38, 0x0a, 0x07, 0x61, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x18, 0x07, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, + 0x2e, 0x41, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, + 0x72, 0x73, 0x48, 0x00, 0x52, 0x07, 0x61, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x12, 0x38, 0x0a, + 0x07, 0x62, 0x69, 0x6d, 0x6f, 0x64, 0x61, 0x6c, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, + 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x42, 0x69, 0x6d, 0x6f, 0x64, + 0x61, 0x6c, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x48, 0x00, 0x52, 0x07, + 0x62, 0x69, 0x6d, 0x6f, 0x64, 0x61, 0x6c, 0x22, 0x2c, 0x0a, 0x10, 0x50, 0x72, 0x6f, 0x62, 0x61, + 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x4d, 0x6f, 0x64, 0x65, 0x6c, 0x12, 0x0b, 0x0a, 0x07, 0x41, + 0x50, 0x52, 0x49, 0x4f, 0x52, 0x49, 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x42, 0x49, 0x4d, 0x4f, + 0x44, 0x41, 0x4c, 0x10, 0x01, 0x42, 0x11, 0x0a, 0x0f, 0x45, 0x73, 0x74, 0x69, 0x6d, 0x61, 0x74, + 0x6f, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x22, 0x72, 0x0a, 0x11, 0x42, 0x69, 0x6d, 0x6f, + 0x64, 0x61, 0x6c, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x12, 0x1f, 0x0a, + 0x0b, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x01, 0x52, 0x0a, 0x6e, 0x6f, 0x64, 0x65, 0x57, 0x65, 0x69, 0x67, 0x68, 0x74, 0x12, 0x1d, + 0x0a, 0x0a, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x04, 0x52, 0x09, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x4d, 0x73, 0x61, 0x74, 0x12, 0x1d, 0x0a, + 0x0a, 0x64, 0x65, 0x63, 0x61, 0x79, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x04, 0x52, 0x09, 0x64, 0x65, 0x63, 0x61, 0x79, 0x54, 0x69, 0x6d, 0x65, 0x22, 0xad, 0x01, 0x0a, + 0x11, 0x41, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, + 0x72, 0x73, 0x12, 0x2a, 0x0a, 0x11, 0x68, 0x61, 0x6c, 0x66, 0x5f, 0x6c, 0x69, 0x66, 0x65, 0x5f, + 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0f, 0x68, + 0x61, 0x6c, 0x66, 0x4c, 0x69, 0x66, 0x65, 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x12, 0x27, + 0x0a, 0x0f, 0x68, 0x6f, 0x70, 0x5f, 0x70, 0x72, 0x6f, 0x62, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, + 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x01, 0x52, 0x0e, 0x68, 0x6f, 0x70, 0x50, 0x72, 0x6f, 0x62, + 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x12, 0x16, 0x0a, 0x06, 0x77, 0x65, 0x69, 0x67, 0x68, + 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x01, 0x52, 0x06, 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x12, + 0x2b, 0x0a, 0x11, 0x63, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x5f, 0x66, 0x72, 0x61, 0x63, + 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x01, 0x52, 0x10, 0x63, 0x61, 0x70, 0x61, + 0x63, 0x69, 0x74, 0x79, 0x46, 0x72, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x6a, 0x0a, 0x17, + 0x51, 0x75, 0x65, 0x72, 0x79, 0x50, 0x72, 0x6f, 0x62, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x66, 0x72, 0x6f, 0x6d, 0x5f, + 0x6e, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x66, 0x72, 0x6f, 0x6d, + 0x4e, 0x6f, 0x64, 0x65, 0x12, 0x17, 0x0a, 0x07, 0x74, 0x6f, 0x5f, 0x6e, 0x6f, 0x64, 0x65, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x74, 0x6f, 0x4e, 0x6f, 0x64, 0x65, 0x12, 0x19, 0x0a, + 0x08, 0x61, 0x6d, 0x74, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, + 0x07, 0x61, 0x6d, 0x74, 0x4d, 0x73, 0x61, 0x74, 0x22, 0x6b, 0x0a, 0x18, 0x51, 0x75, 0x65, 0x72, + 0x79, 0x50, 0x72, 0x6f, 0x62, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x70, 0x72, 0x6f, 0x62, 0x61, 0x62, 0x69, 0x6c, + 0x69, 0x74, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x01, 0x52, 0x0b, 0x70, 0x72, 0x6f, 0x62, 0x61, + 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x12, 0x2d, 0x0a, 0x07, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, + 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, + 0x72, 0x70, 0x63, 0x2e, 0x50, 0x61, 0x69, 0x72, 0x44, 0x61, 0x74, 0x61, 0x52, 0x07, 0x68, 0x69, + 0x73, 0x74, 0x6f, 0x72, 0x79, 0x22, 0x86, 0x03, 0x0a, 0x11, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x52, + 0x6f, 0x75, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x61, + 0x6d, 0x74, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x61, + 0x6d, 0x74, 0x4d, 0x73, 0x61, 0x74, 0x12, 0x28, 0x0a, 0x10, 0x66, 0x69, 0x6e, 0x61, 0x6c, 0x5f, + 0x63, 0x6c, 0x74, 0x76, 0x5f, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, + 0x52, 0x0e, 0x66, 0x69, 0x6e, 0x61, 0x6c, 0x43, 0x6c, 0x74, 0x76, 0x44, 0x65, 0x6c, 0x74, 0x61, + 0x12, 0x2c, 0x0a, 0x10, 0x6f, 0x75, 0x74, 0x67, 0x6f, 0x69, 0x6e, 0x67, 0x5f, 0x63, 0x68, 0x61, + 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x42, 0x02, 0x30, 0x01, 0x52, 0x0e, + 0x6f, 0x75, 0x74, 0x67, 0x6f, 0x69, 0x6e, 0x67, 0x43, 0x68, 0x61, 0x6e, 0x49, 0x64, 0x12, 0x1f, + 0x0a, 0x0b, 0x68, 0x6f, 0x70, 0x5f, 0x70, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x73, 0x18, 0x04, 0x20, + 0x03, 0x28, 0x0c, 0x52, 0x0a, 0x68, 0x6f, 0x70, 0x50, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x73, 0x12, + 0x21, 0x0a, 0x0c, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x18, + 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x41, 0x64, + 0x64, 0x72, 0x12, 0x70, 0x0a, 0x18, 0x66, 0x69, 0x72, 0x73, 0x74, 0x5f, 0x68, 0x6f, 0x70, 0x5f, + 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5f, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x18, 0x06, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x37, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, + 0x2e, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x2e, 0x46, 0x69, 0x72, 0x73, 0x74, 0x48, 0x6f, 0x70, 0x43, 0x75, 0x73, 0x74, 0x6f, + 0x6d, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x15, 0x66, + 0x69, 0x72, 0x73, 0x74, 0x48, 0x6f, 0x70, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x52, 0x65, 0x63, + 0x6f, 0x72, 0x64, 0x73, 0x1a, 0x48, 0x0a, 0x1a, 0x46, 0x69, 0x72, 0x73, 0x74, 0x48, 0x6f, 0x70, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x45, 0x6e, 0x74, - 0x72, 0x79, 0x52, 0x14, 0x6f, 0x75, 0x74, 0x57, 0x69, 0x72, 0x65, 0x43, 0x75, 0x73, 0x74, 0x6f, - 0x6d, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x1a, 0x47, 0x0a, 0x19, 0x4f, 0x75, 0x74, 0x57, + 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, + 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0c, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x38, + 0x0a, 0x12, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x22, 0x0a, 0x05, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x6f, 0x75, 0x74, + 0x65, 0x52, 0x05, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x22, 0x1c, 0x0a, 0x1a, 0x53, 0x75, 0x62, 0x73, + 0x63, 0x72, 0x69, 0x62, 0x65, 0x48, 0x74, 0x6c, 0x63, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x86, 0x06, 0x0a, 0x09, 0x48, 0x74, 0x6c, 0x63, 0x45, + 0x76, 0x65, 0x6e, 0x74, 0x12, 0x2e, 0x0a, 0x13, 0x69, 0x6e, 0x63, 0x6f, 0x6d, 0x69, 0x6e, 0x67, + 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x04, 0x52, 0x11, 0x69, 0x6e, 0x63, 0x6f, 0x6d, 0x69, 0x6e, 0x67, 0x43, 0x68, 0x61, 0x6e, 0x6e, + 0x65, 0x6c, 0x49, 0x64, 0x12, 0x2e, 0x0a, 0x13, 0x6f, 0x75, 0x74, 0x67, 0x6f, 0x69, 0x6e, 0x67, + 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x04, 0x52, 0x11, 0x6f, 0x75, 0x74, 0x67, 0x6f, 0x69, 0x6e, 0x67, 0x43, 0x68, 0x61, 0x6e, 0x6e, + 0x65, 0x6c, 0x49, 0x64, 0x12, 0x28, 0x0a, 0x10, 0x69, 0x6e, 0x63, 0x6f, 0x6d, 0x69, 0x6e, 0x67, + 0x5f, 0x68, 0x74, 0x6c, 0x63, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0e, + 0x69, 0x6e, 0x63, 0x6f, 0x6d, 0x69, 0x6e, 0x67, 0x48, 0x74, 0x6c, 0x63, 0x49, 0x64, 0x12, 0x28, + 0x0a, 0x10, 0x6f, 0x75, 0x74, 0x67, 0x6f, 0x69, 0x6e, 0x67, 0x5f, 0x68, 0x74, 0x6c, 0x63, 0x5f, + 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0e, 0x6f, 0x75, 0x74, 0x67, 0x6f, 0x69, + 0x6e, 0x67, 0x48, 0x74, 0x6c, 0x63, 0x49, 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x74, 0x69, 0x6d, 0x65, + 0x73, 0x74, 0x61, 0x6d, 0x70, 0x5f, 0x6e, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, + 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x4e, 0x73, 0x12, 0x3d, 0x0a, 0x0a, 0x65, + 0x76, 0x65, 0x6e, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0e, 0x32, + 0x1e, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x48, 0x74, 0x6c, 0x63, + 0x45, 0x76, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x52, + 0x09, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x3e, 0x0a, 0x0d, 0x66, 0x6f, + 0x72, 0x77, 0x61, 0x72, 0x64, 0x5f, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x17, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x6f, + 0x72, 0x77, 0x61, 0x72, 0x64, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x0c, 0x66, 0x6f, + 0x72, 0x77, 0x61, 0x72, 0x64, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x4b, 0x0a, 0x12, 0x66, 0x6f, + 0x72, 0x77, 0x61, 0x72, 0x64, 0x5f, 0x66, 0x61, 0x69, 0x6c, 0x5f, 0x65, 0x76, 0x65, 0x6e, 0x74, + 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, + 0x70, 0x63, 0x2e, 0x46, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x46, 0x61, 0x69, 0x6c, 0x45, 0x76, + 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x10, 0x66, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x46, 0x61, + 0x69, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x3b, 0x0a, 0x0c, 0x73, 0x65, 0x74, 0x74, 0x6c, + 0x65, 0x5f, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, + 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x74, 0x74, 0x6c, 0x65, + 0x45, 0x76, 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x0b, 0x73, 0x65, 0x74, 0x74, 0x6c, 0x65, 0x45, + 0x76, 0x65, 0x6e, 0x74, 0x12, 0x42, 0x0a, 0x0f, 0x6c, 0x69, 0x6e, 0x6b, 0x5f, 0x66, 0x61, 0x69, + 0x6c, 0x5f, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, + 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x6e, 0x6b, 0x46, 0x61, + 0x69, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x0d, 0x6c, 0x69, 0x6e, 0x6b, 0x46, + 0x61, 0x69, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x47, 0x0a, 0x10, 0x73, 0x75, 0x62, 0x73, + 0x63, 0x72, 0x69, 0x62, 0x65, 0x64, 0x5f, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x18, 0x0b, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x53, + 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x64, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x48, 0x00, + 0x52, 0x0f, 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x64, 0x45, 0x76, 0x65, 0x6e, + 0x74, 0x12, 0x45, 0x0a, 0x10, 0x66, 0x69, 0x6e, 0x61, 0x6c, 0x5f, 0x68, 0x74, 0x6c, 0x63, 0x5f, + 0x65, 0x76, 0x65, 0x6e, 0x74, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x72, 0x6f, + 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x69, 0x6e, 0x61, 0x6c, 0x48, 0x74, 0x6c, + 0x63, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x0e, 0x66, 0x69, 0x6e, 0x61, 0x6c, 0x48, + 0x74, 0x6c, 0x63, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x22, 0x3c, 0x0a, 0x09, 0x45, 0x76, 0x65, 0x6e, + 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, + 0x10, 0x00, 0x12, 0x08, 0x0a, 0x04, 0x53, 0x45, 0x4e, 0x44, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, + 0x52, 0x45, 0x43, 0x45, 0x49, 0x56, 0x45, 0x10, 0x02, 0x12, 0x0b, 0x0a, 0x07, 0x46, 0x4f, 0x52, + 0x57, 0x41, 0x52, 0x44, 0x10, 0x03, 0x42, 0x07, 0x0a, 0x05, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x22, + 0xbc, 0x01, 0x0a, 0x08, 0x48, 0x74, 0x6c, 0x63, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x2b, 0x0a, 0x11, + 0x69, 0x6e, 0x63, 0x6f, 0x6d, 0x69, 0x6e, 0x67, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x6c, 0x6f, 0x63, + 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x10, 0x69, 0x6e, 0x63, 0x6f, 0x6d, 0x69, 0x6e, + 0x67, 0x54, 0x69, 0x6d, 0x65, 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x2b, 0x0a, 0x11, 0x6f, 0x75, 0x74, + 0x67, 0x6f, 0x69, 0x6e, 0x67, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x6c, 0x6f, 0x63, 0x6b, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x0d, 0x52, 0x10, 0x6f, 0x75, 0x74, 0x67, 0x6f, 0x69, 0x6e, 0x67, 0x54, 0x69, + 0x6d, 0x65, 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x2a, 0x0a, 0x11, 0x69, 0x6e, 0x63, 0x6f, 0x6d, 0x69, + 0x6e, 0x67, 0x5f, 0x61, 0x6d, 0x74, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x04, 0x52, 0x0f, 0x69, 0x6e, 0x63, 0x6f, 0x6d, 0x69, 0x6e, 0x67, 0x41, 0x6d, 0x74, 0x4d, 0x73, + 0x61, 0x74, 0x12, 0x2a, 0x0a, 0x11, 0x6f, 0x75, 0x74, 0x67, 0x6f, 0x69, 0x6e, 0x67, 0x5f, 0x61, + 0x6d, 0x74, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0f, 0x6f, + 0x75, 0x74, 0x67, 0x6f, 0x69, 0x6e, 0x67, 0x41, 0x6d, 0x74, 0x4d, 0x73, 0x61, 0x74, 0x22, 0x37, + 0x0a, 0x0c, 0x46, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x27, + 0x0a, 0x04, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x72, + 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x48, 0x74, 0x6c, 0x63, 0x49, 0x6e, 0x66, + 0x6f, 0x52, 0x04, 0x69, 0x6e, 0x66, 0x6f, 0x22, 0x12, 0x0a, 0x10, 0x46, 0x6f, 0x72, 0x77, 0x61, + 0x72, 0x64, 0x46, 0x61, 0x69, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x22, 0x29, 0x0a, 0x0b, 0x53, + 0x65, 0x74, 0x74, 0x6c, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x72, + 0x65, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x70, 0x72, + 0x65, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x22, 0x46, 0x0a, 0x0e, 0x46, 0x69, 0x6e, 0x61, 0x6c, 0x48, + 0x74, 0x6c, 0x63, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x65, 0x74, 0x74, + 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x73, 0x65, 0x74, 0x74, 0x6c, + 0x65, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x6f, 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x6f, 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x22, 0x11, + 0x0a, 0x0f, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x64, 0x45, 0x76, 0x65, 0x6e, + 0x74, 0x22, 0xdf, 0x01, 0x0a, 0x0d, 0x4c, 0x69, 0x6e, 0x6b, 0x46, 0x61, 0x69, 0x6c, 0x45, 0x76, + 0x65, 0x6e, 0x74, 0x12, 0x27, 0x0a, 0x04, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x13, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x48, 0x74, + 0x6c, 0x63, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x04, 0x69, 0x6e, 0x66, 0x6f, 0x12, 0x3d, 0x0a, 0x0c, + 0x77, 0x69, 0x72, 0x65, 0x5f, 0x66, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0e, 0x32, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x61, 0x69, 0x6c, 0x75, + 0x72, 0x65, 0x2e, 0x46, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x43, 0x6f, 0x64, 0x65, 0x52, 0x0b, + 0x77, 0x69, 0x72, 0x65, 0x46, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x12, 0x3f, 0x0a, 0x0e, 0x66, + 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x5f, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x0e, 0x32, 0x18, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, + 0x46, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x52, 0x0d, 0x66, + 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x12, 0x25, 0x0a, 0x0e, + 0x66, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x5f, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x66, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x53, 0x74, 0x72, + 0x69, 0x6e, 0x67, 0x22, 0x8a, 0x01, 0x0a, 0x0d, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x53, + 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x2d, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x0e, 0x32, 0x17, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, + 0x2e, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x05, 0x73, + 0x74, 0x61, 0x74, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x72, 0x65, 0x69, 0x6d, 0x61, 0x67, 0x65, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x70, 0x72, 0x65, 0x69, 0x6d, 0x61, 0x67, 0x65, + 0x12, 0x28, 0x0a, 0x05, 0x68, 0x74, 0x6c, 0x63, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x12, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x48, 0x54, 0x4c, 0x43, 0x41, 0x74, 0x74, 0x65, + 0x6d, 0x70, 0x74, 0x52, 0x05, 0x68, 0x74, 0x6c, 0x63, 0x73, 0x4a, 0x04, 0x08, 0x03, 0x10, 0x04, + 0x22, 0x3e, 0x0a, 0x0a, 0x43, 0x69, 0x72, 0x63, 0x75, 0x69, 0x74, 0x4b, 0x65, 0x79, 0x12, 0x17, + 0x0a, 0x07, 0x63, 0x68, 0x61, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, + 0x06, 0x63, 0x68, 0x61, 0x6e, 0x49, 0x64, 0x12, 0x17, 0x0a, 0x07, 0x68, 0x74, 0x6c, 0x63, 0x5f, + 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x68, 0x74, 0x6c, 0x63, 0x49, 0x64, + 0x22, 0xa7, 0x06, 0x0a, 0x1b, 0x46, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x48, 0x74, 0x6c, 0x63, + 0x49, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x12, 0x47, 0x0a, 0x14, 0x69, 0x6e, 0x63, 0x6f, 0x6d, 0x69, 0x6e, 0x67, 0x5f, 0x63, 0x69, 0x72, + 0x63, 0x75, 0x69, 0x74, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, + 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x69, 0x72, 0x63, 0x75, + 0x69, 0x74, 0x4b, 0x65, 0x79, 0x52, 0x12, 0x69, 0x6e, 0x63, 0x6f, 0x6d, 0x69, 0x6e, 0x67, 0x43, + 0x69, 0x72, 0x63, 0x75, 0x69, 0x74, 0x4b, 0x65, 0x79, 0x12, 0x30, 0x0a, 0x14, 0x69, 0x6e, 0x63, + 0x6f, 0x6d, 0x69, 0x6e, 0x67, 0x5f, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x6d, 0x73, 0x61, + 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x12, 0x69, 0x6e, 0x63, 0x6f, 0x6d, 0x69, 0x6e, + 0x67, 0x41, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x4d, 0x73, 0x61, 0x74, 0x12, 0x27, 0x0a, 0x0f, 0x69, + 0x6e, 0x63, 0x6f, 0x6d, 0x69, 0x6e, 0x67, 0x5f, 0x65, 0x78, 0x70, 0x69, 0x72, 0x79, 0x18, 0x06, + 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0e, 0x69, 0x6e, 0x63, 0x6f, 0x6d, 0x69, 0x6e, 0x67, 0x45, 0x78, + 0x70, 0x69, 0x72, 0x79, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x5f, + 0x68, 0x61, 0x73, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x70, 0x61, 0x79, 0x6d, + 0x65, 0x6e, 0x74, 0x48, 0x61, 0x73, 0x68, 0x12, 0x3b, 0x0a, 0x1a, 0x6f, 0x75, 0x74, 0x67, 0x6f, + 0x69, 0x6e, 0x67, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x65, 0x64, 0x5f, 0x63, 0x68, + 0x61, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x07, 0x20, 0x01, 0x28, 0x04, 0x52, 0x17, 0x6f, 0x75, 0x74, + 0x67, 0x6f, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x65, 0x64, 0x43, 0x68, + 0x61, 0x6e, 0x49, 0x64, 0x12, 0x30, 0x0a, 0x14, 0x6f, 0x75, 0x74, 0x67, 0x6f, 0x69, 0x6e, 0x67, + 0x5f, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x04, 0x52, 0x12, 0x6f, 0x75, 0x74, 0x67, 0x6f, 0x69, 0x6e, 0x67, 0x41, 0x6d, 0x6f, 0x75, + 0x6e, 0x74, 0x4d, 0x73, 0x61, 0x74, 0x12, 0x27, 0x0a, 0x0f, 0x6f, 0x75, 0x74, 0x67, 0x6f, 0x69, + 0x6e, 0x67, 0x5f, 0x65, 0x78, 0x70, 0x69, 0x72, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, + 0x0e, 0x6f, 0x75, 0x74, 0x67, 0x6f, 0x69, 0x6e, 0x67, 0x45, 0x78, 0x70, 0x69, 0x72, 0x79, 0x12, + 0x60, 0x0a, 0x0e, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5f, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, + 0x73, 0x18, 0x08, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x39, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, + 0x72, 0x70, 0x63, 0x2e, 0x46, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x48, 0x74, 0x6c, 0x63, 0x49, + 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, + 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x45, 0x6e, 0x74, + 0x72, 0x79, 0x52, 0x0d, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, + 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x6f, 0x6e, 0x69, 0x6f, 0x6e, 0x5f, 0x62, 0x6c, 0x6f, 0x62, 0x18, + 0x09, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x6f, 0x6e, 0x69, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x62, + 0x12, 0x28, 0x0a, 0x10, 0x61, 0x75, 0x74, 0x6f, 0x5f, 0x66, 0x61, 0x69, 0x6c, 0x5f, 0x68, 0x65, + 0x69, 0x67, 0x68, 0x74, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0e, 0x61, 0x75, 0x74, 0x6f, + 0x46, 0x61, 0x69, 0x6c, 0x48, 0x65, 0x69, 0x67, 0x68, 0x74, 0x12, 0x74, 0x0a, 0x16, 0x69, 0x6e, + 0x5f, 0x77, 0x69, 0x72, 0x65, 0x5f, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5f, 0x72, 0x65, 0x63, + 0x6f, 0x72, 0x64, 0x73, 0x18, 0x0b, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x3f, 0x2e, 0x72, 0x6f, 0x75, + 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x48, 0x74, + 0x6c, 0x63, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x2e, 0x49, 0x6e, 0x57, 0x69, 0x72, 0x65, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x52, + 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x13, 0x69, 0x6e, 0x57, 0x69, 0x72, 0x65, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, - 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x04, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, - 0x01, 0x22, 0x82, 0x01, 0x0a, 0x17, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x43, 0x68, 0x61, 0x6e, - 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x32, 0x0a, - 0x0a, 0x63, 0x68, 0x61, 0x6e, 0x5f, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x13, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, - 0x6c, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x09, 0x63, 0x68, 0x61, 0x6e, 0x50, 0x6f, 0x69, 0x6e, - 0x74, 0x12, 0x33, 0x0a, 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x0e, 0x32, 0x1b, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, - 0x61, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x06, - 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x1a, 0x0a, 0x18, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, - 0x43, 0x68, 0x61, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x43, 0x0a, 0x11, 0x41, 0x64, 0x64, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x65, 0x73, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2e, 0x0a, 0x0a, 0x61, 0x6c, 0x69, 0x61, 0x73, - 0x5f, 0x6d, 0x61, 0x70, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x6c, 0x6e, - 0x72, 0x70, 0x63, 0x2e, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x4d, 0x61, 0x70, 0x52, 0x09, 0x61, 0x6c, - 0x69, 0x61, 0x73, 0x4d, 0x61, 0x70, 0x73, 0x22, 0x44, 0x0a, 0x12, 0x41, 0x64, 0x64, 0x41, 0x6c, - 0x69, 0x61, 0x73, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2e, 0x0a, - 0x0a, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x5f, 0x6d, 0x61, 0x70, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, - 0x0b, 0x32, 0x0f, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x4d, - 0x61, 0x70, 0x52, 0x09, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x4d, 0x61, 0x70, 0x73, 0x22, 0x46, 0x0a, - 0x14, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x65, 0x73, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2e, 0x0a, 0x0a, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x5f, 0x6d, - 0x61, 0x70, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x6c, 0x6e, 0x72, 0x70, - 0x63, 0x2e, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x4d, 0x61, 0x70, 0x52, 0x09, 0x61, 0x6c, 0x69, 0x61, - 0x73, 0x4d, 0x61, 0x70, 0x73, 0x22, 0x47, 0x0a, 0x15, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x41, - 0x6c, 0x69, 0x61, 0x73, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2e, - 0x0a, 0x0a, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x5f, 0x6d, 0x61, 0x70, 0x73, 0x18, 0x01, 0x20, 0x03, - 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x6c, 0x69, 0x61, 0x73, - 0x4d, 0x61, 0x70, 0x52, 0x09, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x4d, 0x61, 0x70, 0x73, 0x2a, 0x81, - 0x04, 0x0a, 0x0d, 0x46, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, - 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x0d, 0x0a, - 0x09, 0x4e, 0x4f, 0x5f, 0x44, 0x45, 0x54, 0x41, 0x49, 0x4c, 0x10, 0x01, 0x12, 0x10, 0x0a, 0x0c, - 0x4f, 0x4e, 0x49, 0x4f, 0x4e, 0x5f, 0x44, 0x45, 0x43, 0x4f, 0x44, 0x45, 0x10, 0x02, 0x12, 0x15, - 0x0a, 0x11, 0x4c, 0x49, 0x4e, 0x4b, 0x5f, 0x4e, 0x4f, 0x54, 0x5f, 0x45, 0x4c, 0x49, 0x47, 0x49, - 0x42, 0x4c, 0x45, 0x10, 0x03, 0x12, 0x14, 0x0a, 0x10, 0x4f, 0x4e, 0x5f, 0x43, 0x48, 0x41, 0x49, - 0x4e, 0x5f, 0x54, 0x49, 0x4d, 0x45, 0x4f, 0x55, 0x54, 0x10, 0x04, 0x12, 0x14, 0x0a, 0x10, 0x48, - 0x54, 0x4c, 0x43, 0x5f, 0x45, 0x58, 0x43, 0x45, 0x45, 0x44, 0x53, 0x5f, 0x4d, 0x41, 0x58, 0x10, - 0x05, 0x12, 0x18, 0x0a, 0x14, 0x49, 0x4e, 0x53, 0x55, 0x46, 0x46, 0x49, 0x43, 0x49, 0x45, 0x4e, - 0x54, 0x5f, 0x42, 0x41, 0x4c, 0x41, 0x4e, 0x43, 0x45, 0x10, 0x06, 0x12, 0x16, 0x0a, 0x12, 0x49, - 0x4e, 0x43, 0x4f, 0x4d, 0x50, 0x4c, 0x45, 0x54, 0x45, 0x5f, 0x46, 0x4f, 0x52, 0x57, 0x41, 0x52, - 0x44, 0x10, 0x07, 0x12, 0x13, 0x0a, 0x0f, 0x48, 0x54, 0x4c, 0x43, 0x5f, 0x41, 0x44, 0x44, 0x5f, - 0x46, 0x41, 0x49, 0x4c, 0x45, 0x44, 0x10, 0x08, 0x12, 0x15, 0x0a, 0x11, 0x46, 0x4f, 0x52, 0x57, - 0x41, 0x52, 0x44, 0x53, 0x5f, 0x44, 0x49, 0x53, 0x41, 0x42, 0x4c, 0x45, 0x44, 0x10, 0x09, 0x12, - 0x14, 0x0a, 0x10, 0x49, 0x4e, 0x56, 0x4f, 0x49, 0x43, 0x45, 0x5f, 0x43, 0x41, 0x4e, 0x43, 0x45, - 0x4c, 0x45, 0x44, 0x10, 0x0a, 0x12, 0x15, 0x0a, 0x11, 0x49, 0x4e, 0x56, 0x4f, 0x49, 0x43, 0x45, - 0x5f, 0x55, 0x4e, 0x44, 0x45, 0x52, 0x50, 0x41, 0x49, 0x44, 0x10, 0x0b, 0x12, 0x1b, 0x0a, 0x17, - 0x49, 0x4e, 0x56, 0x4f, 0x49, 0x43, 0x45, 0x5f, 0x45, 0x58, 0x50, 0x49, 0x52, 0x59, 0x5f, 0x54, - 0x4f, 0x4f, 0x5f, 0x53, 0x4f, 0x4f, 0x4e, 0x10, 0x0c, 0x12, 0x14, 0x0a, 0x10, 0x49, 0x4e, 0x56, - 0x4f, 0x49, 0x43, 0x45, 0x5f, 0x4e, 0x4f, 0x54, 0x5f, 0x4f, 0x50, 0x45, 0x4e, 0x10, 0x0d, 0x12, - 0x17, 0x0a, 0x13, 0x4d, 0x50, 0x50, 0x5f, 0x49, 0x4e, 0x56, 0x4f, 0x49, 0x43, 0x45, 0x5f, 0x54, - 0x49, 0x4d, 0x45, 0x4f, 0x55, 0x54, 0x10, 0x0e, 0x12, 0x14, 0x0a, 0x10, 0x41, 0x44, 0x44, 0x52, - 0x45, 0x53, 0x53, 0x5f, 0x4d, 0x49, 0x53, 0x4d, 0x41, 0x54, 0x43, 0x48, 0x10, 0x0f, 0x12, 0x16, - 0x0a, 0x12, 0x53, 0x45, 0x54, 0x5f, 0x54, 0x4f, 0x54, 0x41, 0x4c, 0x5f, 0x4d, 0x49, 0x53, 0x4d, - 0x41, 0x54, 0x43, 0x48, 0x10, 0x10, 0x12, 0x15, 0x0a, 0x11, 0x53, 0x45, 0x54, 0x5f, 0x54, 0x4f, - 0x54, 0x41, 0x4c, 0x5f, 0x54, 0x4f, 0x4f, 0x5f, 0x4c, 0x4f, 0x57, 0x10, 0x11, 0x12, 0x10, 0x0a, - 0x0c, 0x53, 0x45, 0x54, 0x5f, 0x4f, 0x56, 0x45, 0x52, 0x50, 0x41, 0x49, 0x44, 0x10, 0x12, 0x12, - 0x13, 0x0a, 0x0f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x5f, 0x49, 0x4e, 0x56, 0x4f, 0x49, - 0x43, 0x45, 0x10, 0x13, 0x12, 0x13, 0x0a, 0x0f, 0x49, 0x4e, 0x56, 0x41, 0x4c, 0x49, 0x44, 0x5f, - 0x4b, 0x45, 0x59, 0x53, 0x45, 0x4e, 0x44, 0x10, 0x14, 0x12, 0x13, 0x0a, 0x0f, 0x4d, 0x50, 0x50, - 0x5f, 0x49, 0x4e, 0x5f, 0x50, 0x52, 0x4f, 0x47, 0x52, 0x45, 0x53, 0x53, 0x10, 0x15, 0x12, 0x12, - 0x0a, 0x0e, 0x43, 0x49, 0x52, 0x43, 0x55, 0x4c, 0x41, 0x52, 0x5f, 0x52, 0x4f, 0x55, 0x54, 0x45, - 0x10, 0x16, 0x2a, 0xae, 0x01, 0x0a, 0x0c, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x53, 0x74, - 0x61, 0x74, 0x65, 0x12, 0x0d, 0x0a, 0x09, 0x49, 0x4e, 0x5f, 0x46, 0x4c, 0x49, 0x47, 0x48, 0x54, - 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x53, 0x55, 0x43, 0x43, 0x45, 0x45, 0x44, 0x45, 0x44, 0x10, - 0x01, 0x12, 0x12, 0x0a, 0x0e, 0x46, 0x41, 0x49, 0x4c, 0x45, 0x44, 0x5f, 0x54, 0x49, 0x4d, 0x45, - 0x4f, 0x55, 0x54, 0x10, 0x02, 0x12, 0x13, 0x0a, 0x0f, 0x46, 0x41, 0x49, 0x4c, 0x45, 0x44, 0x5f, - 0x4e, 0x4f, 0x5f, 0x52, 0x4f, 0x55, 0x54, 0x45, 0x10, 0x03, 0x12, 0x10, 0x0a, 0x0c, 0x46, 0x41, - 0x49, 0x4c, 0x45, 0x44, 0x5f, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, 0x04, 0x12, 0x24, 0x0a, 0x20, - 0x46, 0x41, 0x49, 0x4c, 0x45, 0x44, 0x5f, 0x49, 0x4e, 0x43, 0x4f, 0x52, 0x52, 0x45, 0x43, 0x54, - 0x5f, 0x50, 0x41, 0x59, 0x4d, 0x45, 0x4e, 0x54, 0x5f, 0x44, 0x45, 0x54, 0x41, 0x49, 0x4c, 0x53, - 0x10, 0x05, 0x12, 0x1f, 0x0a, 0x1b, 0x46, 0x41, 0x49, 0x4c, 0x45, 0x44, 0x5f, 0x49, 0x4e, 0x53, - 0x55, 0x46, 0x46, 0x49, 0x43, 0x49, 0x45, 0x4e, 0x54, 0x5f, 0x42, 0x41, 0x4c, 0x41, 0x4e, 0x43, - 0x45, 0x10, 0x06, 0x2a, 0x51, 0x0a, 0x18, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x48, 0x6f, - 0x6c, 0x64, 0x46, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, - 0x0a, 0x0a, 0x06, 0x53, 0x45, 0x54, 0x54, 0x4c, 0x45, 0x10, 0x00, 0x12, 0x08, 0x0a, 0x04, 0x46, - 0x41, 0x49, 0x4c, 0x10, 0x01, 0x12, 0x0a, 0x0a, 0x06, 0x52, 0x45, 0x53, 0x55, 0x4d, 0x45, 0x10, - 0x02, 0x12, 0x13, 0x0a, 0x0f, 0x52, 0x45, 0x53, 0x55, 0x4d, 0x45, 0x5f, 0x4d, 0x4f, 0x44, 0x49, - 0x46, 0x49, 0x45, 0x44, 0x10, 0x03, 0x2a, 0x35, 0x0a, 0x10, 0x43, 0x68, 0x61, 0x6e, 0x53, 0x74, - 0x61, 0x74, 0x75, 0x73, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x0a, 0x0a, 0x06, 0x45, 0x4e, - 0x41, 0x42, 0x4c, 0x45, 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x44, 0x49, 0x53, 0x41, 0x42, 0x4c, - 0x45, 0x10, 0x01, 0x12, 0x08, 0x0a, 0x04, 0x41, 0x55, 0x54, 0x4f, 0x10, 0x02, 0x32, 0xe8, 0x0d, - 0x0a, 0x06, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x12, 0x40, 0x0a, 0x0d, 0x53, 0x65, 0x6e, 0x64, - 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x56, 0x32, 0x12, 0x1d, 0x2e, 0x72, 0x6f, 0x75, 0x74, - 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, - 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x0e, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, - 0x2e, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x30, 0x01, 0x12, 0x42, 0x0a, 0x0e, 0x54, 0x72, - 0x61, 0x63, 0x6b, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x56, 0x32, 0x12, 0x1e, 0x2e, 0x72, - 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x54, 0x72, 0x61, 0x63, 0x6b, 0x50, 0x61, - 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x0e, 0x2e, 0x6c, - 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x30, 0x01, 0x12, 0x42, - 0x0a, 0x0d, 0x54, 0x72, 0x61, 0x63, 0x6b, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x12, - 0x1f, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x54, 0x72, 0x61, 0x63, - 0x6b, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x0e, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, - 0x30, 0x01, 0x12, 0x4b, 0x0a, 0x10, 0x45, 0x73, 0x74, 0x69, 0x6d, 0x61, 0x74, 0x65, 0x52, 0x6f, - 0x75, 0x74, 0x65, 0x46, 0x65, 0x65, 0x12, 0x1a, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, - 0x70, 0x63, 0x2e, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x46, 0x65, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x52, - 0x6f, 0x75, 0x74, 0x65, 0x46, 0x65, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, - 0x51, 0x0a, 0x0b, 0x53, 0x65, 0x6e, 0x64, 0x54, 0x6f, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x12, 0x1d, - 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x54, - 0x6f, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, - 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x54, 0x6f, - 0x52, 0x6f, 0x75, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x03, 0x88, - 0x02, 0x01, 0x12, 0x42, 0x0a, 0x0d, 0x53, 0x65, 0x6e, 0x64, 0x54, 0x6f, 0x52, 0x6f, 0x75, 0x74, - 0x65, 0x56, 0x32, 0x12, 0x1d, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, + 0x1a, 0x40, 0x0a, 0x12, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, + 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x04, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, + 0x38, 0x01, 0x1a, 0x46, 0x0a, 0x18, 0x49, 0x6e, 0x57, 0x69, 0x72, 0x65, 0x43, 0x75, 0x73, 0x74, + 0x6f, 0x6d, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, + 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x03, 0x6b, 0x65, 0x79, + 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, + 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xb9, 0x04, 0x0a, 0x1c, 0x46, + 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x48, 0x74, 0x6c, 0x63, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x63, + 0x65, 0x70, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x47, 0x0a, 0x14, 0x69, + 0x6e, 0x63, 0x6f, 0x6d, 0x69, 0x6e, 0x67, 0x5f, 0x63, 0x69, 0x72, 0x63, 0x75, 0x69, 0x74, 0x5f, + 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x72, 0x6f, 0x75, 0x74, + 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x69, 0x72, 0x63, 0x75, 0x69, 0x74, 0x4b, 0x65, 0x79, + 0x52, 0x12, 0x69, 0x6e, 0x63, 0x6f, 0x6d, 0x69, 0x6e, 0x67, 0x43, 0x69, 0x72, 0x63, 0x75, 0x69, + 0x74, 0x4b, 0x65, 0x79, 0x12, 0x3b, 0x0a, 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x0e, 0x32, 0x23, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, + 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x48, 0x6f, 0x6c, 0x64, 0x46, 0x6f, 0x72, 0x77, + 0x61, 0x72, 0x64, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, + 0x6e, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x72, 0x65, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x0c, 0x52, 0x08, 0x70, 0x72, 0x65, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x12, 0x27, 0x0a, + 0x0f, 0x66, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0e, 0x66, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x4d, + 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x3d, 0x0a, 0x0c, 0x66, 0x61, 0x69, 0x6c, 0x75, 0x72, + 0x65, 0x5f, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1a, 0x2e, 0x6c, + 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x2e, 0x46, 0x61, 0x69, + 0x6c, 0x75, 0x72, 0x65, 0x43, 0x6f, 0x64, 0x65, 0x52, 0x0b, 0x66, 0x61, 0x69, 0x6c, 0x75, 0x72, + 0x65, 0x43, 0x6f, 0x64, 0x65, 0x12, 0x24, 0x0a, 0x0e, 0x69, 0x6e, 0x5f, 0x61, 0x6d, 0x6f, 0x75, + 0x6e, 0x74, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0c, 0x69, + 0x6e, 0x41, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x4d, 0x73, 0x61, 0x74, 0x12, 0x26, 0x0a, 0x0f, 0x6f, + 0x75, 0x74, 0x5f, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x07, + 0x20, 0x01, 0x28, 0x04, 0x52, 0x0d, 0x6f, 0x75, 0x74, 0x41, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x4d, + 0x73, 0x61, 0x74, 0x12, 0x78, 0x0a, 0x17, 0x6f, 0x75, 0x74, 0x5f, 0x77, 0x69, 0x72, 0x65, 0x5f, + 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5f, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x18, 0x08, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x41, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, + 0x2e, 0x46, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x48, 0x74, 0x6c, 0x63, 0x49, 0x6e, 0x74, 0x65, + 0x72, 0x63, 0x65, 0x70, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x4f, 0x75, + 0x74, 0x57, 0x69, 0x72, 0x65, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x52, 0x65, 0x63, 0x6f, 0x72, + 0x64, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x14, 0x6f, 0x75, 0x74, 0x57, 0x69, 0x72, 0x65, + 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x1a, 0x47, 0x0a, + 0x19, 0x4f, 0x75, 0x74, 0x57, 0x69, 0x72, 0x65, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x52, 0x65, + 0x63, 0x6f, 0x72, 0x64, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, + 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, + 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x76, 0x61, 0x6c, + 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x82, 0x01, 0x0a, 0x17, 0x55, 0x70, 0x64, 0x61, 0x74, + 0x65, 0x43, 0x68, 0x61, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x12, 0x32, 0x0a, 0x0a, 0x63, 0x68, 0x61, 0x6e, 0x5f, 0x70, 0x6f, 0x69, 0x6e, 0x74, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, + 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x09, 0x63, 0x68, 0x61, + 0x6e, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x33, 0x0a, 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1b, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, + 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x41, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x52, 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x1a, 0x0a, 0x18, 0x55, + 0x70, 0x64, 0x61, 0x74, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x43, 0x0a, 0x11, 0x41, 0x64, 0x64, 0x41, 0x6c, + 0x69, 0x61, 0x73, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2e, 0x0a, 0x0a, + 0x61, 0x6c, 0x69, 0x61, 0x73, 0x5f, 0x6d, 0x61, 0x70, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x0f, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x4d, 0x61, + 0x70, 0x52, 0x09, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x4d, 0x61, 0x70, 0x73, 0x22, 0x44, 0x0a, 0x12, + 0x41, 0x64, 0x64, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x2e, 0x0a, 0x0a, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x5f, 0x6d, 0x61, 0x70, 0x73, + 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x41, + 0x6c, 0x69, 0x61, 0x73, 0x4d, 0x61, 0x70, 0x52, 0x09, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x4d, 0x61, + 0x70, 0x73, 0x22, 0x46, 0x0a, 0x14, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x41, 0x6c, 0x69, 0x61, + 0x73, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2e, 0x0a, 0x0a, 0x61, 0x6c, + 0x69, 0x61, 0x73, 0x5f, 0x6d, 0x61, 0x70, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0f, + 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x4d, 0x61, 0x70, 0x52, + 0x09, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x4d, 0x61, 0x70, 0x73, 0x22, 0x47, 0x0a, 0x15, 0x44, 0x65, + 0x6c, 0x65, 0x74, 0x65, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x2e, 0x0a, 0x0a, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x5f, 0x6d, 0x61, 0x70, + 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, + 0x41, 0x6c, 0x69, 0x61, 0x73, 0x4d, 0x61, 0x70, 0x52, 0x09, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x4d, + 0x61, 0x70, 0x73, 0x2a, 0x81, 0x04, 0x0a, 0x0d, 0x46, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x44, + 0x65, 0x74, 0x61, 0x69, 0x6c, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, + 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x4e, 0x4f, 0x5f, 0x44, 0x45, 0x54, 0x41, 0x49, 0x4c, 0x10, + 0x01, 0x12, 0x10, 0x0a, 0x0c, 0x4f, 0x4e, 0x49, 0x4f, 0x4e, 0x5f, 0x44, 0x45, 0x43, 0x4f, 0x44, + 0x45, 0x10, 0x02, 0x12, 0x15, 0x0a, 0x11, 0x4c, 0x49, 0x4e, 0x4b, 0x5f, 0x4e, 0x4f, 0x54, 0x5f, + 0x45, 0x4c, 0x49, 0x47, 0x49, 0x42, 0x4c, 0x45, 0x10, 0x03, 0x12, 0x14, 0x0a, 0x10, 0x4f, 0x4e, + 0x5f, 0x43, 0x48, 0x41, 0x49, 0x4e, 0x5f, 0x54, 0x49, 0x4d, 0x45, 0x4f, 0x55, 0x54, 0x10, 0x04, + 0x12, 0x14, 0x0a, 0x10, 0x48, 0x54, 0x4c, 0x43, 0x5f, 0x45, 0x58, 0x43, 0x45, 0x45, 0x44, 0x53, + 0x5f, 0x4d, 0x41, 0x58, 0x10, 0x05, 0x12, 0x18, 0x0a, 0x14, 0x49, 0x4e, 0x53, 0x55, 0x46, 0x46, + 0x49, 0x43, 0x49, 0x45, 0x4e, 0x54, 0x5f, 0x42, 0x41, 0x4c, 0x41, 0x4e, 0x43, 0x45, 0x10, 0x06, + 0x12, 0x16, 0x0a, 0x12, 0x49, 0x4e, 0x43, 0x4f, 0x4d, 0x50, 0x4c, 0x45, 0x54, 0x45, 0x5f, 0x46, + 0x4f, 0x52, 0x57, 0x41, 0x52, 0x44, 0x10, 0x07, 0x12, 0x13, 0x0a, 0x0f, 0x48, 0x54, 0x4c, 0x43, + 0x5f, 0x41, 0x44, 0x44, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x45, 0x44, 0x10, 0x08, 0x12, 0x15, 0x0a, + 0x11, 0x46, 0x4f, 0x52, 0x57, 0x41, 0x52, 0x44, 0x53, 0x5f, 0x44, 0x49, 0x53, 0x41, 0x42, 0x4c, + 0x45, 0x44, 0x10, 0x09, 0x12, 0x14, 0x0a, 0x10, 0x49, 0x4e, 0x56, 0x4f, 0x49, 0x43, 0x45, 0x5f, + 0x43, 0x41, 0x4e, 0x43, 0x45, 0x4c, 0x45, 0x44, 0x10, 0x0a, 0x12, 0x15, 0x0a, 0x11, 0x49, 0x4e, + 0x56, 0x4f, 0x49, 0x43, 0x45, 0x5f, 0x55, 0x4e, 0x44, 0x45, 0x52, 0x50, 0x41, 0x49, 0x44, 0x10, + 0x0b, 0x12, 0x1b, 0x0a, 0x17, 0x49, 0x4e, 0x56, 0x4f, 0x49, 0x43, 0x45, 0x5f, 0x45, 0x58, 0x50, + 0x49, 0x52, 0x59, 0x5f, 0x54, 0x4f, 0x4f, 0x5f, 0x53, 0x4f, 0x4f, 0x4e, 0x10, 0x0c, 0x12, 0x14, + 0x0a, 0x10, 0x49, 0x4e, 0x56, 0x4f, 0x49, 0x43, 0x45, 0x5f, 0x4e, 0x4f, 0x54, 0x5f, 0x4f, 0x50, + 0x45, 0x4e, 0x10, 0x0d, 0x12, 0x17, 0x0a, 0x13, 0x4d, 0x50, 0x50, 0x5f, 0x49, 0x4e, 0x56, 0x4f, + 0x49, 0x43, 0x45, 0x5f, 0x54, 0x49, 0x4d, 0x45, 0x4f, 0x55, 0x54, 0x10, 0x0e, 0x12, 0x14, 0x0a, + 0x10, 0x41, 0x44, 0x44, 0x52, 0x45, 0x53, 0x53, 0x5f, 0x4d, 0x49, 0x53, 0x4d, 0x41, 0x54, 0x43, + 0x48, 0x10, 0x0f, 0x12, 0x16, 0x0a, 0x12, 0x53, 0x45, 0x54, 0x5f, 0x54, 0x4f, 0x54, 0x41, 0x4c, + 0x5f, 0x4d, 0x49, 0x53, 0x4d, 0x41, 0x54, 0x43, 0x48, 0x10, 0x10, 0x12, 0x15, 0x0a, 0x11, 0x53, + 0x45, 0x54, 0x5f, 0x54, 0x4f, 0x54, 0x41, 0x4c, 0x5f, 0x54, 0x4f, 0x4f, 0x5f, 0x4c, 0x4f, 0x57, + 0x10, 0x11, 0x12, 0x10, 0x0a, 0x0c, 0x53, 0x45, 0x54, 0x5f, 0x4f, 0x56, 0x45, 0x52, 0x50, 0x41, + 0x49, 0x44, 0x10, 0x12, 0x12, 0x13, 0x0a, 0x0f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x5f, + 0x49, 0x4e, 0x56, 0x4f, 0x49, 0x43, 0x45, 0x10, 0x13, 0x12, 0x13, 0x0a, 0x0f, 0x49, 0x4e, 0x56, + 0x41, 0x4c, 0x49, 0x44, 0x5f, 0x4b, 0x45, 0x59, 0x53, 0x45, 0x4e, 0x44, 0x10, 0x14, 0x12, 0x13, + 0x0a, 0x0f, 0x4d, 0x50, 0x50, 0x5f, 0x49, 0x4e, 0x5f, 0x50, 0x52, 0x4f, 0x47, 0x52, 0x45, 0x53, + 0x53, 0x10, 0x15, 0x12, 0x12, 0x0a, 0x0e, 0x43, 0x49, 0x52, 0x43, 0x55, 0x4c, 0x41, 0x52, 0x5f, + 0x52, 0x4f, 0x55, 0x54, 0x45, 0x10, 0x16, 0x2a, 0xae, 0x01, 0x0a, 0x0c, 0x50, 0x61, 0x79, 0x6d, + 0x65, 0x6e, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x0d, 0x0a, 0x09, 0x49, 0x4e, 0x5f, 0x46, + 0x4c, 0x49, 0x47, 0x48, 0x54, 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x53, 0x55, 0x43, 0x43, 0x45, + 0x45, 0x44, 0x45, 0x44, 0x10, 0x01, 0x12, 0x12, 0x0a, 0x0e, 0x46, 0x41, 0x49, 0x4c, 0x45, 0x44, + 0x5f, 0x54, 0x49, 0x4d, 0x45, 0x4f, 0x55, 0x54, 0x10, 0x02, 0x12, 0x13, 0x0a, 0x0f, 0x46, 0x41, + 0x49, 0x4c, 0x45, 0x44, 0x5f, 0x4e, 0x4f, 0x5f, 0x52, 0x4f, 0x55, 0x54, 0x45, 0x10, 0x03, 0x12, + 0x10, 0x0a, 0x0c, 0x46, 0x41, 0x49, 0x4c, 0x45, 0x44, 0x5f, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, + 0x04, 0x12, 0x24, 0x0a, 0x20, 0x46, 0x41, 0x49, 0x4c, 0x45, 0x44, 0x5f, 0x49, 0x4e, 0x43, 0x4f, + 0x52, 0x52, 0x45, 0x43, 0x54, 0x5f, 0x50, 0x41, 0x59, 0x4d, 0x45, 0x4e, 0x54, 0x5f, 0x44, 0x45, + 0x54, 0x41, 0x49, 0x4c, 0x53, 0x10, 0x05, 0x12, 0x1f, 0x0a, 0x1b, 0x46, 0x41, 0x49, 0x4c, 0x45, + 0x44, 0x5f, 0x49, 0x4e, 0x53, 0x55, 0x46, 0x46, 0x49, 0x43, 0x49, 0x45, 0x4e, 0x54, 0x5f, 0x42, + 0x41, 0x4c, 0x41, 0x4e, 0x43, 0x45, 0x10, 0x06, 0x2a, 0x51, 0x0a, 0x18, 0x52, 0x65, 0x73, 0x6f, + 0x6c, 0x76, 0x65, 0x48, 0x6f, 0x6c, 0x64, 0x46, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x41, 0x63, + 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x0a, 0x0a, 0x06, 0x53, 0x45, 0x54, 0x54, 0x4c, 0x45, 0x10, 0x00, + 0x12, 0x08, 0x0a, 0x04, 0x46, 0x41, 0x49, 0x4c, 0x10, 0x01, 0x12, 0x0a, 0x0a, 0x06, 0x52, 0x45, + 0x53, 0x55, 0x4d, 0x45, 0x10, 0x02, 0x12, 0x13, 0x0a, 0x0f, 0x52, 0x45, 0x53, 0x55, 0x4d, 0x45, + 0x5f, 0x4d, 0x4f, 0x44, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x03, 0x2a, 0x35, 0x0a, 0x10, 0x43, + 0x68, 0x61, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, + 0x0a, 0x0a, 0x06, 0x45, 0x4e, 0x41, 0x42, 0x4c, 0x45, 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x44, + 0x49, 0x53, 0x41, 0x42, 0x4c, 0x45, 0x10, 0x01, 0x12, 0x08, 0x0a, 0x04, 0x41, 0x55, 0x54, 0x4f, + 0x10, 0x02, 0x32, 0xe8, 0x0d, 0x0a, 0x06, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x12, 0x40, 0x0a, + 0x0d, 0x53, 0x65, 0x6e, 0x64, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x56, 0x32, 0x12, 0x1d, + 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x50, + 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x0e, 0x2e, + 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x30, 0x01, 0x12, + 0x42, 0x0a, 0x0e, 0x54, 0x72, 0x61, 0x63, 0x6b, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x56, + 0x32, 0x12, 0x1e, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x54, 0x72, + 0x61, 0x63, 0x6b, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x0e, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, + 0x74, 0x30, 0x01, 0x12, 0x42, 0x0a, 0x0d, 0x54, 0x72, 0x61, 0x63, 0x6b, 0x50, 0x61, 0x79, 0x6d, + 0x65, 0x6e, 0x74, 0x73, 0x12, 0x1f, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, + 0x2e, 0x54, 0x72, 0x61, 0x63, 0x6b, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x0e, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x61, + 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x30, 0x01, 0x12, 0x4b, 0x0a, 0x10, 0x45, 0x73, 0x74, 0x69, 0x6d, + 0x61, 0x74, 0x65, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x46, 0x65, 0x65, 0x12, 0x1a, 0x2e, 0x72, 0x6f, + 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x46, 0x65, 0x65, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, + 0x72, 0x70, 0x63, 0x2e, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x46, 0x65, 0x65, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x51, 0x0a, 0x0b, 0x53, 0x65, 0x6e, 0x64, 0x54, 0x6f, 0x52, 0x6f, + 0x75, 0x74, 0x65, 0x12, 0x1d, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x54, 0x6f, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x12, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x48, 0x54, 0x4c, 0x43, 0x41, - 0x74, 0x74, 0x65, 0x6d, 0x70, 0x74, 0x12, 0x64, 0x0a, 0x13, 0x52, 0x65, 0x73, 0x65, 0x74, 0x4d, - 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x12, 0x25, 0x2e, - 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x65, 0x73, 0x65, 0x74, 0x4d, - 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, - 0x2e, 0x52, 0x65, 0x73, 0x65, 0x74, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x6e, - 0x74, 0x72, 0x6f, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x64, 0x0a, 0x13, - 0x51, 0x75, 0x65, 0x72, 0x79, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x6e, 0x74, - 0x72, 0x6f, 0x6c, 0x12, 0x25, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, - 0x51, 0x75, 0x65, 0x72, 0x79, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x6e, 0x74, - 0x72, 0x6f, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x72, 0x6f, 0x75, - 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x4d, 0x69, 0x73, 0x73, - 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x6a, 0x0a, 0x15, 0x58, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x4d, 0x69, 0x73, - 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x12, 0x27, 0x2e, 0x72, 0x6f, - 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x58, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x4d, - 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, - 0x2e, 0x58, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x43, - 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x70, - 0x0a, 0x17, 0x47, 0x65, 0x74, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x6e, 0x74, - 0x72, 0x6f, 0x6c, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x29, 0x2e, 0x72, 0x6f, 0x75, 0x74, + 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x53, + 0x65, 0x6e, 0x64, 0x54, 0x6f, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x22, 0x03, 0x88, 0x02, 0x01, 0x12, 0x42, 0x0a, 0x0d, 0x53, 0x65, 0x6e, 0x64, 0x54, + 0x6f, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x56, 0x32, 0x12, 0x1d, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, + 0x72, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x54, 0x6f, 0x52, 0x6f, 0x75, 0x74, 0x65, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x12, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, + 0x48, 0x54, 0x4c, 0x43, 0x41, 0x74, 0x74, 0x65, 0x6d, 0x70, 0x74, 0x12, 0x64, 0x0a, 0x13, 0x52, + 0x65, 0x73, 0x65, 0x74, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x6e, 0x74, 0x72, + 0x6f, 0x6c, 0x12, 0x25, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x52, + 0x65, 0x73, 0x65, 0x74, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x6e, 0x74, 0x72, + 0x6f, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x72, 0x6f, 0x75, 0x74, + 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x65, 0x73, 0x65, 0x74, 0x4d, 0x69, 0x73, 0x73, 0x69, + 0x6f, 0x6e, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x64, 0x0a, 0x13, 0x51, 0x75, 0x65, 0x72, 0x79, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6f, + 0x6e, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x12, 0x25, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, + 0x72, 0x72, 0x70, 0x63, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6f, + 0x6e, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x26, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x51, 0x75, 0x65, 0x72, + 0x79, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x6a, 0x0a, 0x15, 0x58, 0x49, 0x6d, 0x70, 0x6f, + 0x72, 0x74, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, + 0x12, 0x27, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x58, 0x49, 0x6d, + 0x70, 0x6f, 0x72, 0x74, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x6e, 0x74, 0x72, + 0x6f, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x72, 0x6f, 0x75, 0x74, + 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x58, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x4d, 0x69, 0x73, + 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x70, 0x0a, 0x17, 0x47, 0x65, 0x74, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6f, + 0x6e, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x29, + 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4d, 0x69, + 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x43, 0x6f, 0x6e, 0x66, + 0x69, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, - 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, - 0x2e, 0x47, 0x65, 0x74, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x6e, 0x74, 0x72, - 0x6f, 0x6c, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x12, 0x70, 0x0a, 0x17, 0x53, 0x65, 0x74, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x6f, - 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x29, 0x2e, 0x72, 0x6f, + 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x70, 0x0a, 0x17, 0x53, 0x65, 0x74, 0x4d, 0x69, 0x73, 0x73, + 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, + 0x12, 0x29, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x74, + 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x43, 0x6f, + 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x74, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, - 0x70, 0x63, 0x2e, 0x53, 0x65, 0x74, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x6e, - 0x74, 0x72, 0x6f, 0x6c, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x5b, 0x0a, 0x10, 0x51, 0x75, 0x65, 0x72, 0x79, 0x50, 0x72, 0x6f, 0x62, 0x61, - 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x12, 0x22, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, - 0x70, 0x63, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x50, 0x72, 0x6f, 0x62, 0x61, 0x62, 0x69, 0x6c, - 0x69, 0x74, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x72, 0x6f, 0x75, - 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x50, 0x72, 0x6f, 0x62, - 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, - 0x49, 0x0a, 0x0a, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x12, 0x1c, 0x2e, - 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x52, - 0x6f, 0x75, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x72, 0x6f, - 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x52, 0x6f, 0x75, - 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x54, 0x0a, 0x13, 0x53, 0x75, - 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x48, 0x74, 0x6c, 0x63, 0x45, 0x76, 0x65, 0x6e, 0x74, - 0x73, 0x12, 0x25, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x75, - 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x48, 0x74, 0x6c, 0x63, 0x45, 0x76, 0x65, 0x6e, 0x74, - 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x14, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, - 0x72, 0x72, 0x70, 0x63, 0x2e, 0x48, 0x74, 0x6c, 0x63, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x30, 0x01, - 0x12, 0x4d, 0x0a, 0x0b, 0x53, 0x65, 0x6e, 0x64, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x12, - 0x1d, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x6e, 0x64, - 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, - 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x61, 0x79, 0x6d, 0x65, - 0x6e, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x03, 0x88, 0x02, 0x01, 0x30, 0x01, 0x12, - 0x4f, 0x0a, 0x0c, 0x54, 0x72, 0x61, 0x63, 0x6b, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x12, - 0x1e, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x54, 0x72, 0x61, 0x63, - 0x6b, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x18, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x61, 0x79, 0x6d, - 0x65, 0x6e, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x03, 0x88, 0x02, 0x01, 0x30, 0x01, - 0x12, 0x66, 0x0a, 0x0f, 0x48, 0x74, 0x6c, 0x63, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, - 0x74, 0x6f, 0x72, 0x12, 0x27, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, - 0x46, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x48, 0x74, 0x6c, 0x63, 0x49, 0x6e, 0x74, 0x65, 0x72, - 0x63, 0x65, 0x70, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x1a, 0x26, 0x2e, 0x72, - 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, - 0x48, 0x74, 0x6c, 0x63, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x28, 0x01, 0x30, 0x01, 0x12, 0x5b, 0x0a, 0x10, 0x55, 0x70, 0x64, 0x61, - 0x74, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x22, 0x2e, 0x72, - 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x43, - 0x68, 0x61, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x23, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x55, 0x70, 0x64, - 0x61, 0x74, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x53, 0x0a, 0x14, 0x58, 0x41, 0x64, 0x64, 0x4c, 0x6f, 0x63, - 0x61, 0x6c, 0x43, 0x68, 0x61, 0x6e, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x65, 0x73, 0x12, 0x1c, 0x2e, - 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x64, 0x64, 0x41, 0x6c, 0x69, - 0x61, 0x73, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x72, 0x6f, - 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x64, 0x64, 0x41, 0x6c, 0x69, 0x61, 0x73, - 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5c, 0x0a, 0x17, 0x58, 0x44, - 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4c, 0x6f, 0x63, 0x61, 0x6c, 0x43, 0x68, 0x61, 0x6e, 0x41, 0x6c, - 0x69, 0x61, 0x73, 0x65, 0x73, 0x12, 0x1f, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, - 0x63, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x65, 0x73, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, - 0x70, 0x63, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x65, 0x73, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x31, 0x5a, 0x2f, 0x67, 0x69, 0x74, 0x68, - 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x6e, 0x69, 0x6e, 0x67, - 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x2f, 0x6c, 0x6e, 0x64, 0x2f, 0x6c, 0x6e, 0x72, 0x70, - 0x63, 0x2f, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x62, 0x06, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x33, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5b, 0x0a, 0x10, 0x51, 0x75, 0x65, 0x72, 0x79, + 0x50, 0x72, 0x6f, 0x62, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x12, 0x22, 0x2e, 0x72, 0x6f, + 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x50, 0x72, 0x6f, + 0x62, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x23, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x51, 0x75, 0x65, 0x72, + 0x79, 0x50, 0x72, 0x6f, 0x62, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x49, 0x0a, 0x0a, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x52, 0x6f, 0x75, + 0x74, 0x65, 0x12, 0x1c, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x42, + 0x75, 0x69, 0x6c, 0x64, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x1d, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x42, 0x75, 0x69, + 0x6c, 0x64, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x54, 0x0a, 0x13, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x48, 0x74, 0x6c, 0x63, + 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x25, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, + 0x70, 0x63, 0x2e, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x48, 0x74, 0x6c, 0x63, + 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x14, 0x2e, + 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x48, 0x74, 0x6c, 0x63, 0x45, 0x76, + 0x65, 0x6e, 0x74, 0x30, 0x01, 0x12, 0x4d, 0x0a, 0x0b, 0x53, 0x65, 0x6e, 0x64, 0x50, 0x61, 0x79, + 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x1d, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, + 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, + 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x03, 0x88, + 0x02, 0x01, 0x30, 0x01, 0x12, 0x4f, 0x0a, 0x0c, 0x54, 0x72, 0x61, 0x63, 0x6b, 0x50, 0x61, 0x79, + 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x1e, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, + 0x2e, 0x54, 0x72, 0x61, 0x63, 0x6b, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, + 0x2e, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x03, + 0x88, 0x02, 0x01, 0x30, 0x01, 0x12, 0x66, 0x0a, 0x0f, 0x48, 0x74, 0x6c, 0x63, 0x49, 0x6e, 0x74, + 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x12, 0x27, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, + 0x72, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x48, 0x74, 0x6c, 0x63, + 0x49, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x1a, 0x26, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x6f, + 0x72, 0x77, 0x61, 0x72, 0x64, 0x48, 0x74, 0x6c, 0x63, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, + 0x70, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x28, 0x01, 0x30, 0x01, 0x12, 0x5b, 0x0a, + 0x10, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x75, + 0x73, 0x12, 0x22, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x55, 0x70, + 0x64, 0x61, 0x74, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, + 0x63, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x53, 0x74, 0x61, 0x74, + 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x53, 0x0a, 0x14, 0x58, 0x41, + 0x64, 0x64, 0x4c, 0x6f, 0x63, 0x61, 0x6c, 0x43, 0x68, 0x61, 0x6e, 0x41, 0x6c, 0x69, 0x61, 0x73, + 0x65, 0x73, 0x12, 0x1c, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x41, + 0x64, 0x64, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x1d, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x64, 0x64, + 0x41, 0x6c, 0x69, 0x61, 0x73, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x5c, 0x0a, 0x17, 0x58, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4c, 0x6f, 0x63, 0x61, 0x6c, 0x43, + 0x68, 0x61, 0x6e, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x65, 0x73, 0x12, 0x1f, 0x2e, 0x72, 0x6f, 0x75, + 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x41, 0x6c, 0x69, + 0x61, 0x73, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x72, 0x6f, + 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x41, 0x6c, + 0x69, 0x61, 0x73, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x31, 0x5a, + 0x2f, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6c, 0x69, 0x67, 0x68, + 0x74, 0x6e, 0x69, 0x6e, 0x67, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x2f, 0x6c, 0x6e, 0x64, + 0x2f, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2f, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x72, 0x70, 0x63, + 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -4219,7 +4314,7 @@ func file_routerrpc_router_proto_rawDescGZIP() []byte { } var file_routerrpc_router_proto_enumTypes = make([]protoimpl.EnumInfo, 6) -var file_routerrpc_router_proto_msgTypes = make([]protoimpl.MessageInfo, 48) +var file_routerrpc_router_proto_msgTypes = make([]protoimpl.MessageInfo, 52) var file_routerrpc_router_proto_goTypes = []interface{}{ (FailureDetail)(0), // 0: routerrpc.FailureDetail (PaymentState)(0), // 1: routerrpc.PaymentState @@ -4273,106 +4368,114 @@ var file_routerrpc_router_proto_goTypes = []interface{}{ (*DeleteAliasesRequest)(nil), // 49: routerrpc.DeleteAliasesRequest (*DeleteAliasesResponse)(nil), // 50: routerrpc.DeleteAliasesResponse nil, // 51: routerrpc.SendPaymentRequest.DestCustomRecordsEntry - nil, // 52: routerrpc.ForwardHtlcInterceptRequest.CustomRecordsEntry - nil, // 53: routerrpc.ForwardHtlcInterceptResponse.OutWireCustomRecordsEntry - (*lnrpc.RouteHint)(nil), // 54: lnrpc.RouteHint - (lnrpc.FeatureBit)(0), // 55: lnrpc.FeatureBit - (lnrpc.PaymentFailureReason)(0), // 56: lnrpc.PaymentFailureReason - (*lnrpc.Route)(nil), // 57: lnrpc.Route - (*lnrpc.Failure)(nil), // 58: lnrpc.Failure - (lnrpc.Failure_FailureCode)(0), // 59: lnrpc.Failure.FailureCode - (*lnrpc.HTLCAttempt)(nil), // 60: lnrpc.HTLCAttempt - (*lnrpc.ChannelPoint)(nil), // 61: lnrpc.ChannelPoint - (*lnrpc.AliasMap)(nil), // 62: lnrpc.AliasMap - (*lnrpc.Payment)(nil), // 63: lnrpc.Payment + nil, // 52: routerrpc.SendPaymentRequest.FirstHopCustomRecordsEntry + nil, // 53: routerrpc.SendToRouteRequest.FirstHopCustomRecordsEntry + nil, // 54: routerrpc.BuildRouteRequest.FirstHopCustomRecordsEntry + nil, // 55: routerrpc.ForwardHtlcInterceptRequest.CustomRecordsEntry + nil, // 56: routerrpc.ForwardHtlcInterceptRequest.InWireCustomRecordsEntry + nil, // 57: routerrpc.ForwardHtlcInterceptResponse.OutWireCustomRecordsEntry + (*lnrpc.RouteHint)(nil), // 58: lnrpc.RouteHint + (lnrpc.FeatureBit)(0), // 59: lnrpc.FeatureBit + (lnrpc.PaymentFailureReason)(0), // 60: lnrpc.PaymentFailureReason + (*lnrpc.Route)(nil), // 61: lnrpc.Route + (*lnrpc.Failure)(nil), // 62: lnrpc.Failure + (lnrpc.Failure_FailureCode)(0), // 63: lnrpc.Failure.FailureCode + (*lnrpc.HTLCAttempt)(nil), // 64: lnrpc.HTLCAttempt + (*lnrpc.ChannelPoint)(nil), // 65: lnrpc.ChannelPoint + (*lnrpc.AliasMap)(nil), // 66: lnrpc.AliasMap + (*lnrpc.Payment)(nil), // 67: lnrpc.Payment } var file_routerrpc_router_proto_depIdxs = []int32{ - 54, // 0: routerrpc.SendPaymentRequest.route_hints:type_name -> lnrpc.RouteHint + 58, // 0: routerrpc.SendPaymentRequest.route_hints:type_name -> lnrpc.RouteHint 51, // 1: routerrpc.SendPaymentRequest.dest_custom_records:type_name -> routerrpc.SendPaymentRequest.DestCustomRecordsEntry - 55, // 2: routerrpc.SendPaymentRequest.dest_features:type_name -> lnrpc.FeatureBit - 56, // 3: routerrpc.RouteFeeResponse.failure_reason:type_name -> lnrpc.PaymentFailureReason - 57, // 4: routerrpc.SendToRouteRequest.route:type_name -> lnrpc.Route - 58, // 5: routerrpc.SendToRouteResponse.failure:type_name -> lnrpc.Failure - 19, // 6: routerrpc.QueryMissionControlResponse.pairs:type_name -> routerrpc.PairHistory - 19, // 7: routerrpc.XImportMissionControlRequest.pairs:type_name -> routerrpc.PairHistory - 20, // 8: routerrpc.PairHistory.history:type_name -> routerrpc.PairData - 25, // 9: routerrpc.GetMissionControlConfigResponse.config:type_name -> routerrpc.MissionControlConfig - 25, // 10: routerrpc.SetMissionControlConfigRequest.config:type_name -> routerrpc.MissionControlConfig - 4, // 11: routerrpc.MissionControlConfig.model:type_name -> routerrpc.MissionControlConfig.ProbabilityModel - 27, // 12: routerrpc.MissionControlConfig.apriori:type_name -> routerrpc.AprioriParameters - 26, // 13: routerrpc.MissionControlConfig.bimodal:type_name -> routerrpc.BimodalParameters - 20, // 14: routerrpc.QueryProbabilityResponse.history:type_name -> routerrpc.PairData - 57, // 15: routerrpc.BuildRouteResponse.route:type_name -> lnrpc.Route - 5, // 16: routerrpc.HtlcEvent.event_type:type_name -> routerrpc.HtlcEvent.EventType - 35, // 17: routerrpc.HtlcEvent.forward_event:type_name -> routerrpc.ForwardEvent - 36, // 18: routerrpc.HtlcEvent.forward_fail_event:type_name -> routerrpc.ForwardFailEvent - 37, // 19: routerrpc.HtlcEvent.settle_event:type_name -> routerrpc.SettleEvent - 40, // 20: routerrpc.HtlcEvent.link_fail_event:type_name -> routerrpc.LinkFailEvent - 39, // 21: routerrpc.HtlcEvent.subscribed_event:type_name -> routerrpc.SubscribedEvent - 38, // 22: routerrpc.HtlcEvent.final_htlc_event:type_name -> routerrpc.FinalHtlcEvent - 34, // 23: routerrpc.ForwardEvent.info:type_name -> routerrpc.HtlcInfo - 34, // 24: routerrpc.LinkFailEvent.info:type_name -> routerrpc.HtlcInfo - 59, // 25: routerrpc.LinkFailEvent.wire_failure:type_name -> lnrpc.Failure.FailureCode - 0, // 26: routerrpc.LinkFailEvent.failure_detail:type_name -> routerrpc.FailureDetail - 1, // 27: routerrpc.PaymentStatus.state:type_name -> routerrpc.PaymentState - 60, // 28: routerrpc.PaymentStatus.htlcs:type_name -> lnrpc.HTLCAttempt - 42, // 29: routerrpc.ForwardHtlcInterceptRequest.incoming_circuit_key:type_name -> routerrpc.CircuitKey - 52, // 30: routerrpc.ForwardHtlcInterceptRequest.custom_records:type_name -> routerrpc.ForwardHtlcInterceptRequest.CustomRecordsEntry - 42, // 31: routerrpc.ForwardHtlcInterceptResponse.incoming_circuit_key:type_name -> routerrpc.CircuitKey - 2, // 32: routerrpc.ForwardHtlcInterceptResponse.action:type_name -> routerrpc.ResolveHoldForwardAction - 59, // 33: routerrpc.ForwardHtlcInterceptResponse.failure_code:type_name -> lnrpc.Failure.FailureCode - 53, // 34: routerrpc.ForwardHtlcInterceptResponse.out_wire_custom_records:type_name -> routerrpc.ForwardHtlcInterceptResponse.OutWireCustomRecordsEntry - 61, // 35: routerrpc.UpdateChanStatusRequest.chan_point:type_name -> lnrpc.ChannelPoint - 3, // 36: routerrpc.UpdateChanStatusRequest.action:type_name -> routerrpc.ChanStatusAction - 62, // 37: routerrpc.AddAliasesRequest.alias_maps:type_name -> lnrpc.AliasMap - 62, // 38: routerrpc.AddAliasesResponse.alias_maps:type_name -> lnrpc.AliasMap - 62, // 39: routerrpc.DeleteAliasesRequest.alias_maps:type_name -> lnrpc.AliasMap - 62, // 40: routerrpc.DeleteAliasesResponse.alias_maps:type_name -> lnrpc.AliasMap - 6, // 41: routerrpc.Router.SendPaymentV2:input_type -> routerrpc.SendPaymentRequest - 7, // 42: routerrpc.Router.TrackPaymentV2:input_type -> routerrpc.TrackPaymentRequest - 8, // 43: routerrpc.Router.TrackPayments:input_type -> routerrpc.TrackPaymentsRequest - 9, // 44: routerrpc.Router.EstimateRouteFee:input_type -> routerrpc.RouteFeeRequest - 11, // 45: routerrpc.Router.SendToRoute:input_type -> routerrpc.SendToRouteRequest - 11, // 46: routerrpc.Router.SendToRouteV2:input_type -> routerrpc.SendToRouteRequest - 13, // 47: routerrpc.Router.ResetMissionControl:input_type -> routerrpc.ResetMissionControlRequest - 15, // 48: routerrpc.Router.QueryMissionControl:input_type -> routerrpc.QueryMissionControlRequest - 17, // 49: routerrpc.Router.XImportMissionControl:input_type -> routerrpc.XImportMissionControlRequest - 21, // 50: routerrpc.Router.GetMissionControlConfig:input_type -> routerrpc.GetMissionControlConfigRequest - 23, // 51: routerrpc.Router.SetMissionControlConfig:input_type -> routerrpc.SetMissionControlConfigRequest - 28, // 52: routerrpc.Router.QueryProbability:input_type -> routerrpc.QueryProbabilityRequest - 30, // 53: routerrpc.Router.BuildRoute:input_type -> routerrpc.BuildRouteRequest - 32, // 54: routerrpc.Router.SubscribeHtlcEvents:input_type -> routerrpc.SubscribeHtlcEventsRequest - 6, // 55: routerrpc.Router.SendPayment:input_type -> routerrpc.SendPaymentRequest - 7, // 56: routerrpc.Router.TrackPayment:input_type -> routerrpc.TrackPaymentRequest - 44, // 57: routerrpc.Router.HtlcInterceptor:input_type -> routerrpc.ForwardHtlcInterceptResponse - 45, // 58: routerrpc.Router.UpdateChanStatus:input_type -> routerrpc.UpdateChanStatusRequest - 47, // 59: routerrpc.Router.XAddLocalChanAliases:input_type -> routerrpc.AddAliasesRequest - 49, // 60: routerrpc.Router.XDeleteLocalChanAliases:input_type -> routerrpc.DeleteAliasesRequest - 63, // 61: routerrpc.Router.SendPaymentV2:output_type -> lnrpc.Payment - 63, // 62: routerrpc.Router.TrackPaymentV2:output_type -> lnrpc.Payment - 63, // 63: routerrpc.Router.TrackPayments:output_type -> lnrpc.Payment - 10, // 64: routerrpc.Router.EstimateRouteFee:output_type -> routerrpc.RouteFeeResponse - 12, // 65: routerrpc.Router.SendToRoute:output_type -> routerrpc.SendToRouteResponse - 60, // 66: routerrpc.Router.SendToRouteV2:output_type -> lnrpc.HTLCAttempt - 14, // 67: routerrpc.Router.ResetMissionControl:output_type -> routerrpc.ResetMissionControlResponse - 16, // 68: routerrpc.Router.QueryMissionControl:output_type -> routerrpc.QueryMissionControlResponse - 18, // 69: routerrpc.Router.XImportMissionControl:output_type -> routerrpc.XImportMissionControlResponse - 22, // 70: routerrpc.Router.GetMissionControlConfig:output_type -> routerrpc.GetMissionControlConfigResponse - 24, // 71: routerrpc.Router.SetMissionControlConfig:output_type -> routerrpc.SetMissionControlConfigResponse - 29, // 72: routerrpc.Router.QueryProbability:output_type -> routerrpc.QueryProbabilityResponse - 31, // 73: routerrpc.Router.BuildRoute:output_type -> routerrpc.BuildRouteResponse - 33, // 74: routerrpc.Router.SubscribeHtlcEvents:output_type -> routerrpc.HtlcEvent - 41, // 75: routerrpc.Router.SendPayment:output_type -> routerrpc.PaymentStatus - 41, // 76: routerrpc.Router.TrackPayment:output_type -> routerrpc.PaymentStatus - 43, // 77: routerrpc.Router.HtlcInterceptor:output_type -> routerrpc.ForwardHtlcInterceptRequest - 46, // 78: routerrpc.Router.UpdateChanStatus:output_type -> routerrpc.UpdateChanStatusResponse - 48, // 79: routerrpc.Router.XAddLocalChanAliases:output_type -> routerrpc.AddAliasesResponse - 50, // 80: routerrpc.Router.XDeleteLocalChanAliases:output_type -> routerrpc.DeleteAliasesResponse - 61, // [61:81] is the sub-list for method output_type - 41, // [41:61] is the sub-list for method input_type - 41, // [41:41] is the sub-list for extension type_name - 41, // [41:41] is the sub-list for extension extendee - 0, // [0:41] is the sub-list for field type_name + 59, // 2: routerrpc.SendPaymentRequest.dest_features:type_name -> lnrpc.FeatureBit + 52, // 3: routerrpc.SendPaymentRequest.first_hop_custom_records:type_name -> routerrpc.SendPaymentRequest.FirstHopCustomRecordsEntry + 60, // 4: routerrpc.RouteFeeResponse.failure_reason:type_name -> lnrpc.PaymentFailureReason + 61, // 5: routerrpc.SendToRouteRequest.route:type_name -> lnrpc.Route + 53, // 6: routerrpc.SendToRouteRequest.first_hop_custom_records:type_name -> routerrpc.SendToRouteRequest.FirstHopCustomRecordsEntry + 62, // 7: routerrpc.SendToRouteResponse.failure:type_name -> lnrpc.Failure + 19, // 8: routerrpc.QueryMissionControlResponse.pairs:type_name -> routerrpc.PairHistory + 19, // 9: routerrpc.XImportMissionControlRequest.pairs:type_name -> routerrpc.PairHistory + 20, // 10: routerrpc.PairHistory.history:type_name -> routerrpc.PairData + 25, // 11: routerrpc.GetMissionControlConfigResponse.config:type_name -> routerrpc.MissionControlConfig + 25, // 12: routerrpc.SetMissionControlConfigRequest.config:type_name -> routerrpc.MissionControlConfig + 4, // 13: routerrpc.MissionControlConfig.model:type_name -> routerrpc.MissionControlConfig.ProbabilityModel + 27, // 14: routerrpc.MissionControlConfig.apriori:type_name -> routerrpc.AprioriParameters + 26, // 15: routerrpc.MissionControlConfig.bimodal:type_name -> routerrpc.BimodalParameters + 20, // 16: routerrpc.QueryProbabilityResponse.history:type_name -> routerrpc.PairData + 54, // 17: routerrpc.BuildRouteRequest.first_hop_custom_records:type_name -> routerrpc.BuildRouteRequest.FirstHopCustomRecordsEntry + 61, // 18: routerrpc.BuildRouteResponse.route:type_name -> lnrpc.Route + 5, // 19: routerrpc.HtlcEvent.event_type:type_name -> routerrpc.HtlcEvent.EventType + 35, // 20: routerrpc.HtlcEvent.forward_event:type_name -> routerrpc.ForwardEvent + 36, // 21: routerrpc.HtlcEvent.forward_fail_event:type_name -> routerrpc.ForwardFailEvent + 37, // 22: routerrpc.HtlcEvent.settle_event:type_name -> routerrpc.SettleEvent + 40, // 23: routerrpc.HtlcEvent.link_fail_event:type_name -> routerrpc.LinkFailEvent + 39, // 24: routerrpc.HtlcEvent.subscribed_event:type_name -> routerrpc.SubscribedEvent + 38, // 25: routerrpc.HtlcEvent.final_htlc_event:type_name -> routerrpc.FinalHtlcEvent + 34, // 26: routerrpc.ForwardEvent.info:type_name -> routerrpc.HtlcInfo + 34, // 27: routerrpc.LinkFailEvent.info:type_name -> routerrpc.HtlcInfo + 63, // 28: routerrpc.LinkFailEvent.wire_failure:type_name -> lnrpc.Failure.FailureCode + 0, // 29: routerrpc.LinkFailEvent.failure_detail:type_name -> routerrpc.FailureDetail + 1, // 30: routerrpc.PaymentStatus.state:type_name -> routerrpc.PaymentState + 64, // 31: routerrpc.PaymentStatus.htlcs:type_name -> lnrpc.HTLCAttempt + 42, // 32: routerrpc.ForwardHtlcInterceptRequest.incoming_circuit_key:type_name -> routerrpc.CircuitKey + 55, // 33: routerrpc.ForwardHtlcInterceptRequest.custom_records:type_name -> routerrpc.ForwardHtlcInterceptRequest.CustomRecordsEntry + 56, // 34: routerrpc.ForwardHtlcInterceptRequest.in_wire_custom_records:type_name -> routerrpc.ForwardHtlcInterceptRequest.InWireCustomRecordsEntry + 42, // 35: routerrpc.ForwardHtlcInterceptResponse.incoming_circuit_key:type_name -> routerrpc.CircuitKey + 2, // 36: routerrpc.ForwardHtlcInterceptResponse.action:type_name -> routerrpc.ResolveHoldForwardAction + 63, // 37: routerrpc.ForwardHtlcInterceptResponse.failure_code:type_name -> lnrpc.Failure.FailureCode + 57, // 38: routerrpc.ForwardHtlcInterceptResponse.out_wire_custom_records:type_name -> routerrpc.ForwardHtlcInterceptResponse.OutWireCustomRecordsEntry + 65, // 39: routerrpc.UpdateChanStatusRequest.chan_point:type_name -> lnrpc.ChannelPoint + 3, // 40: routerrpc.UpdateChanStatusRequest.action:type_name -> routerrpc.ChanStatusAction + 66, // 41: routerrpc.AddAliasesRequest.alias_maps:type_name -> lnrpc.AliasMap + 66, // 42: routerrpc.AddAliasesResponse.alias_maps:type_name -> lnrpc.AliasMap + 66, // 43: routerrpc.DeleteAliasesRequest.alias_maps:type_name -> lnrpc.AliasMap + 66, // 44: routerrpc.DeleteAliasesResponse.alias_maps:type_name -> lnrpc.AliasMap + 6, // 45: routerrpc.Router.SendPaymentV2:input_type -> routerrpc.SendPaymentRequest + 7, // 46: routerrpc.Router.TrackPaymentV2:input_type -> routerrpc.TrackPaymentRequest + 8, // 47: routerrpc.Router.TrackPayments:input_type -> routerrpc.TrackPaymentsRequest + 9, // 48: routerrpc.Router.EstimateRouteFee:input_type -> routerrpc.RouteFeeRequest + 11, // 49: routerrpc.Router.SendToRoute:input_type -> routerrpc.SendToRouteRequest + 11, // 50: routerrpc.Router.SendToRouteV2:input_type -> routerrpc.SendToRouteRequest + 13, // 51: routerrpc.Router.ResetMissionControl:input_type -> routerrpc.ResetMissionControlRequest + 15, // 52: routerrpc.Router.QueryMissionControl:input_type -> routerrpc.QueryMissionControlRequest + 17, // 53: routerrpc.Router.XImportMissionControl:input_type -> routerrpc.XImportMissionControlRequest + 21, // 54: routerrpc.Router.GetMissionControlConfig:input_type -> routerrpc.GetMissionControlConfigRequest + 23, // 55: routerrpc.Router.SetMissionControlConfig:input_type -> routerrpc.SetMissionControlConfigRequest + 28, // 56: routerrpc.Router.QueryProbability:input_type -> routerrpc.QueryProbabilityRequest + 30, // 57: routerrpc.Router.BuildRoute:input_type -> routerrpc.BuildRouteRequest + 32, // 58: routerrpc.Router.SubscribeHtlcEvents:input_type -> routerrpc.SubscribeHtlcEventsRequest + 6, // 59: routerrpc.Router.SendPayment:input_type -> routerrpc.SendPaymentRequest + 7, // 60: routerrpc.Router.TrackPayment:input_type -> routerrpc.TrackPaymentRequest + 44, // 61: routerrpc.Router.HtlcInterceptor:input_type -> routerrpc.ForwardHtlcInterceptResponse + 45, // 62: routerrpc.Router.UpdateChanStatus:input_type -> routerrpc.UpdateChanStatusRequest + 47, // 63: routerrpc.Router.XAddLocalChanAliases:input_type -> routerrpc.AddAliasesRequest + 49, // 64: routerrpc.Router.XDeleteLocalChanAliases:input_type -> routerrpc.DeleteAliasesRequest + 67, // 65: routerrpc.Router.SendPaymentV2:output_type -> lnrpc.Payment + 67, // 66: routerrpc.Router.TrackPaymentV2:output_type -> lnrpc.Payment + 67, // 67: routerrpc.Router.TrackPayments:output_type -> lnrpc.Payment + 10, // 68: routerrpc.Router.EstimateRouteFee:output_type -> routerrpc.RouteFeeResponse + 12, // 69: routerrpc.Router.SendToRoute:output_type -> routerrpc.SendToRouteResponse + 64, // 70: routerrpc.Router.SendToRouteV2:output_type -> lnrpc.HTLCAttempt + 14, // 71: routerrpc.Router.ResetMissionControl:output_type -> routerrpc.ResetMissionControlResponse + 16, // 72: routerrpc.Router.QueryMissionControl:output_type -> routerrpc.QueryMissionControlResponse + 18, // 73: routerrpc.Router.XImportMissionControl:output_type -> routerrpc.XImportMissionControlResponse + 22, // 74: routerrpc.Router.GetMissionControlConfig:output_type -> routerrpc.GetMissionControlConfigResponse + 24, // 75: routerrpc.Router.SetMissionControlConfig:output_type -> routerrpc.SetMissionControlConfigResponse + 29, // 76: routerrpc.Router.QueryProbability:output_type -> routerrpc.QueryProbabilityResponse + 31, // 77: routerrpc.Router.BuildRoute:output_type -> routerrpc.BuildRouteResponse + 33, // 78: routerrpc.Router.SubscribeHtlcEvents:output_type -> routerrpc.HtlcEvent + 41, // 79: routerrpc.Router.SendPayment:output_type -> routerrpc.PaymentStatus + 41, // 80: routerrpc.Router.TrackPayment:output_type -> routerrpc.PaymentStatus + 43, // 81: routerrpc.Router.HtlcInterceptor:output_type -> routerrpc.ForwardHtlcInterceptRequest + 46, // 82: routerrpc.Router.UpdateChanStatus:output_type -> routerrpc.UpdateChanStatusResponse + 48, // 83: routerrpc.Router.XAddLocalChanAliases:output_type -> routerrpc.AddAliasesResponse + 50, // 84: routerrpc.Router.XDeleteLocalChanAliases:output_type -> routerrpc.DeleteAliasesResponse + 65, // [65:85] is the sub-list for method output_type + 45, // [45:65] is the sub-list for method input_type + 45, // [45:45] is the sub-list for extension type_name + 45, // [45:45] is the sub-list for extension extendee + 0, // [0:45] is the sub-list for field type_name } func init() { file_routerrpc_router_proto_init() } @@ -4940,7 +5043,7 @@ func file_routerrpc_router_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_routerrpc_router_proto_rawDesc, NumEnums: 6, - NumMessages: 48, + NumMessages: 52, NumExtensions: 0, NumServices: 1, }, diff --git a/lnrpc/routerrpc/router.proto b/lnrpc/routerrpc/router.proto index b9054efa06..92da751929 100644 --- a/lnrpc/routerrpc/router.proto +++ b/lnrpc/routerrpc/router.proto @@ -358,6 +358,15 @@ message SendPaymentRequest { being sent. */ bool cancelable = 24; + + /* + An optional field that can be used to pass an arbitrary set of TLV records + to the first hop peer of this payment. This can be used to pass application + specific data during the payment attempt. Record types are required to be in + the custom range >= 65536. When using REST, the values must be encoded as + base64. + */ + map first_hop_custom_records = 25; } message TrackPaymentRequest { @@ -451,6 +460,15 @@ message SendToRouteRequest { routes, incorrect payment details, or insufficient funds. */ bool skip_temp_err = 3; + + /* + An optional field that can be used to pass an arbitrary set of TLV records + to the first hop peer of this payment. This can be used to pass application + specific data during the payment attempt. Record types are required to be in + the custom range >= 65536. When using REST, the values must be encoded as + base64. + */ + map first_hop_custom_records = 4; } message SendToRouteResponse { @@ -726,6 +744,15 @@ message BuildRouteRequest { This is also called payment secret in specifications (e.g. BOLT 11). */ bytes payment_addr = 5; + + /* + An optional field that can be used to pass an arbitrary set of TLV records + to the first hop peer of this payment. This can be used to pass application + specific data during the payment attempt. Record types are required to be in + the custom range >= 65536. When using REST, the values must be encoded as + base64. + */ + map first_hop_custom_records = 6; } message BuildRouteResponse { @@ -982,6 +1009,9 @@ message ForwardHtlcInterceptRequest { // The block height at which this htlc will be auto-failed to prevent the // channel from force-closing. int32 auto_fail_height = 10; + + // The custom records of the peer's incoming p2p wire message. + map in_wire_custom_records = 11; } /** diff --git a/lnrpc/routerrpc/router.swagger.json b/lnrpc/routerrpc/router.swagger.json index c469fd736c..e50061604c 100644 --- a/lnrpc/routerrpc/router.swagger.json +++ b/lnrpc/routerrpc/router.swagger.json @@ -1283,6 +1283,14 @@ "type": "string", "format": "byte", "description": "An optional payment addr to be included within the last hop of the route.\nThis is also called payment secret in specifications (e.g. BOLT 11)." + }, + "first_hop_custom_records": { + "type": "object", + "additionalProperties": { + "type": "string", + "format": "byte" + }, + "description": "An optional field that can be used to pass an arbitrary set of TLV records\nto the first hop peer of this payment. This can be used to pass application\nspecific data during the payment attempt. Record types are required to be in\nthe custom range \u003e= 65536. When using REST, the values must be encoded as\nbase64." } } }, @@ -1447,6 +1455,14 @@ "type": "integer", "format": "int32", "description": "The block height at which this htlc will be auto-failed to prevent the\nchannel from force-closing." + }, + "in_wire_custom_records": { + "type": "object", + "additionalProperties": { + "type": "string", + "format": "byte" + }, + "description": "The custom records of the peer's incoming p2p wire message." } } }, @@ -1957,6 +1973,14 @@ "cancelable": { "type": "boolean", "description": "If set, the payment loop can be interrupted by manually canceling the\npayment context, even before the payment timeout is reached. Note that the\npayment may still succeed after cancellation, as in-flight attempts can\nstill settle afterwards. Canceling will only prevent further attempts from\nbeing sent." + }, + "first_hop_custom_records": { + "type": "object", + "additionalProperties": { + "type": "string", + "format": "byte" + }, + "description": "An optional field that can be used to pass an arbitrary set of TLV records\nto the first hop peer of this payment. This can be used to pass application\nspecific data during the payment attempt. Record types are required to be in\nthe custom range \u003e= 65536. When using REST, the values must be encoded as\nbase64." } } }, @@ -1975,6 +1999,14 @@ "skip_temp_err": { "type": "boolean", "description": "Whether the payment should be marked as failed when a temporary error is\nreturned from the given route. Set it to true so the payment won't be\nfailed unless a terminal error is occurred, such as payment timeout, no\nroutes, incorrect payment details, or insufficient funds." + }, + "first_hop_custom_records": { + "type": "object", + "additionalProperties": { + "type": "string", + "format": "byte" + }, + "description": "An optional field that can be used to pass an arbitrary set of TLV records\nto the first hop peer of this payment. This can be used to pass application\nspecific data during the payment attempt. Record types are required to be in\nthe custom range \u003e= 65536. When using REST, the values must be encoded as\nbase64." } } }, diff --git a/lnrpc/routerrpc/router_backend.go b/lnrpc/routerrpc/router_backend.go index ac493ba330..9b41d9d8c1 100644 --- a/lnrpc/routerrpc/router_backend.go +++ b/lnrpc/routerrpc/router_backend.go @@ -858,6 +858,12 @@ func (r *RouterBackend) extractIntentFromSendRequest( } payIntent.DestCustomRecords = customRecords + firstHopRecords := lnwire.CustomRecords(rpcPayReq.FirstHopCustomRecords) + if err := firstHopRecords.Validate(); err != nil { + return nil, err + } + payIntent.FirstHopCustomRecords = firstHopRecords + payIntent.PayAttemptTimeout = time.Second * time.Duration(rpcPayReq.TimeoutSeconds) diff --git a/lnrpc/routerrpc/router_server.go b/lnrpc/routerrpc/router_server.go index f43f95929f..8091998a93 100644 --- a/lnrpc/routerrpc/router_server.go +++ b/lnrpc/routerrpc/router_server.go @@ -864,6 +864,11 @@ func (s *Server) SendToRouteV2(ctx context.Context, return nil, err } + firstHopRecords := lnwire.CustomRecords(req.FirstHopCustomRecords) + if err := firstHopRecords.Validate(); err != nil { + return nil, err + } + var attempt *channeldb.HTLCAttempt // Pass route to the router. This call returns the full htlc attempt @@ -873,9 +878,13 @@ func (s *Server) SendToRouteV2(ctx context.Context, // case, we give precedence to the attempt information as stored in the // db. if req.SkipTempErr { - attempt, err = s.cfg.Router.SendToRouteSkipTempErr(hash, route) + attempt, err = s.cfg.Router.SendToRouteSkipTempErr( + hash, route, firstHopRecords, + ) } else { - attempt, err = s.cfg.Router.SendToRoute(hash, route) + attempt, err = s.cfg.Router.SendToRoute( + hash, route, firstHopRecords, + ) } if attempt != nil { rpcAttempt, err := s.cfg.RouterBackend.MarshalHTLCAttempt( @@ -1458,9 +1467,26 @@ func (s *Server) BuildRoute(_ context.Context, ) } + var firstHopBlob fn.Option[[]byte] + if len(req.FirstHopCustomRecords) > 0 { + firstHopRecords := lnwire.CustomRecords( + req.FirstHopCustomRecords, + ) + if err := firstHopRecords.Validate(); err != nil { + return nil, err + } + + firstHopData, err := firstHopRecords.Serialize() + if err != nil { + return nil, err + } + firstHopBlob = fn.Some(firstHopData) + } + // Build the route and return it to the caller. route, err := s.cfg.Router.BuildRoute( amt, hops, outgoingChan, req.FinalCltvDelta, payAddr, + firstHopBlob, ) if err != nil { return nil, err diff --git a/routing/router.go b/routing/router.go index 0ec5976aeb..dbd2824bd7 100644 --- a/routing/router.go +++ b/routing/router.go @@ -1057,18 +1057,21 @@ func (r *ChannelRouter) PreparePayment(payment *LightningPayment) ( // SendToRoute sends a payment using the provided route and fails the payment // when an error is returned from the attempt. -func (r *ChannelRouter) SendToRoute(htlcHash lntypes.Hash, - rt *route.Route) (*channeldb.HTLCAttempt, error) { +func (r *ChannelRouter) SendToRoute(htlcHash lntypes.Hash, rt *route.Route, + firstHopCustomRecords lnwire.CustomRecords) (*channeldb.HTLCAttempt, + error) { - return r.sendToRoute(htlcHash, rt, false) + return r.sendToRoute(htlcHash, rt, false, firstHopCustomRecords) } // SendToRouteSkipTempErr sends a payment using the provided route and fails // the payment ONLY when a terminal error is returned from the attempt. func (r *ChannelRouter) SendToRouteSkipTempErr(htlcHash lntypes.Hash, - rt *route.Route) (*channeldb.HTLCAttempt, error) { + rt *route.Route, + firstHopCustomRecords lnwire.CustomRecords) (*channeldb.HTLCAttempt, + error) { - return r.sendToRoute(htlcHash, rt, true) + return r.sendToRoute(htlcHash, rt, true, firstHopCustomRecords) } // sendToRoute attempts to send a payment with the given hash through the @@ -1078,7 +1081,9 @@ func (r *ChannelRouter) SendToRouteSkipTempErr(htlcHash lntypes.Hash, // was initiated, both return values will be non-nil. If skipTempErr is true, // the payment won't be failed unless a terminal error has occurred. func (r *ChannelRouter) sendToRoute(htlcHash lntypes.Hash, rt *route.Route, - skipTempErr bool) (*channeldb.HTLCAttempt, error) { + skipTempErr bool, + firstHopCustomRecords lnwire.CustomRecords) (*channeldb.HTLCAttempt, + error) { log.Debugf("SendToRoute for payment %v with skipTempErr=%v", htlcHash, skipTempErr) @@ -1149,7 +1154,8 @@ func (r *ChannelRouter) sendToRoute(htlcHash lntypes.Hash, rt *route.Route, // - no payment timeout. // - no current block height. p := newPaymentLifecycle( - r, 0, paymentIdentifier, nil, shardTracker, 0, nil, + r, 0, paymentIdentifier, nil, shardTracker, 0, + firstHopCustomRecords, ) // We found a route to try, create a new HTLC attempt to try. @@ -1318,8 +1324,9 @@ func (e ErrNoChannel) Error() string { // amount is nil, the minimum routable amount is used. To force a specific // outgoing channel, use the outgoingChan parameter. func (r *ChannelRouter) BuildRoute(amt fn.Option[lnwire.MilliSatoshi], - hops []route.Vertex, outgoingChan *uint64, - finalCltvDelta int32, payAddr *[32]byte) (*route.Route, error) { + hops []route.Vertex, outgoingChan *uint64, finalCltvDelta int32, + payAddr *[32]byte, _ fn.Option[[]byte]) (*route.Route, + error) { log.Tracef("BuildRoute called: hopsCount=%v, amt=%v", len(hops), amt) diff --git a/routing/router_test.go b/routing/router_test.go index 411a6c81cd..676fde4cc9 100644 --- a/routing/router_test.go +++ b/routing/router_test.go @@ -519,7 +519,7 @@ func TestChannelUpdateValidation(t *testing.T) { // Send off the payment request to the router. The specified route // should be attempted and the channel update should be received by // graph and ignored because it is missing a valid signature. - _, err = ctx.router.SendToRoute(payment, rt) + _, err = ctx.router.SendToRoute(payment, rt, nil) require.Error(t, err, "expected route to fail with channel update") _, e1, e2, err = ctx.graph.FetchChannelEdgesByID( @@ -539,7 +539,7 @@ func TestChannelUpdateValidation(t *testing.T) { ctx.graphBuilder.setNextReject(false) // Retry the payment using the same route as before. - _, err = ctx.router.SendToRoute(payment, rt) + _, err = ctx.router.SendToRoute(payment, rt, nil) require.Error(t, err, "expected route to fail with channel update") // This time a valid signature was supplied and the policy change should @@ -1428,7 +1428,7 @@ func TestSendToRouteStructuredError(t *testing.T) { // update should be received by router and ignored // because it is missing a valid // signature. - _, err = ctx.router.SendToRoute(payment, rt) + _, err = ctx.router.SendToRoute(payment, rt, nil) fErr, ok := err.(*htlcswitch.ForwardingError) require.True( @@ -1507,7 +1507,7 @@ func TestSendToRouteMaxHops(t *testing.T) { // Send off the payment request to the router. We expect an error back // indicating that the route is too long. var payHash lntypes.Hash - _, err = ctx.router.SendToRoute(payHash, rt) + _, err = ctx.router.SendToRoute(payHash, rt, nil) if err != route.ErrMaxRouteHopsExceeded { t.Fatalf("expected ErrMaxRouteHopsExceeded, but got %v", err) } @@ -1649,12 +1649,16 @@ func TestBuildRoute(t *testing.T) { // Test that we can't build a route when no hops are given. hops = []route.Vertex{} - _, err = ctx.router.BuildRoute(noAmt, hops, nil, 40, nil) + _, err = ctx.router.BuildRoute( + noAmt, hops, nil, 40, nil, fn.None[[]byte](), + ) require.Error(t, err) // Create hop list for an unknown destination. hops := []route.Vertex{ctx.aliases["b"], ctx.aliases["y"]} - _, err = ctx.router.BuildRoute(noAmt, hops, nil, 40, &payAddr) + _, err = ctx.router.BuildRoute( + noAmt, hops, nil, 40, &payAddr, fn.None[[]byte](), + ) noChanErr := ErrNoChannel{} require.ErrorAs(t, err, &noChanErr) require.Equal(t, 1, noChanErr.position) @@ -1664,7 +1668,9 @@ func TestBuildRoute(t *testing.T) { amt := lnwire.NewMSatFromSatoshis(100) // Build the route for the given amount. - rt, err := ctx.router.BuildRoute(fn.Some(amt), hops, nil, 40, &payAddr) + rt, err := ctx.router.BuildRoute( + fn.Some(amt), hops, nil, 40, &payAddr, fn.None[[]byte](), + ) require.NoError(t, err) // Check that we get the expected route back. The total amount should be @@ -1674,7 +1680,9 @@ func TestBuildRoute(t *testing.T) { require.Equal(t, lnwire.MilliSatoshi(106000), rt.TotalAmount) // Build the route for the minimum amount. - rt, err = ctx.router.BuildRoute(noAmt, hops, nil, 40, &payAddr) + rt, err = ctx.router.BuildRoute( + noAmt, hops, nil, 40, &payAddr, fn.None[[]byte](), + ) require.NoError(t, err) // Check that we get the expected route back. The minimum that we can @@ -1690,7 +1698,9 @@ func TestBuildRoute(t *testing.T) { // Test a route that contains incompatible channel htlc constraints. // There is no amount that can pass through both channel 5 and 4. hops = []route.Vertex{ctx.aliases["e"], ctx.aliases["c"]} - _, err = ctx.router.BuildRoute(noAmt, hops, nil, 40, nil) + _, err = ctx.router.BuildRoute( + noAmt, hops, nil, 40, nil, fn.None[[]byte](), + ) require.Error(t, err) noChanErr = ErrNoChannel{} require.ErrorAs(t, err, &noChanErr) @@ -1708,7 +1718,9 @@ func TestBuildRoute(t *testing.T) { // amount that could be delivered to the receiver of 21819 msat, using // policy of channel 3. hops = []route.Vertex{ctx.aliases["b"], ctx.aliases["z"]} - rt, err = ctx.router.BuildRoute(noAmt, hops, nil, 40, &payAddr) + rt, err = ctx.router.BuildRoute( + noAmt, hops, nil, 40, &payAddr, fn.None[[]byte](), + ) require.NoError(t, err) checkHops(rt, []uint64{1, 8}, payAddr) require.Equal(t, lnwire.MilliSatoshi(21200), rt.TotalAmount) @@ -1720,7 +1732,9 @@ func TestBuildRoute(t *testing.T) { // We get 106000 - 1000 (base in) - 0.001 * 106000 (rate in) = 104894. hops = []route.Vertex{ctx.aliases["d"], ctx.aliases["f"]} amt = lnwire.NewMSatFromSatoshis(100) - rt, err = ctx.router.BuildRoute(fn.Some(amt), hops, nil, 40, &payAddr) + rt, err = ctx.router.BuildRoute( + fn.Some(amt), hops, nil, 40, &payAddr, fn.None[[]byte](), + ) require.NoError(t, err) checkHops(rt, []uint64{9, 10}, payAddr) require.EqualValues(t, 104894, rt.TotalAmount) @@ -1734,7 +1748,9 @@ func TestBuildRoute(t *testing.T) { // of 20179 msat, which results in underpayment of 1 msat in fee. There // is a third pass through newRoute in which this gets corrected to end hops = []route.Vertex{ctx.aliases["d"], ctx.aliases["f"]} - rt, err = ctx.router.BuildRoute(noAmt, hops, nil, 40, &payAddr) + rt, err = ctx.router.BuildRoute( + noAmt, hops, nil, 40, &payAddr, fn.None[[]byte](), + ) require.NoError(t, err) checkHops(rt, []uint64{9, 10}, payAddr) require.EqualValues(t, 20180, rt.TotalAmount, "%v", rt.TotalAmount) @@ -2208,7 +2224,7 @@ func TestSendToRouteSkipTempErrSuccess(t *testing.T) { payment.On("TerminalInfo").Return(nil, nil) // Expect a successful send to route. - attempt, err := router.SendToRouteSkipTempErr(payHash, rt) + attempt, err := router.SendToRouteSkipTempErr(payHash, rt, nil) require.NoError(t, err) require.Equal(t, testAttempt, attempt) @@ -2261,7 +2277,7 @@ func TestSendToRouteSkipTempErrNonMPP(t *testing.T) { }} // Expect an error to be returned. - attempt, err := router.SendToRouteSkipTempErr(payHash, rt) + attempt, err := router.SendToRouteSkipTempErr(payHash, rt, nil) require.ErrorIs(t, ErrSkipTempErr, err) require.Nil(t, attempt) @@ -2345,7 +2361,7 @@ func TestSendToRouteSkipTempErrTempFailure(t *testing.T) { payment.On("TerminalInfo").Return(nil, nil) // Expect a failed send to route. - attempt, err := router.SendToRouteSkipTempErr(payHash, rt) + attempt, err := router.SendToRouteSkipTempErr(payHash, rt, nil) require.Equal(t, tempErr, err) require.Equal(t, testAttempt, attempt) @@ -2432,7 +2448,7 @@ func TestSendToRouteSkipTempErrPermanentFailure(t *testing.T) { payment.On("TerminalInfo").Return(nil, &failureReason) // Expect a failed send to route. - attempt, err := router.SendToRouteSkipTempErr(payHash, rt) + attempt, err := router.SendToRouteSkipTempErr(payHash, rt, nil) require.Equal(t, permErr, err) require.Equal(t, testAttempt, attempt) @@ -2517,7 +2533,7 @@ func TestSendToRouteTempFailure(t *testing.T) { ).Return(nil, nil) // Expect a failed send to route. - attempt, err := router.SendToRoute(payHash, rt) + attempt, err := router.SendToRoute(payHash, rt, nil) require.Equal(t, tempErr, err) require.Equal(t, testAttempt, attempt) diff --git a/rpcserver.go b/rpcserver.go index ad54601577..cfbdbdd103 100644 --- a/rpcserver.go +++ b/rpcserver.go @@ -5452,7 +5452,7 @@ func (r *rpcServer) dispatchPaymentIntent( } else { var attempt *channeldb.HTLCAttempt attempt, routerErr = r.server.chanRouter.SendToRoute( - payIntent.rHash, payIntent.route, + payIntent.rHash, payIntent.route, nil, ) if routerErr == nil { From ff1a45549e4d475987e57736b63e5084b0106e4e Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Fri, 30 Aug 2024 13:10:34 +0200 Subject: [PATCH 068/218] channeldb+routing: persist first hop custom records With this commit we make sure the first hop custom records aren't lost on restart/resume of a payment, so we persist it as part of the PaymentCreationInfo struct. --- channeldb/payments.go | 27 +++++++++++++++++++- channeldb/payments_test.go | 52 +++++++++++++++++++------------------- routing/router.go | 20 ++++++++------- 3 files changed, 63 insertions(+), 36 deletions(-) diff --git a/channeldb/payments.go b/channeldb/payments.go index 079fedf235..a0db8f22ad 100644 --- a/channeldb/payments.go +++ b/channeldb/payments.go @@ -195,6 +195,11 @@ type PaymentCreationInfo struct { // PaymentRequest is the full payment request, if any. PaymentRequest []byte + + // FirstHopCustomRecords are the TLV records that are to be sent to the + // first hop of this payment. These records will be transmitted via the + // wire message only and therefore do not affect the onion payload size. + FirstHopCustomRecords lnwire.CustomRecords } // htlcBucketKey creates a composite key from prefix and id where the result is @@ -1010,10 +1015,21 @@ func serializePaymentCreationInfo(w io.Writer, c *PaymentCreationInfo) error { return err } + // Any remaining bytes are TLV encoded records. Currently, these are + // only the custom records provided by the user to be sent to the first + // hop. But this can easily be extended with further records by merging + // the records into a single TLV stream. + err := c.FirstHopCustomRecords.SerializeTo(w) + if err != nil { + return err + } + return nil } -func deserializePaymentCreationInfo(r io.Reader) (*PaymentCreationInfo, error) { +func deserializePaymentCreationInfo(r io.Reader) (*PaymentCreationInfo, + error) { + var scratch [8]byte c := &PaymentCreationInfo{} @@ -1046,6 +1062,15 @@ func deserializePaymentCreationInfo(r io.Reader) (*PaymentCreationInfo, error) { } c.PaymentRequest = payReq + // Any remaining bytes are TLV encoded records. Currently, these are + // only the custom records provided by the user to be sent to the first + // hop. But this can easily be extended with further records by merging + // the records into a single TLV stream. + c.FirstHopCustomRecords, err = lnwire.ParseCustomRecordsFrom(r) + if err != nil { + return nil, err + } + return c, nil } diff --git a/channeldb/payments_test.go b/channeldb/payments_test.go index 769f4cc77f..844b818e84 100644 --- a/channeldb/payments_test.go +++ b/channeldb/payments_test.go @@ -13,6 +13,7 @@ import ( "github.com/davecgh/go-spew/spew" "github.com/lightningnetwork/lnd/kvdb" "github.com/lightningnetwork/lnd/lntypes" + "github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/record" "github.com/lightningnetwork/lnd/routing/route" "github.com/stretchr/testify/require" @@ -108,7 +109,7 @@ func makeFakeInfo() (*PaymentCreationInfo, *HTLCAttemptInfo) { // Use single second precision to avoid false positive test // failures due to the monotonic time component. CreationTime: time.Unix(time.Now().Unix(), 0), - PaymentRequest: []byte(""), + PaymentRequest: []byte("test"), } a := NewHtlcAttempt( @@ -124,36 +125,40 @@ func TestSentPaymentSerialization(t *testing.T) { c, s := makeFakeInfo() var b bytes.Buffer - if err := serializePaymentCreationInfo(&b, c); err != nil { - t.Fatalf("unable to serialize creation info: %v", err) - } + require.NoError(t, serializePaymentCreationInfo(&b, c), "serialize") - newCreationInfo, err := deserializePaymentCreationInfo(&b) - require.NoError(t, err, "unable to deserialize creation info") + // Assert the length of the serialized creation info is as expected, + // without any custom records. + baseLength := 32 + 8 + 8 + 4 + len(c.PaymentRequest) + require.Len(t, b.Bytes(), baseLength) - if !reflect.DeepEqual(c, newCreationInfo) { - t.Fatalf("Payments do not match after "+ - "serialization/deserialization %v vs %v", - spew.Sdump(c), spew.Sdump(newCreationInfo), - ) - } + newCreationInfo, err := deserializePaymentCreationInfo(&b) + require.NoError(t, err, "deserialize") + require.Equal(t, c, newCreationInfo) b.Reset() - if err := serializeHTLCAttemptInfo(&b, s); err != nil { - t.Fatalf("unable to serialize info: %v", err) + + // Now we add some custom records to the creation info and serialize it + // again. + c.FirstHopCustomRecords = lnwire.CustomRecords{ + lnwire.MinCustomRecordsTlvType: []byte{1, 2, 3}, } + require.NoError(t, serializePaymentCreationInfo(&b, c), "serialize") + + newCreationInfo, err = deserializePaymentCreationInfo(&b) + require.NoError(t, err, "deserialize") + require.Equal(t, c, newCreationInfo) + + require.NoError(t, serializeHTLCAttemptInfo(&b, s), "serialize") newWireInfo, err := deserializeHTLCAttemptInfo(&b) - require.NoError(t, err, "unable to deserialize info") + require.NoError(t, err, "deserialize") newWireInfo.AttemptID = s.AttemptID - // First we verify all the records match up porperly, as they aren't + // First we verify all the records match up properly, as they aren't // able to be properly compared using reflect.DeepEqual. err = assertRouteEqual(&s.Route, &newWireInfo.Route) - if err != nil { - t.Fatalf("Routes do not match after "+ - "serialization/deserialization: %v", err) - } + require.NoError(t, err) // Clear routes to allow DeepEqual to compare the remaining fields. newWireInfo.Route = route.Route{} @@ -163,12 +168,7 @@ func TestSentPaymentSerialization(t *testing.T) { // DeepEqual, and assert that our key equals the original key. require.Equal(t, s.cachedSessionKey, newWireInfo.SessionKey()) - if !reflect.DeepEqual(s, newWireInfo) { - t.Fatalf("Payments do not match after "+ - "serialization/deserialization %v vs %v", - spew.Sdump(s), spew.Sdump(newWireInfo), - ) - } + require.Equal(t, s, newWireInfo) } // assertRouteEquals compares to routes for equality and returns an error if diff --git a/routing/router.go b/routing/router.go index dbd2824bd7..de5f45fd89 100644 --- a/routing/router.go +++ b/routing/router.go @@ -1022,10 +1022,11 @@ func (r *ChannelRouter) PreparePayment(payment *LightningPayment) ( // // TODO(roasbeef): store records as part of creation info? info := &channeldb.PaymentCreationInfo{ - PaymentIdentifier: payment.Identifier(), - Value: payment.Amount, - CreationTime: r.cfg.Clock.Now(), - PaymentRequest: payment.PaymentRequest, + PaymentIdentifier: payment.Identifier(), + Value: payment.Amount, + CreationTime: r.cfg.Clock.Now(), + PaymentRequest: payment.PaymentRequest, + FirstHopCustomRecords: payment.FirstHopCustomRecords, } // Create a new ShardTracker that we'll use during the life cycle of @@ -1120,10 +1121,11 @@ func (r *ChannelRouter) sendToRoute(htlcHash lntypes.Hash, rt *route.Route, // Record this payment hash with the ControlTower, ensuring it is not // already in-flight. info := &channeldb.PaymentCreationInfo{ - PaymentIdentifier: paymentIdentifier, - Value: amt, - CreationTime: r.cfg.Clock.Now(), - PaymentRequest: nil, + PaymentIdentifier: paymentIdentifier, + Value: amt, + CreationTime: r.cfg.Clock.Now(), + PaymentRequest: nil, + FirstHopCustomRecords: firstHopCustomRecords, } err := r.cfg.Control.InitPayment(paymentIdentifier, info) @@ -1483,7 +1485,7 @@ func (r *ChannelRouter) resumePayments() error { noTimeout := time.Duration(0) _, _, err := r.sendPayment( context.Background(), 0, payHash, noTimeout, paySession, - shardTracker, nil, + shardTracker, payment.Info.FirstHopCustomRecords, ) if err != nil { log.Errorf("Resuming payment %v failed: %v", payHash, From 0403b97a871c30dff7343e029cc7f223a307e324 Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Fri, 30 Aug 2024 13:11:22 +0200 Subject: [PATCH 069/218] lnrpc: add first hop custom records to RPC payment info --- lnrpc/lightning.pb.go | 2449 ++++++++++++++------------- lnrpc/lightning.proto | 6 + lnrpc/lightning.swagger.json | 8 + lnrpc/routerrpc/router.swagger.json | 8 + lnrpc/routerrpc/router_backend.go | 31 +- 5 files changed, 1274 insertions(+), 1228 deletions(-) diff --git a/lnrpc/lightning.pb.go b/lnrpc/lightning.pb.go index 285dbdce9c..624a13be43 100644 --- a/lnrpc/lightning.pb.go +++ b/lnrpc/lightning.pb.go @@ -13519,6 +13519,9 @@ type Payment struct { // older versions of lnd. PaymentIndex uint64 `protobuf:"varint,15,opt,name=payment_index,json=paymentIndex,proto3" json:"payment_index,omitempty"` FailureReason PaymentFailureReason `protobuf:"varint,16,opt,name=failure_reason,json=failureReason,proto3,enum=lnrpc.PaymentFailureReason" json:"failure_reason,omitempty"` + // The custom TLV records that were sent to the first hop as part of the HTLC + // wire message for this payment. + FirstHopCustomRecords map[uint64][]byte `protobuf:"bytes,17,rep,name=first_hop_custom_records,json=firstHopCustomRecords,proto3" json:"first_hop_custom_records,omitempty" protobuf_key:"varint,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` } func (x *Payment) Reset() { @@ -13661,6 +13664,13 @@ func (x *Payment) GetFailureReason() PaymentFailureReason { return PaymentFailureReason_FAILURE_REASON_NONE } +func (x *Payment) GetFirstHopCustomRecords() map[uint64][]byte { + if x != nil { + return x.FirstHopCustomRecords + } + return nil +} + type HTLCAttempt struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -20210,7 +20220,7 @@ var file_lightning_proto_rawDesc = []byte{ 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x61, 0x64, 0x64, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x21, 0x0a, 0x0c, 0x73, 0x65, 0x74, 0x74, 0x6c, 0x65, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x73, 0x65, 0x74, 0x74, 0x6c, 0x65, 0x49, 0x6e, 0x64, 0x65, - 0x78, 0x22, 0x9d, 0x05, 0x0a, 0x07, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x21, 0x0a, + 0x78, 0x22, 0xcb, 0x06, 0x0a, 0x07, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x48, 0x61, 0x73, 0x68, 0x12, 0x18, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x42, @@ -20246,1027 +20256,1038 @@ var file_lightning_proto_rawDesc = []byte{ 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1b, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x46, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x52, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x52, 0x0d, 0x66, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x52, 0x65, 0x61, 0x73, 0x6f, 0x6e, - 0x22, 0x59, 0x0a, 0x0d, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, - 0x73, 0x12, 0x0f, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x1a, 0x02, - 0x08, 0x01, 0x12, 0x0d, 0x0a, 0x09, 0x49, 0x4e, 0x5f, 0x46, 0x4c, 0x49, 0x47, 0x48, 0x54, 0x10, - 0x01, 0x12, 0x0d, 0x0a, 0x09, 0x53, 0x55, 0x43, 0x43, 0x45, 0x45, 0x44, 0x45, 0x44, 0x10, 0x02, - 0x12, 0x0a, 0x0a, 0x06, 0x46, 0x41, 0x49, 0x4c, 0x45, 0x44, 0x10, 0x03, 0x12, 0x0d, 0x0a, 0x09, - 0x49, 0x4e, 0x49, 0x54, 0x49, 0x41, 0x54, 0x45, 0x44, 0x10, 0x04, 0x4a, 0x04, 0x08, 0x04, 0x10, - 0x05, 0x22, 0xd5, 0x02, 0x0a, 0x0b, 0x48, 0x54, 0x4c, 0x43, 0x41, 0x74, 0x74, 0x65, 0x6d, 0x70, - 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x61, 0x74, 0x74, 0x65, 0x6d, 0x70, 0x74, 0x5f, 0x69, 0x64, 0x18, - 0x07, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x61, 0x74, 0x74, 0x65, 0x6d, 0x70, 0x74, 0x49, 0x64, - 0x12, 0x35, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, - 0x32, 0x1d, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x48, 0x54, 0x4c, 0x43, 0x41, 0x74, 0x74, - 0x65, 0x6d, 0x70, 0x74, 0x2e, 0x48, 0x54, 0x4c, 0x43, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, - 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x22, 0x0a, 0x05, 0x72, 0x6f, 0x75, 0x74, 0x65, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x52, - 0x6f, 0x75, 0x74, 0x65, 0x52, 0x05, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x12, 0x26, 0x0a, 0x0f, 0x61, - 0x74, 0x74, 0x65, 0x6d, 0x70, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x6e, 0x73, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x03, 0x52, 0x0d, 0x61, 0x74, 0x74, 0x65, 0x6d, 0x70, 0x74, 0x54, 0x69, 0x6d, - 0x65, 0x4e, 0x73, 0x12, 0x26, 0x0a, 0x0f, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x5f, 0x74, - 0x69, 0x6d, 0x65, 0x5f, 0x6e, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0d, 0x72, 0x65, - 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x4e, 0x73, 0x12, 0x28, 0x0a, 0x07, 0x66, - 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x6c, - 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x52, 0x07, 0x66, 0x61, - 0x69, 0x6c, 0x75, 0x72, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x72, 0x65, 0x69, 0x6d, 0x61, 0x67, - 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x70, 0x72, 0x65, 0x69, 0x6d, 0x61, 0x67, - 0x65, 0x22, 0x36, 0x0a, 0x0a, 0x48, 0x54, 0x4c, 0x43, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, - 0x0d, 0x0a, 0x09, 0x49, 0x4e, 0x5f, 0x46, 0x4c, 0x49, 0x47, 0x48, 0x54, 0x10, 0x00, 0x12, 0x0d, - 0x0a, 0x09, 0x53, 0x55, 0x43, 0x43, 0x45, 0x45, 0x44, 0x45, 0x44, 0x10, 0x01, 0x12, 0x0a, 0x0a, - 0x06, 0x46, 0x41, 0x49, 0x4c, 0x45, 0x44, 0x10, 0x02, 0x22, 0xb4, 0x02, 0x0a, 0x13, 0x4c, 0x69, - 0x73, 0x74, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x12, 0x2d, 0x0a, 0x12, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x5f, 0x69, 0x6e, 0x63, - 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x11, 0x69, - 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x49, 0x6e, 0x63, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, - 0x12, 0x21, 0x0a, 0x0c, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x5f, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x4f, 0x66, 0x66, - 0x73, 0x65, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x6d, 0x61, 0x78, 0x5f, 0x70, 0x61, 0x79, 0x6d, 0x65, - 0x6e, 0x74, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x6d, 0x61, 0x78, 0x50, 0x61, - 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x76, 0x65, 0x72, 0x73, - 0x65, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x72, 0x65, 0x76, 0x65, 0x72, 0x73, - 0x65, 0x64, 0x12, 0x30, 0x0a, 0x14, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x74, 0x6f, 0x74, 0x61, - 0x6c, 0x5f, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, - 0x52, 0x12, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x54, 0x6f, 0x74, 0x61, 0x6c, 0x50, 0x61, 0x79, 0x6d, - 0x65, 0x6e, 0x74, 0x73, 0x12, 0x2e, 0x0a, 0x13, 0x63, 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x5f, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x73, 0x74, 0x61, 0x72, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, - 0x04, 0x52, 0x11, 0x63, 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x61, 0x74, 0x65, 0x53, - 0x74, 0x61, 0x72, 0x74, 0x12, 0x2a, 0x0a, 0x11, 0x63, 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x5f, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x65, 0x6e, 0x64, 0x18, 0x07, 0x20, 0x01, 0x28, 0x04, 0x52, - 0x0f, 0x63, 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x61, 0x74, 0x65, 0x45, 0x6e, 0x64, - 0x22, 0xca, 0x01, 0x0a, 0x14, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, - 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2a, 0x0a, 0x08, 0x70, 0x61, 0x79, - 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x6c, 0x6e, - 0x72, 0x70, 0x63, 0x2e, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x08, 0x70, 0x61, 0x79, - 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x2c, 0x0a, 0x12, 0x66, 0x69, 0x72, 0x73, 0x74, 0x5f, 0x69, - 0x6e, 0x64, 0x65, 0x78, 0x5f, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x04, 0x52, 0x10, 0x66, 0x69, 0x72, 0x73, 0x74, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x4f, 0x66, 0x66, - 0x73, 0x65, 0x74, 0x12, 0x2a, 0x0a, 0x11, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x69, 0x6e, 0x64, 0x65, - 0x78, 0x5f, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0f, - 0x6c, 0x61, 0x73, 0x74, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x12, - 0x2c, 0x0a, 0x12, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x6e, 0x75, 0x6d, 0x5f, 0x70, 0x61, 0x79, - 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x10, 0x74, 0x6f, 0x74, - 0x61, 0x6c, 0x4e, 0x75, 0x6d, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x22, 0x65, 0x0a, - 0x14, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, - 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x70, 0x61, 0x79, - 0x6d, 0x65, 0x6e, 0x74, 0x48, 0x61, 0x73, 0x68, 0x12, 0x2a, 0x0a, 0x11, 0x66, 0x61, 0x69, 0x6c, - 0x65, 0x64, 0x5f, 0x68, 0x74, 0x6c, 0x63, 0x73, 0x5f, 0x6f, 0x6e, 0x6c, 0x79, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x08, 0x52, 0x0f, 0x66, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x48, 0x74, 0x6c, 0x63, 0x73, - 0x4f, 0x6e, 0x6c, 0x79, 0x22, 0x9b, 0x01, 0x0a, 0x18, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x41, - 0x6c, 0x6c, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x12, 0x30, 0x0a, 0x14, 0x66, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x5f, 0x70, 0x61, 0x79, 0x6d, - 0x65, 0x6e, 0x74, 0x73, 0x5f, 0x6f, 0x6e, 0x6c, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, - 0x12, 0x66, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x4f, - 0x6e, 0x6c, 0x79, 0x12, 0x2a, 0x0a, 0x11, 0x66, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x5f, 0x68, 0x74, - 0x6c, 0x63, 0x73, 0x5f, 0x6f, 0x6e, 0x6c, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, - 0x66, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x48, 0x74, 0x6c, 0x63, 0x73, 0x4f, 0x6e, 0x6c, 0x79, 0x12, - 0x21, 0x0a, 0x0c, 0x61, 0x6c, 0x6c, 0x5f, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x18, - 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x61, 0x6c, 0x6c, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, - 0x74, 0x73, 0x22, 0x17, 0x0a, 0x15, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x50, 0x61, 0x79, 0x6d, - 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x0a, 0x19, 0x44, - 0x65, 0x6c, 0x65, 0x74, 0x65, 0x41, 0x6c, 0x6c, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x73, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xbf, 0x01, 0x0a, 0x15, 0x41, 0x62, 0x61, - 0x6e, 0x64, 0x6f, 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x12, 0x38, 0x0a, 0x0d, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x5f, 0x70, 0x6f, - 0x69, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x6c, 0x6e, 0x72, 0x70, - 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x0c, - 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x39, 0x0a, 0x19, - 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x66, 0x75, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x5f, - 0x73, 0x68, 0x69, 0x6d, 0x5f, 0x6f, 0x6e, 0x6c, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, - 0x16, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x46, 0x75, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x53, - 0x68, 0x69, 0x6d, 0x4f, 0x6e, 0x6c, 0x79, 0x12, 0x31, 0x0a, 0x16, 0x69, 0x5f, 0x6b, 0x6e, 0x6f, - 0x77, 0x5f, 0x77, 0x68, 0x61, 0x74, 0x5f, 0x69, 0x5f, 0x61, 0x6d, 0x5f, 0x64, 0x6f, 0x69, 0x6e, - 0x67, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x11, 0x69, 0x4b, 0x6e, 0x6f, 0x77, 0x57, 0x68, - 0x61, 0x74, 0x49, 0x41, 0x6d, 0x44, 0x6f, 0x69, 0x6e, 0x67, 0x22, 0x18, 0x0a, 0x16, 0x41, 0x62, - 0x61, 0x6e, 0x64, 0x6f, 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x46, 0x0a, 0x11, 0x44, 0x65, 0x62, 0x75, 0x67, 0x4c, 0x65, 0x76, - 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x68, 0x6f, - 0x77, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x04, 0x73, 0x68, 0x6f, 0x77, 0x12, 0x1d, 0x0a, - 0x0a, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x5f, 0x73, 0x70, 0x65, 0x63, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x09, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x53, 0x70, 0x65, 0x63, 0x22, 0x35, 0x0a, 0x12, - 0x44, 0x65, 0x62, 0x75, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x75, 0x62, 0x5f, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, - 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x75, 0x62, 0x53, 0x79, 0x73, 0x74, - 0x65, 0x6d, 0x73, 0x22, 0x27, 0x0a, 0x0c, 0x50, 0x61, 0x79, 0x52, 0x65, 0x71, 0x53, 0x74, 0x72, - 0x69, 0x6e, 0x67, 0x12, 0x17, 0x0a, 0x07, 0x70, 0x61, 0x79, 0x5f, 0x72, 0x65, 0x71, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x70, 0x61, 0x79, 0x52, 0x65, 0x71, 0x22, 0xf0, 0x04, 0x0a, - 0x06, 0x50, 0x61, 0x79, 0x52, 0x65, 0x71, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x74, 0x69, - 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, - 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x61, 0x79, - 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x0b, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x48, 0x61, 0x73, 0x68, 0x12, 0x21, 0x0a, 0x0c, - 0x6e, 0x75, 0x6d, 0x5f, 0x73, 0x61, 0x74, 0x6f, 0x73, 0x68, 0x69, 0x73, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x03, 0x52, 0x0b, 0x6e, 0x75, 0x6d, 0x53, 0x61, 0x74, 0x6f, 0x73, 0x68, 0x69, 0x73, 0x12, - 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x04, 0x20, 0x01, - 0x28, 0x03, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x16, 0x0a, - 0x06, 0x65, 0x78, 0x70, 0x69, 0x72, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x65, - 0x78, 0x70, 0x69, 0x72, 0x79, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, - 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, - 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x29, 0x0a, 0x10, 0x64, 0x65, 0x73, 0x63, 0x72, - 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x07, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x0f, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x61, - 0x73, 0x68, 0x12, 0x23, 0x0a, 0x0d, 0x66, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x5f, 0x61, - 0x64, 0x64, 0x72, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x66, 0x61, 0x6c, 0x6c, 0x62, - 0x61, 0x63, 0x6b, 0x41, 0x64, 0x64, 0x72, 0x12, 0x1f, 0x0a, 0x0b, 0x63, 0x6c, 0x74, 0x76, 0x5f, - 0x65, 0x78, 0x70, 0x69, 0x72, 0x79, 0x18, 0x09, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x63, 0x6c, - 0x74, 0x76, 0x45, 0x78, 0x70, 0x69, 0x72, 0x79, 0x12, 0x31, 0x0a, 0x0b, 0x72, 0x6f, 0x75, 0x74, - 0x65, 0x5f, 0x68, 0x69, 0x6e, 0x74, 0x73, 0x18, 0x0a, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x10, 0x2e, - 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x48, 0x69, 0x6e, 0x74, 0x52, - 0x0a, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x48, 0x69, 0x6e, 0x74, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x70, - 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x18, 0x0b, 0x20, 0x01, 0x28, - 0x0c, 0x52, 0x0b, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x41, 0x64, 0x64, 0x72, 0x12, 0x19, - 0x0a, 0x08, 0x6e, 0x75, 0x6d, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x03, - 0x52, 0x07, 0x6e, 0x75, 0x6d, 0x4d, 0x73, 0x61, 0x74, 0x12, 0x37, 0x0a, 0x08, 0x66, 0x65, 0x61, - 0x74, 0x75, 0x72, 0x65, 0x73, 0x18, 0x0d, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x6c, 0x6e, - 0x72, 0x70, 0x63, 0x2e, 0x50, 0x61, 0x79, 0x52, 0x65, 0x71, 0x2e, 0x46, 0x65, 0x61, 0x74, 0x75, - 0x72, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x08, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, - 0x65, 0x73, 0x12, 0x3e, 0x0a, 0x0d, 0x62, 0x6c, 0x69, 0x6e, 0x64, 0x65, 0x64, 0x5f, 0x70, 0x61, - 0x74, 0x68, 0x73, 0x18, 0x0e, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, - 0x63, 0x2e, 0x42, 0x6c, 0x69, 0x6e, 0x64, 0x65, 0x64, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, - 0x50, 0x61, 0x74, 0x68, 0x52, 0x0c, 0x62, 0x6c, 0x69, 0x6e, 0x64, 0x65, 0x64, 0x50, 0x61, 0x74, - 0x68, 0x73, 0x1a, 0x4b, 0x0a, 0x0d, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x45, 0x6e, - 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, - 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x24, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x65, 0x61, - 0x74, 0x75, 0x72, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, - 0x59, 0x0a, 0x07, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, - 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1f, - 0x0a, 0x0b, 0x69, 0x73, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x08, 0x52, 0x0a, 0x69, 0x73, 0x52, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x12, - 0x19, 0x0a, 0x08, 0x69, 0x73, 0x5f, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, - 0x08, 0x52, 0x07, 0x69, 0x73, 0x4b, 0x6e, 0x6f, 0x77, 0x6e, 0x22, 0x12, 0x0a, 0x10, 0x46, 0x65, - 0x65, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x95, - 0x02, 0x0a, 0x10, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x46, 0x65, 0x65, 0x52, 0x65, 0x70, - 0x6f, 0x72, 0x74, 0x12, 0x1b, 0x0a, 0x07, 0x63, 0x68, 0x61, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x05, - 0x20, 0x01, 0x28, 0x04, 0x42, 0x02, 0x30, 0x01, 0x52, 0x06, 0x63, 0x68, 0x61, 0x6e, 0x49, 0x64, - 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x5f, 0x70, 0x6f, 0x69, 0x6e, - 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, - 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x22, 0x0a, 0x0d, 0x62, 0x61, 0x73, 0x65, 0x5f, 0x66, 0x65, - 0x65, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b, 0x62, 0x61, - 0x73, 0x65, 0x46, 0x65, 0x65, 0x4d, 0x73, 0x61, 0x74, 0x12, 0x1e, 0x0a, 0x0b, 0x66, 0x65, 0x65, - 0x5f, 0x70, 0x65, 0x72, 0x5f, 0x6d, 0x69, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, - 0x66, 0x65, 0x65, 0x50, 0x65, 0x72, 0x4d, 0x69, 0x6c, 0x12, 0x19, 0x0a, 0x08, 0x66, 0x65, 0x65, - 0x5f, 0x72, 0x61, 0x74, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x01, 0x52, 0x07, 0x66, 0x65, 0x65, - 0x52, 0x61, 0x74, 0x65, 0x12, 0x31, 0x0a, 0x15, 0x69, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x5f, - 0x62, 0x61, 0x73, 0x65, 0x5f, 0x66, 0x65, 0x65, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x06, 0x20, - 0x01, 0x28, 0x05, 0x52, 0x12, 0x69, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x42, 0x61, 0x73, 0x65, - 0x46, 0x65, 0x65, 0x4d, 0x73, 0x61, 0x74, 0x12, 0x2d, 0x0a, 0x13, 0x69, 0x6e, 0x62, 0x6f, 0x75, - 0x6e, 0x64, 0x5f, 0x66, 0x65, 0x65, 0x5f, 0x70, 0x65, 0x72, 0x5f, 0x6d, 0x69, 0x6c, 0x18, 0x07, - 0x20, 0x01, 0x28, 0x05, 0x52, 0x10, 0x69, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x46, 0x65, 0x65, - 0x50, 0x65, 0x72, 0x4d, 0x69, 0x6c, 0x22, 0xb5, 0x01, 0x0a, 0x11, 0x46, 0x65, 0x65, 0x52, 0x65, - 0x70, 0x6f, 0x72, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3a, 0x0a, 0x0c, - 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x5f, 0x66, 0x65, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, - 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, - 0x65, 0x6c, 0x46, 0x65, 0x65, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x52, 0x0b, 0x63, 0x68, 0x61, - 0x6e, 0x6e, 0x65, 0x6c, 0x46, 0x65, 0x65, 0x73, 0x12, 0x1e, 0x0a, 0x0b, 0x64, 0x61, 0x79, 0x5f, - 0x66, 0x65, 0x65, 0x5f, 0x73, 0x75, 0x6d, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x64, - 0x61, 0x79, 0x46, 0x65, 0x65, 0x53, 0x75, 0x6d, 0x12, 0x20, 0x0a, 0x0c, 0x77, 0x65, 0x65, 0x6b, - 0x5f, 0x66, 0x65, 0x65, 0x5f, 0x73, 0x75, 0x6d, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, - 0x77, 0x65, 0x65, 0x6b, 0x46, 0x65, 0x65, 0x53, 0x75, 0x6d, 0x12, 0x22, 0x0a, 0x0d, 0x6d, 0x6f, - 0x6e, 0x74, 0x68, 0x5f, 0x66, 0x65, 0x65, 0x5f, 0x73, 0x75, 0x6d, 0x18, 0x04, 0x20, 0x01, 0x28, - 0x04, 0x52, 0x0b, 0x6d, 0x6f, 0x6e, 0x74, 0x68, 0x46, 0x65, 0x65, 0x53, 0x75, 0x6d, 0x22, 0x52, - 0x0a, 0x0a, 0x49, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x46, 0x65, 0x65, 0x12, 0x22, 0x0a, 0x0d, - 0x62, 0x61, 0x73, 0x65, 0x5f, 0x66, 0x65, 0x65, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x05, 0x52, 0x0b, 0x62, 0x61, 0x73, 0x65, 0x46, 0x65, 0x65, 0x4d, 0x73, 0x61, 0x74, - 0x12, 0x20, 0x0a, 0x0c, 0x66, 0x65, 0x65, 0x5f, 0x72, 0x61, 0x74, 0x65, 0x5f, 0x70, 0x70, 0x6d, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x66, 0x65, 0x65, 0x52, 0x61, 0x74, 0x65, 0x50, - 0x70, 0x6d, 0x22, 0xaa, 0x03, 0x0a, 0x13, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x55, 0x70, 0x64, - 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x06, 0x67, 0x6c, - 0x6f, 0x62, 0x61, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x48, 0x00, 0x52, 0x06, 0x67, 0x6c, - 0x6f, 0x62, 0x61, 0x6c, 0x12, 0x34, 0x0a, 0x0a, 0x63, 0x68, 0x61, 0x6e, 0x5f, 0x70, 0x6f, 0x69, - 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, - 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x48, 0x00, 0x52, - 0x09, 0x63, 0x68, 0x61, 0x6e, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x22, 0x0a, 0x0d, 0x62, 0x61, - 0x73, 0x65, 0x5f, 0x66, 0x65, 0x65, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x03, 0x52, 0x0b, 0x62, 0x61, 0x73, 0x65, 0x46, 0x65, 0x65, 0x4d, 0x73, 0x61, 0x74, 0x12, 0x19, - 0x0a, 0x08, 0x66, 0x65, 0x65, 0x5f, 0x72, 0x61, 0x74, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x01, - 0x52, 0x07, 0x66, 0x65, 0x65, 0x52, 0x61, 0x74, 0x65, 0x12, 0x20, 0x0a, 0x0c, 0x66, 0x65, 0x65, - 0x5f, 0x72, 0x61, 0x74, 0x65, 0x5f, 0x70, 0x70, 0x6d, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0d, 0x52, - 0x0a, 0x66, 0x65, 0x65, 0x52, 0x61, 0x74, 0x65, 0x50, 0x70, 0x6d, 0x12, 0x26, 0x0a, 0x0f, 0x74, - 0x69, 0x6d, 0x65, 0x5f, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x18, 0x05, - 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0d, 0x74, 0x69, 0x6d, 0x65, 0x4c, 0x6f, 0x63, 0x6b, 0x44, 0x65, - 0x6c, 0x74, 0x61, 0x12, 0x22, 0x0a, 0x0d, 0x6d, 0x61, 0x78, 0x5f, 0x68, 0x74, 0x6c, 0x63, 0x5f, - 0x6d, 0x73, 0x61, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x6d, 0x61, 0x78, 0x48, - 0x74, 0x6c, 0x63, 0x4d, 0x73, 0x61, 0x74, 0x12, 0x22, 0x0a, 0x0d, 0x6d, 0x69, 0x6e, 0x5f, 0x68, - 0x74, 0x6c, 0x63, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, - 0x6d, 0x69, 0x6e, 0x48, 0x74, 0x6c, 0x63, 0x4d, 0x73, 0x61, 0x74, 0x12, 0x35, 0x0a, 0x17, 0x6d, - 0x69, 0x6e, 0x5f, 0x68, 0x74, 0x6c, 0x63, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x5f, 0x73, 0x70, 0x65, - 0x63, 0x69, 0x66, 0x69, 0x65, 0x64, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, 0x14, 0x6d, 0x69, - 0x6e, 0x48, 0x74, 0x6c, 0x63, 0x4d, 0x73, 0x61, 0x74, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, - 0x65, 0x64, 0x12, 0x32, 0x0a, 0x0b, 0x69, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x5f, 0x66, 0x65, - 0x65, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, - 0x49, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x46, 0x65, 0x65, 0x52, 0x0a, 0x69, 0x6e, 0x62, 0x6f, - 0x75, 0x6e, 0x64, 0x46, 0x65, 0x65, 0x42, 0x07, 0x0a, 0x05, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x22, - 0x8c, 0x01, 0x0a, 0x0c, 0x46, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, - 0x12, 0x2b, 0x0a, 0x08, 0x6f, 0x75, 0x74, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4f, 0x75, 0x74, 0x50, 0x6f, - 0x69, 0x6e, 0x74, 0x52, 0x08, 0x6f, 0x75, 0x74, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x2c, 0x0a, - 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x14, 0x2e, - 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x46, 0x61, 0x69, 0x6c, - 0x75, 0x72, 0x65, 0x52, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x12, 0x21, 0x0a, 0x0c, 0x75, - 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x0b, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x22, 0x52, - 0x0a, 0x14, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3a, 0x0a, 0x0e, 0x66, 0x61, 0x69, 0x6c, 0x65, 0x64, - 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, - 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x55, 0x70, 0x64, - 0x61, 0x74, 0x65, 0x52, 0x0d, 0x66, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x55, 0x70, 0x64, 0x61, 0x74, - 0x65, 0x73, 0x22, 0xc9, 0x01, 0x0a, 0x18, 0x46, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x69, 0x6e, - 0x67, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, - 0x1d, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x04, 0x52, 0x09, 0x73, 0x74, 0x61, 0x72, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x19, - 0x0a, 0x08, 0x65, 0x6e, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, - 0x52, 0x07, 0x65, 0x6e, 0x64, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x69, 0x6e, 0x64, - 0x65, 0x78, 0x5f, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, - 0x0b, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x12, 0x24, 0x0a, 0x0e, - 0x6e, 0x75, 0x6d, 0x5f, 0x6d, 0x61, 0x78, 0x5f, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x04, - 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0c, 0x6e, 0x75, 0x6d, 0x4d, 0x61, 0x78, 0x45, 0x76, 0x65, 0x6e, - 0x74, 0x73, 0x12, 0x2a, 0x0a, 0x11, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x61, 0x6c, 0x69, 0x61, 0x73, - 0x5f, 0x6c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, 0x70, - 0x65, 0x65, 0x72, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x22, 0x85, - 0x03, 0x0a, 0x0f, 0x46, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x45, 0x76, 0x65, - 0x6e, 0x74, 0x12, 0x20, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x04, 0x42, 0x02, 0x18, 0x01, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, - 0x74, 0x61, 0x6d, 0x70, 0x12, 0x20, 0x0a, 0x0a, 0x63, 0x68, 0x61, 0x6e, 0x5f, 0x69, 0x64, 0x5f, - 0x69, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x42, 0x02, 0x30, 0x01, 0x52, 0x08, 0x63, 0x68, - 0x61, 0x6e, 0x49, 0x64, 0x49, 0x6e, 0x12, 0x22, 0x0a, 0x0b, 0x63, 0x68, 0x61, 0x6e, 0x5f, 0x69, - 0x64, 0x5f, 0x6f, 0x75, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x42, 0x02, 0x30, 0x01, 0x52, - 0x09, 0x63, 0x68, 0x61, 0x6e, 0x49, 0x64, 0x4f, 0x75, 0x74, 0x12, 0x15, 0x0a, 0x06, 0x61, 0x6d, - 0x74, 0x5f, 0x69, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x61, 0x6d, 0x74, 0x49, - 0x6e, 0x12, 0x17, 0x0a, 0x07, 0x61, 0x6d, 0x74, 0x5f, 0x6f, 0x75, 0x74, 0x18, 0x06, 0x20, 0x01, - 0x28, 0x04, 0x52, 0x06, 0x61, 0x6d, 0x74, 0x4f, 0x75, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x66, 0x65, - 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x04, 0x52, 0x03, 0x66, 0x65, 0x65, 0x12, 0x19, 0x0a, 0x08, - 0x66, 0x65, 0x65, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x08, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, - 0x66, 0x65, 0x65, 0x4d, 0x73, 0x61, 0x74, 0x12, 0x1e, 0x0a, 0x0b, 0x61, 0x6d, 0x74, 0x5f, 0x69, - 0x6e, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x09, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x61, 0x6d, - 0x74, 0x49, 0x6e, 0x4d, 0x73, 0x61, 0x74, 0x12, 0x20, 0x0a, 0x0c, 0x61, 0x6d, 0x74, 0x5f, 0x6f, - 0x75, 0x74, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x61, - 0x6d, 0x74, 0x4f, 0x75, 0x74, 0x4d, 0x73, 0x61, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x74, 0x69, 0x6d, - 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x5f, 0x6e, 0x73, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x04, 0x52, - 0x0b, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x4e, 0x73, 0x12, 0x22, 0x0a, 0x0d, - 0x70, 0x65, 0x65, 0x72, 0x5f, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x5f, 0x69, 0x6e, 0x18, 0x0c, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x0b, 0x70, 0x65, 0x65, 0x72, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x49, 0x6e, - 0x12, 0x24, 0x0a, 0x0e, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x5f, 0x6f, - 0x75, 0x74, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x70, 0x65, 0x65, 0x72, 0x41, 0x6c, - 0x69, 0x61, 0x73, 0x4f, 0x75, 0x74, 0x22, 0x8c, 0x01, 0x0a, 0x19, 0x46, 0x6f, 0x72, 0x77, 0x61, - 0x72, 0x64, 0x69, 0x6e, 0x67, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x43, 0x0a, 0x11, 0x66, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x69, - 0x6e, 0x67, 0x5f, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x16, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x69, - 0x6e, 0x67, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x10, 0x66, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, - 0x69, 0x6e, 0x67, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x2a, 0x0a, 0x11, 0x6c, 0x61, 0x73, - 0x74, 0x5f, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0f, 0x6c, 0x61, 0x73, 0x74, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, - 0x49, 0x6e, 0x64, 0x65, 0x78, 0x22, 0x50, 0x0a, 0x1a, 0x45, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x43, - 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x12, 0x32, 0x0a, 0x0a, 0x63, 0x68, 0x61, 0x6e, 0x5f, 0x70, 0x6f, 0x69, 0x6e, + 0x12, 0x62, 0x0a, 0x18, 0x66, 0x69, 0x72, 0x73, 0x74, 0x5f, 0x68, 0x6f, 0x70, 0x5f, 0x63, 0x75, + 0x73, 0x74, 0x6f, 0x6d, 0x5f, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x18, 0x11, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x61, 0x79, 0x6d, 0x65, + 0x6e, 0x74, 0x2e, 0x46, 0x69, 0x72, 0x73, 0x74, 0x48, 0x6f, 0x70, 0x43, 0x75, 0x73, 0x74, 0x6f, + 0x6d, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x15, 0x66, + 0x69, 0x72, 0x73, 0x74, 0x48, 0x6f, 0x70, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x52, 0x65, 0x63, + 0x6f, 0x72, 0x64, 0x73, 0x1a, 0x48, 0x0a, 0x1a, 0x46, 0x69, 0x72, 0x73, 0x74, 0x48, 0x6f, 0x70, + 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x45, 0x6e, 0x74, + 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, + 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0c, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x59, + 0x0a, 0x0d, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, + 0x0f, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x1a, 0x02, 0x08, 0x01, + 0x12, 0x0d, 0x0a, 0x09, 0x49, 0x4e, 0x5f, 0x46, 0x4c, 0x49, 0x47, 0x48, 0x54, 0x10, 0x01, 0x12, + 0x0d, 0x0a, 0x09, 0x53, 0x55, 0x43, 0x43, 0x45, 0x45, 0x44, 0x45, 0x44, 0x10, 0x02, 0x12, 0x0a, + 0x0a, 0x06, 0x46, 0x41, 0x49, 0x4c, 0x45, 0x44, 0x10, 0x03, 0x12, 0x0d, 0x0a, 0x09, 0x49, 0x4e, + 0x49, 0x54, 0x49, 0x41, 0x54, 0x45, 0x44, 0x10, 0x04, 0x4a, 0x04, 0x08, 0x04, 0x10, 0x05, 0x22, + 0xd5, 0x02, 0x0a, 0x0b, 0x48, 0x54, 0x4c, 0x43, 0x41, 0x74, 0x74, 0x65, 0x6d, 0x70, 0x74, 0x12, + 0x1d, 0x0a, 0x0a, 0x61, 0x74, 0x74, 0x65, 0x6d, 0x70, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x07, 0x20, + 0x01, 0x28, 0x04, 0x52, 0x09, 0x61, 0x74, 0x74, 0x65, 0x6d, 0x70, 0x74, 0x49, 0x64, 0x12, 0x35, + 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1d, + 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x48, 0x54, 0x4c, 0x43, 0x41, 0x74, 0x74, 0x65, 0x6d, + 0x70, 0x74, 0x2e, 0x48, 0x54, 0x4c, 0x43, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, + 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x22, 0x0a, 0x05, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x6f, 0x75, + 0x74, 0x65, 0x52, 0x05, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x12, 0x26, 0x0a, 0x0f, 0x61, 0x74, 0x74, + 0x65, 0x6d, 0x70, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x6e, 0x73, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x03, 0x52, 0x0d, 0x61, 0x74, 0x74, 0x65, 0x6d, 0x70, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x4e, + 0x73, 0x12, 0x26, 0x0a, 0x0f, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x5f, 0x74, 0x69, 0x6d, + 0x65, 0x5f, 0x6e, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0d, 0x72, 0x65, 0x73, 0x6f, + 0x6c, 0x76, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x4e, 0x73, 0x12, 0x28, 0x0a, 0x07, 0x66, 0x61, 0x69, + 0x6c, 0x75, 0x72, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x6c, 0x6e, 0x72, + 0x70, 0x63, 0x2e, 0x46, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x52, 0x07, 0x66, 0x61, 0x69, 0x6c, + 0x75, 0x72, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x72, 0x65, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x18, + 0x06, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x70, 0x72, 0x65, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x22, + 0x36, 0x0a, 0x0a, 0x48, 0x54, 0x4c, 0x43, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x0d, 0x0a, + 0x09, 0x49, 0x4e, 0x5f, 0x46, 0x4c, 0x49, 0x47, 0x48, 0x54, 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, + 0x53, 0x55, 0x43, 0x43, 0x45, 0x45, 0x44, 0x45, 0x44, 0x10, 0x01, 0x12, 0x0a, 0x0a, 0x06, 0x46, + 0x41, 0x49, 0x4c, 0x45, 0x44, 0x10, 0x02, 0x22, 0xb4, 0x02, 0x0a, 0x13, 0x4c, 0x69, 0x73, 0x74, + 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, + 0x2d, 0x0a, 0x12, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x5f, 0x69, 0x6e, 0x63, 0x6f, 0x6d, + 0x70, 0x6c, 0x65, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x11, 0x69, 0x6e, 0x63, + 0x6c, 0x75, 0x64, 0x65, 0x49, 0x6e, 0x63, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x12, 0x21, + 0x0a, 0x0c, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x5f, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x4f, 0x66, 0x66, 0x73, 0x65, + 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x6d, 0x61, 0x78, 0x5f, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, + 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x6d, 0x61, 0x78, 0x50, 0x61, 0x79, 0x6d, + 0x65, 0x6e, 0x74, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x76, 0x65, 0x72, 0x73, 0x65, 0x64, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x72, 0x65, 0x76, 0x65, 0x72, 0x73, 0x65, 0x64, + 0x12, 0x30, 0x0a, 0x14, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, + 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x12, + 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x54, 0x6f, 0x74, 0x61, 0x6c, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, + 0x74, 0x73, 0x12, 0x2e, 0x0a, 0x13, 0x63, 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x64, + 0x61, 0x74, 0x65, 0x5f, 0x73, 0x74, 0x61, 0x72, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x04, 0x52, + 0x11, 0x63, 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x61, 0x74, 0x65, 0x53, 0x74, 0x61, + 0x72, 0x74, 0x12, 0x2a, 0x0a, 0x11, 0x63, 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x64, + 0x61, 0x74, 0x65, 0x5f, 0x65, 0x6e, 0x64, 0x18, 0x07, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0f, 0x63, + 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x61, 0x74, 0x65, 0x45, 0x6e, 0x64, 0x22, 0xca, + 0x01, 0x0a, 0x14, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2a, 0x0a, 0x08, 0x70, 0x61, 0x79, 0x6d, 0x65, + 0x6e, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x6c, 0x6e, 0x72, 0x70, + 0x63, 0x2e, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x08, 0x70, 0x61, 0x79, 0x6d, 0x65, + 0x6e, 0x74, 0x73, 0x12, 0x2c, 0x0a, 0x12, 0x66, 0x69, 0x72, 0x73, 0x74, 0x5f, 0x69, 0x6e, 0x64, + 0x65, 0x78, 0x5f, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, + 0x10, 0x66, 0x69, 0x72, 0x73, 0x74, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x4f, 0x66, 0x66, 0x73, 0x65, + 0x74, 0x12, 0x2a, 0x0a, 0x11, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x5f, + 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0f, 0x6c, 0x61, + 0x73, 0x74, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x12, 0x2c, 0x0a, + 0x12, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x6e, 0x75, 0x6d, 0x5f, 0x70, 0x61, 0x79, 0x6d, 0x65, + 0x6e, 0x74, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x10, 0x74, 0x6f, 0x74, 0x61, 0x6c, + 0x4e, 0x75, 0x6d, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x22, 0x65, 0x0a, 0x14, 0x44, + 0x65, 0x6c, 0x65, 0x74, 0x65, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x68, + 0x61, 0x73, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x70, 0x61, 0x79, 0x6d, 0x65, + 0x6e, 0x74, 0x48, 0x61, 0x73, 0x68, 0x12, 0x2a, 0x0a, 0x11, 0x66, 0x61, 0x69, 0x6c, 0x65, 0x64, + 0x5f, 0x68, 0x74, 0x6c, 0x63, 0x73, 0x5f, 0x6f, 0x6e, 0x6c, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x08, 0x52, 0x0f, 0x66, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x48, 0x74, 0x6c, 0x63, 0x73, 0x4f, 0x6e, + 0x6c, 0x79, 0x22, 0x9b, 0x01, 0x0a, 0x18, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x41, 0x6c, 0x6c, + 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, + 0x30, 0x0a, 0x14, 0x66, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x5f, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, + 0x74, 0x73, 0x5f, 0x6f, 0x6e, 0x6c, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x12, 0x66, + 0x61, 0x69, 0x6c, 0x65, 0x64, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x4f, 0x6e, 0x6c, + 0x79, 0x12, 0x2a, 0x0a, 0x11, 0x66, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x5f, 0x68, 0x74, 0x6c, 0x63, + 0x73, 0x5f, 0x6f, 0x6e, 0x6c, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, 0x66, 0x61, + 0x69, 0x6c, 0x65, 0x64, 0x48, 0x74, 0x6c, 0x63, 0x73, 0x4f, 0x6e, 0x6c, 0x79, 0x12, 0x21, 0x0a, + 0x0c, 0x61, 0x6c, 0x6c, 0x5f, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x08, 0x52, 0x0b, 0x61, 0x6c, 0x6c, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x73, + 0x22, 0x17, 0x0a, 0x15, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, + 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x0a, 0x19, 0x44, 0x65, 0x6c, + 0x65, 0x74, 0x65, 0x41, 0x6c, 0x6c, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xbf, 0x01, 0x0a, 0x15, 0x41, 0x62, 0x61, 0x6e, 0x64, + 0x6f, 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x12, 0x38, 0x0a, 0x0d, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x5f, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, - 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x09, 0x63, 0x68, - 0x61, 0x6e, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x22, 0x64, 0x0a, 0x0d, 0x43, 0x68, 0x61, 0x6e, 0x6e, - 0x65, 0x6c, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x12, 0x32, 0x0a, 0x0a, 0x63, 0x68, 0x61, 0x6e, - 0x5f, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x6c, - 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x50, 0x6f, 0x69, 0x6e, - 0x74, 0x52, 0x09, 0x63, 0x68, 0x61, 0x6e, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x1f, 0x0a, 0x0b, - 0x63, 0x68, 0x61, 0x6e, 0x5f, 0x62, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x0c, 0x52, 0x0a, 0x63, 0x68, 0x61, 0x6e, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x22, 0x73, 0x0a, - 0x0f, 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x43, 0x68, 0x61, 0x6e, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, - 0x12, 0x34, 0x0a, 0x0b, 0x63, 0x68, 0x61, 0x6e, 0x5f, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x18, - 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, - 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x0a, 0x63, 0x68, 0x61, 0x6e, - 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x12, 0x2a, 0x0a, 0x11, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x5f, - 0x63, 0x68, 0x61, 0x6e, 0x5f, 0x62, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x0c, 0x52, 0x0f, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x43, 0x68, 0x61, 0x6e, 0x42, 0x61, 0x63, 0x6b, - 0x75, 0x70, 0x22, 0x19, 0x0a, 0x17, 0x43, 0x68, 0x61, 0x6e, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, - 0x45, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x9f, 0x01, - 0x0a, 0x12, 0x43, 0x68, 0x61, 0x6e, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x53, 0x6e, 0x61, 0x70, - 0x73, 0x68, 0x6f, 0x74, 0x12, 0x45, 0x0a, 0x13, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x5f, 0x63, - 0x68, 0x61, 0x6e, 0x5f, 0x62, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x15, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, - 0x6c, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x73, 0x52, 0x11, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, - 0x43, 0x68, 0x61, 0x6e, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x73, 0x12, 0x42, 0x0a, 0x11, 0x6d, - 0x75, 0x6c, 0x74, 0x69, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x5f, 0x62, 0x61, 0x63, 0x6b, 0x75, 0x70, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4d, - 0x75, 0x6c, 0x74, 0x69, 0x43, 0x68, 0x61, 0x6e, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x52, 0x0f, - 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x43, 0x68, 0x61, 0x6e, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x22, - 0x49, 0x0a, 0x0e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, - 0x73, 0x12, 0x37, 0x0a, 0x0c, 0x63, 0x68, 0x61, 0x6e, 0x5f, 0x62, 0x61, 0x63, 0x6b, 0x75, 0x70, - 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, - 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x52, 0x0b, 0x63, - 0x68, 0x61, 0x6e, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x73, 0x22, 0x8e, 0x01, 0x0a, 0x18, 0x52, - 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x3a, 0x0a, 0x0c, 0x63, 0x68, 0x61, 0x6e, 0x5f, - 0x62, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, - 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x42, 0x61, 0x63, - 0x6b, 0x75, 0x70, 0x73, 0x48, 0x00, 0x52, 0x0b, 0x63, 0x68, 0x61, 0x6e, 0x42, 0x61, 0x63, 0x6b, - 0x75, 0x70, 0x73, 0x12, 0x2c, 0x0a, 0x11, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x5f, 0x63, 0x68, 0x61, - 0x6e, 0x5f, 0x62, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, - 0x52, 0x0f, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x43, 0x68, 0x61, 0x6e, 0x42, 0x61, 0x63, 0x6b, 0x75, - 0x70, 0x42, 0x08, 0x0a, 0x06, 0x62, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x22, 0x17, 0x0a, 0x15, 0x52, - 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x0a, 0x19, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x42, - 0x61, 0x63, 0x6b, 0x75, 0x70, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, - 0x6e, 0x22, 0x1a, 0x0a, 0x18, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x43, 0x68, 0x61, 0x6e, 0x42, - 0x61, 0x63, 0x6b, 0x75, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x44, 0x0a, - 0x12, 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, - 0x69, 0x6f, 0x6e, 0x12, 0x16, 0x0a, 0x06, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x06, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x12, 0x16, 0x0a, 0x06, 0x61, - 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x61, 0x63, 0x74, - 0x69, 0x6f, 0x6e, 0x22, 0xb0, 0x01, 0x0a, 0x13, 0x42, 0x61, 0x6b, 0x65, 0x4d, 0x61, 0x63, 0x61, - 0x72, 0x6f, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x3b, 0x0a, 0x0b, 0x70, - 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, - 0x6e, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x0b, 0x70, 0x65, 0x72, - 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x1e, 0x0a, 0x0b, 0x72, 0x6f, 0x6f, 0x74, - 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x72, - 0x6f, 0x6f, 0x74, 0x4b, 0x65, 0x79, 0x49, 0x64, 0x12, 0x3c, 0x0a, 0x1a, 0x61, 0x6c, 0x6c, 0x6f, - 0x77, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5f, 0x70, 0x65, 0x72, 0x6d, 0x69, - 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x18, 0x61, 0x6c, - 0x6c, 0x6f, 0x77, 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x50, 0x65, 0x72, 0x6d, 0x69, - 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x32, 0x0a, 0x14, 0x42, 0x61, 0x6b, 0x65, 0x4d, 0x61, - 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1a, - 0x0a, 0x08, 0x6d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x08, 0x6d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x22, 0x18, 0x0a, 0x16, 0x4c, 0x69, - 0x73, 0x74, 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x49, 0x44, 0x73, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x22, 0x3b, 0x0a, 0x17, 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x61, 0x63, 0x61, - 0x72, 0x6f, 0x6f, 0x6e, 0x49, 0x44, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, - 0x20, 0x0a, 0x0c, 0x72, 0x6f, 0x6f, 0x74, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x69, 0x64, 0x73, 0x18, - 0x01, 0x20, 0x03, 0x28, 0x04, 0x52, 0x0a, 0x72, 0x6f, 0x6f, 0x74, 0x4b, 0x65, 0x79, 0x49, 0x64, - 0x73, 0x22, 0x39, 0x0a, 0x17, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4d, 0x61, 0x63, 0x61, 0x72, - 0x6f, 0x6f, 0x6e, 0x49, 0x44, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1e, 0x0a, 0x0b, - 0x72, 0x6f, 0x6f, 0x74, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x04, 0x52, 0x09, 0x72, 0x6f, 0x6f, 0x74, 0x4b, 0x65, 0x79, 0x49, 0x64, 0x22, 0x34, 0x0a, 0x18, - 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x49, 0x44, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x64, 0x65, 0x6c, 0x65, - 0x74, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x64, 0x65, 0x6c, 0x65, 0x74, - 0x65, 0x64, 0x22, 0x55, 0x0a, 0x16, 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x50, 0x65, - 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x3b, 0x0a, 0x0b, - 0x70, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, - 0x0b, 0x32, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, - 0x6f, 0x6e, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x0b, 0x70, 0x65, - 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x18, 0x0a, 0x16, 0x4c, 0x69, 0x73, - 0x74, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x22, 0xe4, 0x01, 0x0a, 0x17, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x65, 0x72, 0x6d, - 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, - 0x64, 0x0a, 0x12, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x5f, 0x70, 0x65, 0x72, 0x6d, 0x69, 0x73, - 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x35, 0x2e, 0x6c, 0x6e, - 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, - 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x4d, 0x65, 0x74, 0x68, - 0x6f, 0x64, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x45, 0x6e, 0x74, - 0x72, 0x79, 0x52, 0x11, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, - 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x1a, 0x63, 0x0a, 0x16, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x50, - 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, - 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, - 0x79, 0x12, 0x33, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x1d, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, - 0x6e, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x4c, 0x69, 0x73, 0x74, 0x52, - 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xcc, 0x08, 0x0a, 0x07, 0x46, - 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x12, 0x2e, 0x0a, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x61, 0x69, - 0x6c, 0x75, 0x72, 0x65, 0x2e, 0x46, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x43, 0x6f, 0x64, 0x65, - 0x52, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x12, 0x3b, 0x0a, 0x0e, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, - 0x6c, 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, - 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x55, 0x70, - 0x64, 0x61, 0x74, 0x65, 0x52, 0x0d, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x55, 0x70, 0x64, - 0x61, 0x74, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x68, 0x74, 0x6c, 0x63, 0x5f, 0x6d, 0x73, 0x61, 0x74, - 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x68, 0x74, 0x6c, 0x63, 0x4d, 0x73, 0x61, 0x74, - 0x12, 0x22, 0x0a, 0x0d, 0x6f, 0x6e, 0x69, 0x6f, 0x6e, 0x5f, 0x73, 0x68, 0x61, 0x5f, 0x32, 0x35, - 0x36, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x6f, 0x6e, 0x69, 0x6f, 0x6e, 0x53, 0x68, - 0x61, 0x32, 0x35, 0x36, 0x12, 0x1f, 0x0a, 0x0b, 0x63, 0x6c, 0x74, 0x76, 0x5f, 0x65, 0x78, 0x70, - 0x69, 0x72, 0x79, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x63, 0x6c, 0x74, 0x76, 0x45, - 0x78, 0x70, 0x69, 0x72, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x18, 0x07, - 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x12, 0x30, 0x0a, 0x14, 0x66, - 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x69, 0x6e, - 0x64, 0x65, 0x78, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x12, 0x66, 0x61, 0x69, 0x6c, 0x75, - 0x72, 0x65, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x16, 0x0a, - 0x06, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x68, - 0x65, 0x69, 0x67, 0x68, 0x74, 0x22, 0x8b, 0x06, 0x0a, 0x0b, 0x46, 0x61, 0x69, 0x6c, 0x75, 0x72, - 0x65, 0x43, 0x6f, 0x64, 0x65, 0x12, 0x0c, 0x0a, 0x08, 0x52, 0x45, 0x53, 0x45, 0x52, 0x56, 0x45, - 0x44, 0x10, 0x00, 0x12, 0x28, 0x0a, 0x24, 0x49, 0x4e, 0x43, 0x4f, 0x52, 0x52, 0x45, 0x43, 0x54, - 0x5f, 0x4f, 0x52, 0x5f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x5f, 0x50, 0x41, 0x59, 0x4d, - 0x45, 0x4e, 0x54, 0x5f, 0x44, 0x45, 0x54, 0x41, 0x49, 0x4c, 0x53, 0x10, 0x01, 0x12, 0x1c, 0x0a, - 0x18, 0x49, 0x4e, 0x43, 0x4f, 0x52, 0x52, 0x45, 0x43, 0x54, 0x5f, 0x50, 0x41, 0x59, 0x4d, 0x45, - 0x4e, 0x54, 0x5f, 0x41, 0x4d, 0x4f, 0x55, 0x4e, 0x54, 0x10, 0x02, 0x12, 0x1f, 0x0a, 0x1b, 0x46, - 0x49, 0x4e, 0x41, 0x4c, 0x5f, 0x49, 0x4e, 0x43, 0x4f, 0x52, 0x52, 0x45, 0x43, 0x54, 0x5f, 0x43, - 0x4c, 0x54, 0x56, 0x5f, 0x45, 0x58, 0x50, 0x49, 0x52, 0x59, 0x10, 0x03, 0x12, 0x1f, 0x0a, 0x1b, - 0x46, 0x49, 0x4e, 0x41, 0x4c, 0x5f, 0x49, 0x4e, 0x43, 0x4f, 0x52, 0x52, 0x45, 0x43, 0x54, 0x5f, - 0x48, 0x54, 0x4c, 0x43, 0x5f, 0x41, 0x4d, 0x4f, 0x55, 0x4e, 0x54, 0x10, 0x04, 0x12, 0x19, 0x0a, - 0x15, 0x46, 0x49, 0x4e, 0x41, 0x4c, 0x5f, 0x45, 0x58, 0x50, 0x49, 0x52, 0x59, 0x5f, 0x54, 0x4f, - 0x4f, 0x5f, 0x53, 0x4f, 0x4f, 0x4e, 0x10, 0x05, 0x12, 0x11, 0x0a, 0x0d, 0x49, 0x4e, 0x56, 0x41, - 0x4c, 0x49, 0x44, 0x5f, 0x52, 0x45, 0x41, 0x4c, 0x4d, 0x10, 0x06, 0x12, 0x13, 0x0a, 0x0f, 0x45, - 0x58, 0x50, 0x49, 0x52, 0x59, 0x5f, 0x54, 0x4f, 0x4f, 0x5f, 0x53, 0x4f, 0x4f, 0x4e, 0x10, 0x07, - 0x12, 0x19, 0x0a, 0x15, 0x49, 0x4e, 0x56, 0x41, 0x4c, 0x49, 0x44, 0x5f, 0x4f, 0x4e, 0x49, 0x4f, - 0x4e, 0x5f, 0x56, 0x45, 0x52, 0x53, 0x49, 0x4f, 0x4e, 0x10, 0x08, 0x12, 0x16, 0x0a, 0x12, 0x49, - 0x4e, 0x56, 0x41, 0x4c, 0x49, 0x44, 0x5f, 0x4f, 0x4e, 0x49, 0x4f, 0x4e, 0x5f, 0x48, 0x4d, 0x41, - 0x43, 0x10, 0x09, 0x12, 0x15, 0x0a, 0x11, 0x49, 0x4e, 0x56, 0x41, 0x4c, 0x49, 0x44, 0x5f, 0x4f, - 0x4e, 0x49, 0x4f, 0x4e, 0x5f, 0x4b, 0x45, 0x59, 0x10, 0x0a, 0x12, 0x18, 0x0a, 0x14, 0x41, 0x4d, - 0x4f, 0x55, 0x4e, 0x54, 0x5f, 0x42, 0x45, 0x4c, 0x4f, 0x57, 0x5f, 0x4d, 0x49, 0x4e, 0x49, 0x4d, - 0x55, 0x4d, 0x10, 0x0b, 0x12, 0x14, 0x0a, 0x10, 0x46, 0x45, 0x45, 0x5f, 0x49, 0x4e, 0x53, 0x55, - 0x46, 0x46, 0x49, 0x43, 0x49, 0x45, 0x4e, 0x54, 0x10, 0x0c, 0x12, 0x19, 0x0a, 0x15, 0x49, 0x4e, - 0x43, 0x4f, 0x52, 0x52, 0x45, 0x43, 0x54, 0x5f, 0x43, 0x4c, 0x54, 0x56, 0x5f, 0x45, 0x58, 0x50, - 0x49, 0x52, 0x59, 0x10, 0x0d, 0x12, 0x14, 0x0a, 0x10, 0x43, 0x48, 0x41, 0x4e, 0x4e, 0x45, 0x4c, - 0x5f, 0x44, 0x49, 0x53, 0x41, 0x42, 0x4c, 0x45, 0x44, 0x10, 0x0e, 0x12, 0x1d, 0x0a, 0x19, 0x54, - 0x45, 0x4d, 0x50, 0x4f, 0x52, 0x41, 0x52, 0x59, 0x5f, 0x43, 0x48, 0x41, 0x4e, 0x4e, 0x45, 0x4c, - 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x10, 0x0f, 0x12, 0x21, 0x0a, 0x1d, 0x52, 0x45, - 0x51, 0x55, 0x49, 0x52, 0x45, 0x44, 0x5f, 0x4e, 0x4f, 0x44, 0x45, 0x5f, 0x46, 0x45, 0x41, 0x54, - 0x55, 0x52, 0x45, 0x5f, 0x4d, 0x49, 0x53, 0x53, 0x49, 0x4e, 0x47, 0x10, 0x10, 0x12, 0x24, 0x0a, - 0x20, 0x52, 0x45, 0x51, 0x55, 0x49, 0x52, 0x45, 0x44, 0x5f, 0x43, 0x48, 0x41, 0x4e, 0x4e, 0x45, - 0x4c, 0x5f, 0x46, 0x45, 0x41, 0x54, 0x55, 0x52, 0x45, 0x5f, 0x4d, 0x49, 0x53, 0x53, 0x49, 0x4e, - 0x47, 0x10, 0x11, 0x12, 0x15, 0x0a, 0x11, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x5f, 0x4e, - 0x45, 0x58, 0x54, 0x5f, 0x50, 0x45, 0x45, 0x52, 0x10, 0x12, 0x12, 0x1a, 0x0a, 0x16, 0x54, 0x45, - 0x4d, 0x50, 0x4f, 0x52, 0x41, 0x52, 0x59, 0x5f, 0x4e, 0x4f, 0x44, 0x45, 0x5f, 0x46, 0x41, 0x49, - 0x4c, 0x55, 0x52, 0x45, 0x10, 0x13, 0x12, 0x1a, 0x0a, 0x16, 0x50, 0x45, 0x52, 0x4d, 0x41, 0x4e, - 0x45, 0x4e, 0x54, 0x5f, 0x4e, 0x4f, 0x44, 0x45, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, - 0x10, 0x14, 0x12, 0x1d, 0x0a, 0x19, 0x50, 0x45, 0x52, 0x4d, 0x41, 0x4e, 0x45, 0x4e, 0x54, 0x5f, - 0x43, 0x48, 0x41, 0x4e, 0x4e, 0x45, 0x4c, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x10, - 0x15, 0x12, 0x12, 0x0a, 0x0e, 0x45, 0x58, 0x50, 0x49, 0x52, 0x59, 0x5f, 0x54, 0x4f, 0x4f, 0x5f, - 0x46, 0x41, 0x52, 0x10, 0x16, 0x12, 0x0f, 0x0a, 0x0b, 0x4d, 0x50, 0x50, 0x5f, 0x54, 0x49, 0x4d, - 0x45, 0x4f, 0x55, 0x54, 0x10, 0x17, 0x12, 0x19, 0x0a, 0x15, 0x49, 0x4e, 0x56, 0x41, 0x4c, 0x49, - 0x44, 0x5f, 0x4f, 0x4e, 0x49, 0x4f, 0x4e, 0x5f, 0x50, 0x41, 0x59, 0x4c, 0x4f, 0x41, 0x44, 0x10, - 0x18, 0x12, 0x1a, 0x0a, 0x16, 0x49, 0x4e, 0x56, 0x41, 0x4c, 0x49, 0x44, 0x5f, 0x4f, 0x4e, 0x49, - 0x4f, 0x4e, 0x5f, 0x42, 0x4c, 0x49, 0x4e, 0x44, 0x49, 0x4e, 0x47, 0x10, 0x19, 0x12, 0x15, 0x0a, - 0x10, 0x49, 0x4e, 0x54, 0x45, 0x52, 0x4e, 0x41, 0x4c, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, - 0x45, 0x10, 0xe5, 0x07, 0x12, 0x14, 0x0a, 0x0f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x5f, - 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x10, 0xe6, 0x07, 0x12, 0x17, 0x0a, 0x12, 0x55, 0x4e, - 0x52, 0x45, 0x41, 0x44, 0x41, 0x42, 0x4c, 0x45, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, - 0x10, 0xe7, 0x07, 0x4a, 0x04, 0x08, 0x02, 0x10, 0x03, 0x22, 0xb3, 0x03, 0x0a, 0x0d, 0x43, 0x68, - 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x73, - 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, - 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x68, 0x61, - 0x69, 0x6e, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x63, - 0x68, 0x61, 0x69, 0x6e, 0x48, 0x61, 0x73, 0x68, 0x12, 0x1b, 0x0a, 0x07, 0x63, 0x68, 0x61, 0x6e, - 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x42, 0x02, 0x30, 0x01, 0x52, 0x06, 0x63, - 0x68, 0x61, 0x6e, 0x49, 0x64, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, - 0x6d, 0x70, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, - 0x61, 0x6d, 0x70, 0x12, 0x23, 0x0a, 0x0d, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x66, - 0x6c, 0x61, 0x67, 0x73, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0c, 0x6d, 0x65, 0x73, 0x73, - 0x61, 0x67, 0x65, 0x46, 0x6c, 0x61, 0x67, 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x68, 0x61, 0x6e, - 0x6e, 0x65, 0x6c, 0x5f, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, - 0x0c, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x46, 0x6c, 0x61, 0x67, 0x73, 0x12, 0x26, 0x0a, - 0x0f, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x64, 0x65, 0x6c, 0x74, 0x61, - 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0d, 0x74, 0x69, 0x6d, 0x65, 0x4c, 0x6f, 0x63, 0x6b, - 0x44, 0x65, 0x6c, 0x74, 0x61, 0x12, 0x2a, 0x0a, 0x11, 0x68, 0x74, 0x6c, 0x63, 0x5f, 0x6d, 0x69, - 0x6e, 0x69, 0x6d, 0x75, 0x6d, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x04, - 0x52, 0x0f, 0x68, 0x74, 0x6c, 0x63, 0x4d, 0x69, 0x6e, 0x69, 0x6d, 0x75, 0x6d, 0x4d, 0x73, 0x61, - 0x74, 0x12, 0x19, 0x0a, 0x08, 0x62, 0x61, 0x73, 0x65, 0x5f, 0x66, 0x65, 0x65, 0x18, 0x08, 0x20, - 0x01, 0x28, 0x0d, 0x52, 0x07, 0x62, 0x61, 0x73, 0x65, 0x46, 0x65, 0x65, 0x12, 0x19, 0x0a, 0x08, - 0x66, 0x65, 0x65, 0x5f, 0x72, 0x61, 0x74, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, - 0x66, 0x65, 0x65, 0x52, 0x61, 0x74, 0x65, 0x12, 0x2a, 0x0a, 0x11, 0x68, 0x74, 0x6c, 0x63, 0x5f, - 0x6d, 0x61, 0x78, 0x69, 0x6d, 0x75, 0x6d, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x0b, 0x20, 0x01, - 0x28, 0x04, 0x52, 0x0f, 0x68, 0x74, 0x6c, 0x63, 0x4d, 0x61, 0x78, 0x69, 0x6d, 0x75, 0x6d, 0x4d, - 0x73, 0x61, 0x74, 0x12, 0x2a, 0x0a, 0x11, 0x65, 0x78, 0x74, 0x72, 0x61, 0x5f, 0x6f, 0x70, 0x61, - 0x71, 0x75, 0x65, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0f, - 0x65, 0x78, 0x74, 0x72, 0x61, 0x4f, 0x70, 0x61, 0x71, 0x75, 0x65, 0x44, 0x61, 0x74, 0x61, 0x22, - 0x5d, 0x0a, 0x0a, 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x14, 0x0a, - 0x05, 0x6e, 0x6f, 0x6e, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x6e, 0x6f, - 0x6e, 0x63, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x49, 0x64, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x49, - 0x64, 0x12, 0x1b, 0x0a, 0x03, 0x6f, 0x70, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x09, - 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4f, 0x70, 0x52, 0x03, 0x6f, 0x70, 0x73, 0x22, 0x36, - 0x0a, 0x02, 0x4f, 0x70, 0x12, 0x16, 0x0a, 0x06, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x12, 0x18, 0x0a, 0x07, - 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x61, - 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x8e, 0x01, 0x0a, 0x13, 0x43, 0x68, 0x65, 0x63, 0x6b, - 0x4d, 0x61, 0x63, 0x50, 0x65, 0x72, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, - 0x0a, 0x08, 0x6d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, - 0x52, 0x08, 0x6d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x12, 0x3b, 0x0a, 0x0b, 0x70, 0x65, - 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x0c, 0x63, 0x68, + 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x39, 0x0a, 0x19, 0x70, 0x65, + 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x66, 0x75, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x73, 0x68, + 0x69, 0x6d, 0x5f, 0x6f, 0x6e, 0x6c, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x16, 0x70, + 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x46, 0x75, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x53, 0x68, 0x69, + 0x6d, 0x4f, 0x6e, 0x6c, 0x79, 0x12, 0x31, 0x0a, 0x16, 0x69, 0x5f, 0x6b, 0x6e, 0x6f, 0x77, 0x5f, + 0x77, 0x68, 0x61, 0x74, 0x5f, 0x69, 0x5f, 0x61, 0x6d, 0x5f, 0x64, 0x6f, 0x69, 0x6e, 0x67, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x11, 0x69, 0x4b, 0x6e, 0x6f, 0x77, 0x57, 0x68, 0x61, 0x74, + 0x49, 0x41, 0x6d, 0x44, 0x6f, 0x69, 0x6e, 0x67, 0x22, 0x18, 0x0a, 0x16, 0x41, 0x62, 0x61, 0x6e, + 0x64, 0x6f, 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x22, 0x46, 0x0a, 0x11, 0x44, 0x65, 0x62, 0x75, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x68, 0x6f, 0x77, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x04, 0x73, 0x68, 0x6f, 0x77, 0x12, 0x1d, 0x0a, 0x0a, 0x6c, + 0x65, 0x76, 0x65, 0x6c, 0x5f, 0x73, 0x70, 0x65, 0x63, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x09, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x53, 0x70, 0x65, 0x63, 0x22, 0x35, 0x0a, 0x12, 0x44, 0x65, + 0x62, 0x75, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x75, 0x62, 0x5f, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x73, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x75, 0x62, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, + 0x73, 0x22, 0x27, 0x0a, 0x0c, 0x50, 0x61, 0x79, 0x52, 0x65, 0x71, 0x53, 0x74, 0x72, 0x69, 0x6e, + 0x67, 0x12, 0x17, 0x0a, 0x07, 0x70, 0x61, 0x79, 0x5f, 0x72, 0x65, 0x71, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x06, 0x70, 0x61, 0x79, 0x52, 0x65, 0x71, 0x22, 0xf0, 0x04, 0x0a, 0x06, 0x50, + 0x61, 0x79, 0x52, 0x65, 0x71, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x74, + 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x61, 0x79, 0x6d, 0x65, + 0x6e, 0x74, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x70, + 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x48, 0x61, 0x73, 0x68, 0x12, 0x21, 0x0a, 0x0c, 0x6e, 0x75, + 0x6d, 0x5f, 0x73, 0x61, 0x74, 0x6f, 0x73, 0x68, 0x69, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, + 0x52, 0x0b, 0x6e, 0x75, 0x6d, 0x53, 0x61, 0x74, 0x6f, 0x73, 0x68, 0x69, 0x73, 0x12, 0x1c, 0x0a, + 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, + 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x16, 0x0a, 0x06, 0x65, + 0x78, 0x70, 0x69, 0x72, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x65, 0x78, 0x70, + 0x69, 0x72, 0x79, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, + 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, + 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x29, 0x0a, 0x10, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, + 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0f, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x61, 0x73, 0x68, + 0x12, 0x23, 0x0a, 0x0d, 0x66, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x5f, 0x61, 0x64, 0x64, + 0x72, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x66, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, + 0x6b, 0x41, 0x64, 0x64, 0x72, 0x12, 0x1f, 0x0a, 0x0b, 0x63, 0x6c, 0x74, 0x76, 0x5f, 0x65, 0x78, + 0x70, 0x69, 0x72, 0x79, 0x18, 0x09, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x63, 0x6c, 0x74, 0x76, + 0x45, 0x78, 0x70, 0x69, 0x72, 0x79, 0x12, 0x31, 0x0a, 0x0b, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x5f, + 0x68, 0x69, 0x6e, 0x74, 0x73, 0x18, 0x0a, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x6c, 0x6e, + 0x72, 0x70, 0x63, 0x2e, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x48, 0x69, 0x6e, 0x74, 0x52, 0x0a, 0x72, + 0x6f, 0x75, 0x74, 0x65, 0x48, 0x69, 0x6e, 0x74, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x61, 0x79, + 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0c, 0x52, + 0x0b, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x41, 0x64, 0x64, 0x72, 0x12, 0x19, 0x0a, 0x08, + 0x6e, 0x75, 0x6d, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, + 0x6e, 0x75, 0x6d, 0x4d, 0x73, 0x61, 0x74, 0x12, 0x37, 0x0a, 0x08, 0x66, 0x65, 0x61, 0x74, 0x75, + 0x72, 0x65, 0x73, 0x18, 0x0d, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x6c, 0x6e, 0x72, 0x70, + 0x63, 0x2e, 0x50, 0x61, 0x79, 0x52, 0x65, 0x71, 0x2e, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, + 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x08, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, + 0x12, 0x3e, 0x0a, 0x0d, 0x62, 0x6c, 0x69, 0x6e, 0x64, 0x65, 0x64, 0x5f, 0x70, 0x61, 0x74, 0x68, + 0x73, 0x18, 0x0e, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, + 0x42, 0x6c, 0x69, 0x6e, 0x64, 0x65, 0x64, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x50, 0x61, + 0x74, 0x68, 0x52, 0x0c, 0x62, 0x6c, 0x69, 0x6e, 0x64, 0x65, 0x64, 0x50, 0x61, 0x74, 0x68, 0x73, + 0x1a, 0x4b, 0x0a, 0x0d, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, + 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, + 0x6b, 0x65, 0x79, 0x12, 0x24, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x65, 0x61, 0x74, 0x75, + 0x72, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x59, 0x0a, + 0x07, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1f, 0x0a, 0x0b, + 0x69, 0x73, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x08, 0x52, 0x0a, 0x69, 0x73, 0x52, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x12, 0x19, 0x0a, + 0x08, 0x69, 0x73, 0x5f, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, + 0x07, 0x69, 0x73, 0x4b, 0x6e, 0x6f, 0x77, 0x6e, 0x22, 0x12, 0x0a, 0x10, 0x46, 0x65, 0x65, 0x52, + 0x65, 0x70, 0x6f, 0x72, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x95, 0x02, 0x0a, + 0x10, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x46, 0x65, 0x65, 0x52, 0x65, 0x70, 0x6f, 0x72, + 0x74, 0x12, 0x1b, 0x0a, 0x07, 0x63, 0x68, 0x61, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x05, 0x20, 0x01, + 0x28, 0x04, 0x42, 0x02, 0x30, 0x01, 0x52, 0x06, 0x63, 0x68, 0x61, 0x6e, 0x49, 0x64, 0x12, 0x23, + 0x0a, 0x0d, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x5f, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x50, 0x6f, + 0x69, 0x6e, 0x74, 0x12, 0x22, 0x0a, 0x0d, 0x62, 0x61, 0x73, 0x65, 0x5f, 0x66, 0x65, 0x65, 0x5f, + 0x6d, 0x73, 0x61, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b, 0x62, 0x61, 0x73, 0x65, + 0x46, 0x65, 0x65, 0x4d, 0x73, 0x61, 0x74, 0x12, 0x1e, 0x0a, 0x0b, 0x66, 0x65, 0x65, 0x5f, 0x70, + 0x65, 0x72, 0x5f, 0x6d, 0x69, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x66, 0x65, + 0x65, 0x50, 0x65, 0x72, 0x4d, 0x69, 0x6c, 0x12, 0x19, 0x0a, 0x08, 0x66, 0x65, 0x65, 0x5f, 0x72, + 0x61, 0x74, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x01, 0x52, 0x07, 0x66, 0x65, 0x65, 0x52, 0x61, + 0x74, 0x65, 0x12, 0x31, 0x0a, 0x15, 0x69, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x5f, 0x62, 0x61, + 0x73, 0x65, 0x5f, 0x66, 0x65, 0x65, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, + 0x05, 0x52, 0x12, 0x69, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x42, 0x61, 0x73, 0x65, 0x46, 0x65, + 0x65, 0x4d, 0x73, 0x61, 0x74, 0x12, 0x2d, 0x0a, 0x13, 0x69, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, + 0x5f, 0x66, 0x65, 0x65, 0x5f, 0x70, 0x65, 0x72, 0x5f, 0x6d, 0x69, 0x6c, 0x18, 0x07, 0x20, 0x01, + 0x28, 0x05, 0x52, 0x10, 0x69, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x46, 0x65, 0x65, 0x50, 0x65, + 0x72, 0x4d, 0x69, 0x6c, 0x22, 0xb5, 0x01, 0x0a, 0x11, 0x46, 0x65, 0x65, 0x52, 0x65, 0x70, 0x6f, + 0x72, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3a, 0x0a, 0x0c, 0x63, 0x68, + 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x5f, 0x66, 0x65, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x17, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, + 0x46, 0x65, 0x65, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x52, 0x0b, 0x63, 0x68, 0x61, 0x6e, 0x6e, + 0x65, 0x6c, 0x46, 0x65, 0x65, 0x73, 0x12, 0x1e, 0x0a, 0x0b, 0x64, 0x61, 0x79, 0x5f, 0x66, 0x65, + 0x65, 0x5f, 0x73, 0x75, 0x6d, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x64, 0x61, 0x79, + 0x46, 0x65, 0x65, 0x53, 0x75, 0x6d, 0x12, 0x20, 0x0a, 0x0c, 0x77, 0x65, 0x65, 0x6b, 0x5f, 0x66, + 0x65, 0x65, 0x5f, 0x73, 0x75, 0x6d, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x77, 0x65, + 0x65, 0x6b, 0x46, 0x65, 0x65, 0x53, 0x75, 0x6d, 0x12, 0x22, 0x0a, 0x0d, 0x6d, 0x6f, 0x6e, 0x74, + 0x68, 0x5f, 0x66, 0x65, 0x65, 0x5f, 0x73, 0x75, 0x6d, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, + 0x0b, 0x6d, 0x6f, 0x6e, 0x74, 0x68, 0x46, 0x65, 0x65, 0x53, 0x75, 0x6d, 0x22, 0x52, 0x0a, 0x0a, + 0x49, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x46, 0x65, 0x65, 0x12, 0x22, 0x0a, 0x0d, 0x62, 0x61, + 0x73, 0x65, 0x5f, 0x66, 0x65, 0x65, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x05, 0x52, 0x0b, 0x62, 0x61, 0x73, 0x65, 0x46, 0x65, 0x65, 0x4d, 0x73, 0x61, 0x74, 0x12, 0x20, + 0x0a, 0x0c, 0x66, 0x65, 0x65, 0x5f, 0x72, 0x61, 0x74, 0x65, 0x5f, 0x70, 0x70, 0x6d, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x66, 0x65, 0x65, 0x52, 0x61, 0x74, 0x65, 0x50, 0x70, 0x6d, + 0x22, 0xaa, 0x03, 0x0a, 0x13, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x55, 0x70, 0x64, 0x61, 0x74, + 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x06, 0x67, 0x6c, 0x6f, 0x62, + 0x61, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x48, 0x00, 0x52, 0x06, 0x67, 0x6c, 0x6f, 0x62, + 0x61, 0x6c, 0x12, 0x34, 0x0a, 0x0a, 0x63, 0x68, 0x61, 0x6e, 0x5f, 0x70, 0x6f, 0x69, 0x6e, 0x74, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, + 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x09, 0x63, + 0x68, 0x61, 0x6e, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x22, 0x0a, 0x0d, 0x62, 0x61, 0x73, 0x65, + 0x5f, 0x66, 0x65, 0x65, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, + 0x0b, 0x62, 0x61, 0x73, 0x65, 0x46, 0x65, 0x65, 0x4d, 0x73, 0x61, 0x74, 0x12, 0x19, 0x0a, 0x08, + 0x66, 0x65, 0x65, 0x5f, 0x72, 0x61, 0x74, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x01, 0x52, 0x07, + 0x66, 0x65, 0x65, 0x52, 0x61, 0x74, 0x65, 0x12, 0x20, 0x0a, 0x0c, 0x66, 0x65, 0x65, 0x5f, 0x72, + 0x61, 0x74, 0x65, 0x5f, 0x70, 0x70, 0x6d, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x66, + 0x65, 0x65, 0x52, 0x61, 0x74, 0x65, 0x50, 0x70, 0x6d, 0x12, 0x26, 0x0a, 0x0f, 0x74, 0x69, 0x6d, + 0x65, 0x5f, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x18, 0x05, 0x20, 0x01, + 0x28, 0x0d, 0x52, 0x0d, 0x74, 0x69, 0x6d, 0x65, 0x4c, 0x6f, 0x63, 0x6b, 0x44, 0x65, 0x6c, 0x74, + 0x61, 0x12, 0x22, 0x0a, 0x0d, 0x6d, 0x61, 0x78, 0x5f, 0x68, 0x74, 0x6c, 0x63, 0x5f, 0x6d, 0x73, + 0x61, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x6d, 0x61, 0x78, 0x48, 0x74, 0x6c, + 0x63, 0x4d, 0x73, 0x61, 0x74, 0x12, 0x22, 0x0a, 0x0d, 0x6d, 0x69, 0x6e, 0x5f, 0x68, 0x74, 0x6c, + 0x63, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x6d, 0x69, + 0x6e, 0x48, 0x74, 0x6c, 0x63, 0x4d, 0x73, 0x61, 0x74, 0x12, 0x35, 0x0a, 0x17, 0x6d, 0x69, 0x6e, + 0x5f, 0x68, 0x74, 0x6c, 0x63, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x5f, 0x73, 0x70, 0x65, 0x63, 0x69, + 0x66, 0x69, 0x65, 0x64, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, 0x14, 0x6d, 0x69, 0x6e, 0x48, + 0x74, 0x6c, 0x63, 0x4d, 0x73, 0x61, 0x74, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x65, 0x64, + 0x12, 0x32, 0x0a, 0x0b, 0x69, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x5f, 0x66, 0x65, 0x65, 0x18, + 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x49, 0x6e, + 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x46, 0x65, 0x65, 0x52, 0x0a, 0x69, 0x6e, 0x62, 0x6f, 0x75, 0x6e, + 0x64, 0x46, 0x65, 0x65, 0x42, 0x07, 0x0a, 0x05, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x22, 0x8c, 0x01, + 0x0a, 0x0c, 0x46, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x2b, + 0x0a, 0x08, 0x6f, 0x75, 0x74, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x0f, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4f, 0x75, 0x74, 0x50, 0x6f, 0x69, 0x6e, + 0x74, 0x52, 0x08, 0x6f, 0x75, 0x74, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x2c, 0x0a, 0x06, 0x72, + 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x14, 0x2e, 0x6c, 0x6e, + 0x72, 0x70, 0x63, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x46, 0x61, 0x69, 0x6c, 0x75, 0x72, + 0x65, 0x52, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x12, 0x21, 0x0a, 0x0c, 0x75, 0x70, 0x64, + 0x61, 0x74, 0x65, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0b, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x22, 0x52, 0x0a, 0x14, + 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3a, 0x0a, 0x0e, 0x66, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x5f, 0x75, + 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x6c, + 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x55, 0x70, 0x64, 0x61, 0x74, + 0x65, 0x52, 0x0d, 0x66, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, + 0x22, 0xc9, 0x01, 0x0a, 0x18, 0x46, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x48, + 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, + 0x0a, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x04, 0x52, 0x09, 0x73, 0x74, 0x61, 0x72, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x19, 0x0a, 0x08, + 0x65, 0x6e, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, + 0x65, 0x6e, 0x64, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x69, 0x6e, 0x64, 0x65, 0x78, + 0x5f, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x69, + 0x6e, 0x64, 0x65, 0x78, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x12, 0x24, 0x0a, 0x0e, 0x6e, 0x75, + 0x6d, 0x5f, 0x6d, 0x61, 0x78, 0x5f, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x0d, 0x52, 0x0c, 0x6e, 0x75, 0x6d, 0x4d, 0x61, 0x78, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, + 0x12, 0x2a, 0x0a, 0x11, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x5f, 0x6c, + 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, 0x70, 0x65, 0x65, + 0x72, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x22, 0x85, 0x03, 0x0a, + 0x0f, 0x46, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x45, 0x76, 0x65, 0x6e, 0x74, + 0x12, 0x20, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x04, 0x42, 0x02, 0x18, 0x01, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, + 0x6d, 0x70, 0x12, 0x20, 0x0a, 0x0a, 0x63, 0x68, 0x61, 0x6e, 0x5f, 0x69, 0x64, 0x5f, 0x69, 0x6e, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x42, 0x02, 0x30, 0x01, 0x52, 0x08, 0x63, 0x68, 0x61, 0x6e, + 0x49, 0x64, 0x49, 0x6e, 0x12, 0x22, 0x0a, 0x0b, 0x63, 0x68, 0x61, 0x6e, 0x5f, 0x69, 0x64, 0x5f, + 0x6f, 0x75, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x42, 0x02, 0x30, 0x01, 0x52, 0x09, 0x63, + 0x68, 0x61, 0x6e, 0x49, 0x64, 0x4f, 0x75, 0x74, 0x12, 0x15, 0x0a, 0x06, 0x61, 0x6d, 0x74, 0x5f, + 0x69, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x61, 0x6d, 0x74, 0x49, 0x6e, 0x12, + 0x17, 0x0a, 0x07, 0x61, 0x6d, 0x74, 0x5f, 0x6f, 0x75, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x04, + 0x52, 0x06, 0x61, 0x6d, 0x74, 0x4f, 0x75, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x66, 0x65, 0x65, 0x18, + 0x07, 0x20, 0x01, 0x28, 0x04, 0x52, 0x03, 0x66, 0x65, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x66, 0x65, + 0x65, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x08, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, 0x66, 0x65, + 0x65, 0x4d, 0x73, 0x61, 0x74, 0x12, 0x1e, 0x0a, 0x0b, 0x61, 0x6d, 0x74, 0x5f, 0x69, 0x6e, 0x5f, + 0x6d, 0x73, 0x61, 0x74, 0x18, 0x09, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x61, 0x6d, 0x74, 0x49, + 0x6e, 0x4d, 0x73, 0x61, 0x74, 0x12, 0x20, 0x0a, 0x0c, 0x61, 0x6d, 0x74, 0x5f, 0x6f, 0x75, 0x74, + 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x61, 0x6d, 0x74, + 0x4f, 0x75, 0x74, 0x4d, 0x73, 0x61, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x74, 0x69, 0x6d, 0x65, 0x73, + 0x74, 0x61, 0x6d, 0x70, 0x5f, 0x6e, 0x73, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x74, + 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x4e, 0x73, 0x12, 0x22, 0x0a, 0x0d, 0x70, 0x65, + 0x65, 0x72, 0x5f, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x5f, 0x69, 0x6e, 0x18, 0x0c, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0b, 0x70, 0x65, 0x65, 0x72, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x49, 0x6e, 0x12, 0x24, + 0x0a, 0x0e, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x5f, 0x6f, 0x75, 0x74, + 0x18, 0x0d, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x70, 0x65, 0x65, 0x72, 0x41, 0x6c, 0x69, 0x61, + 0x73, 0x4f, 0x75, 0x74, 0x22, 0x8c, 0x01, 0x0a, 0x19, 0x46, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, + 0x69, 0x6e, 0x67, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x43, 0x0a, 0x11, 0x66, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, + 0x5f, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, + 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, + 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x10, 0x66, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x69, 0x6e, + 0x67, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x2a, 0x0a, 0x11, 0x6c, 0x61, 0x73, 0x74, 0x5f, + 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0d, 0x52, 0x0f, 0x6c, 0x61, 0x73, 0x74, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x49, 0x6e, + 0x64, 0x65, 0x78, 0x22, 0x50, 0x0a, 0x1a, 0x45, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x43, 0x68, 0x61, + 0x6e, 0x6e, 0x65, 0x6c, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x12, 0x32, 0x0a, 0x0a, 0x63, 0x68, 0x61, 0x6e, 0x5f, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, + 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x09, 0x63, 0x68, 0x61, 0x6e, + 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x22, 0x64, 0x0a, 0x0d, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, + 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x12, 0x32, 0x0a, 0x0a, 0x63, 0x68, 0x61, 0x6e, 0x5f, 0x70, + 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x6c, 0x6e, 0x72, + 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x52, + 0x09, 0x63, 0x68, 0x61, 0x6e, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x63, 0x68, + 0x61, 0x6e, 0x5f, 0x62, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, + 0x0a, 0x63, 0x68, 0x61, 0x6e, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x22, 0x73, 0x0a, 0x0f, 0x4d, + 0x75, 0x6c, 0x74, 0x69, 0x43, 0x68, 0x61, 0x6e, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x12, 0x34, + 0x0a, 0x0b, 0x63, 0x68, 0x61, 0x6e, 0x5f, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x18, 0x01, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, + 0x6e, 0x65, 0x6c, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x0a, 0x63, 0x68, 0x61, 0x6e, 0x50, 0x6f, + 0x69, 0x6e, 0x74, 0x73, 0x12, 0x2a, 0x0a, 0x11, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x5f, 0x63, 0x68, + 0x61, 0x6e, 0x5f, 0x62, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, + 0x0f, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x43, 0x68, 0x61, 0x6e, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, + 0x22, 0x19, 0x0a, 0x17, 0x43, 0x68, 0x61, 0x6e, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x45, 0x78, + 0x70, 0x6f, 0x72, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x9f, 0x01, 0x0a, 0x12, + 0x43, 0x68, 0x61, 0x6e, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, + 0x6f, 0x74, 0x12, 0x45, 0x0a, 0x13, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x5f, 0x63, 0x68, 0x61, + 0x6e, 0x5f, 0x62, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x15, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x42, + 0x61, 0x63, 0x6b, 0x75, 0x70, 0x73, 0x52, 0x11, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x43, 0x68, + 0x61, 0x6e, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x73, 0x12, 0x42, 0x0a, 0x11, 0x6d, 0x75, 0x6c, + 0x74, 0x69, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x5f, 0x62, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4d, 0x75, 0x6c, + 0x74, 0x69, 0x43, 0x68, 0x61, 0x6e, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x52, 0x0f, 0x6d, 0x75, + 0x6c, 0x74, 0x69, 0x43, 0x68, 0x61, 0x6e, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x22, 0x49, 0x0a, + 0x0e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x73, 0x12, + 0x37, 0x0a, 0x0c, 0x63, 0x68, 0x61, 0x6e, 0x5f, 0x62, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x73, 0x18, + 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, + 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x52, 0x0b, 0x63, 0x68, 0x61, + 0x6e, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x73, 0x22, 0x8e, 0x01, 0x0a, 0x18, 0x52, 0x65, 0x73, + 0x74, 0x6f, 0x72, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x3a, 0x0a, 0x0c, 0x63, 0x68, 0x61, 0x6e, 0x5f, 0x62, 0x61, + 0x63, 0x6b, 0x75, 0x70, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x6c, 0x6e, + 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x42, 0x61, 0x63, 0x6b, 0x75, + 0x70, 0x73, 0x48, 0x00, 0x52, 0x0b, 0x63, 0x68, 0x61, 0x6e, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, + 0x73, 0x12, 0x2c, 0x0a, 0x11, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x5f, + 0x62, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x0f, + 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x43, 0x68, 0x61, 0x6e, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x42, + 0x08, 0x0a, 0x06, 0x62, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x22, 0x17, 0x0a, 0x15, 0x52, 0x65, 0x73, + 0x74, 0x6f, 0x72, 0x65, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x22, 0x1b, 0x0a, 0x19, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x42, 0x61, 0x63, + 0x6b, 0x75, 0x70, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x22, + 0x1a, 0x0a, 0x18, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x43, 0x68, 0x61, 0x6e, 0x42, 0x61, 0x63, + 0x6b, 0x75, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x44, 0x0a, 0x12, 0x4d, + 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, + 0x6e, 0x12, 0x16, 0x0a, 0x06, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x06, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x12, 0x16, 0x0a, 0x06, 0x61, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, + 0x6e, 0x22, 0xb0, 0x01, 0x0a, 0x13, 0x42, 0x61, 0x6b, 0x65, 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, + 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x3b, 0x0a, 0x0b, 0x70, 0x65, 0x72, + 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, + 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x50, + 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x0b, 0x70, 0x65, 0x72, 0x6d, 0x69, + 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x1e, 0x0a, 0x0b, 0x72, 0x6f, 0x6f, 0x74, 0x5f, 0x6b, + 0x65, 0x79, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x72, 0x6f, 0x6f, + 0x74, 0x4b, 0x65, 0x79, 0x49, 0x64, 0x12, 0x3c, 0x0a, 0x1a, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x5f, + 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5f, 0x70, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, + 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x18, 0x61, 0x6c, 0x6c, 0x6f, + 0x77, 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, + 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x32, 0x0a, 0x14, 0x42, 0x61, 0x6b, 0x65, 0x4d, 0x61, 0x63, 0x61, + 0x72, 0x6f, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1a, 0x0a, 0x08, + 0x6d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, + 0x6d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x22, 0x18, 0x0a, 0x16, 0x4c, 0x69, 0x73, 0x74, + 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x49, 0x44, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x22, 0x3b, 0x0a, 0x17, 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, + 0x6f, 0x6e, 0x49, 0x44, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x20, 0x0a, + 0x0c, 0x72, 0x6f, 0x6f, 0x74, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x01, 0x20, + 0x03, 0x28, 0x04, 0x52, 0x0a, 0x72, 0x6f, 0x6f, 0x74, 0x4b, 0x65, 0x79, 0x49, 0x64, 0x73, 0x22, + 0x39, 0x0a, 0x17, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, + 0x6e, 0x49, 0x44, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1e, 0x0a, 0x0b, 0x72, 0x6f, + 0x6f, 0x74, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, + 0x09, 0x72, 0x6f, 0x6f, 0x74, 0x4b, 0x65, 0x79, 0x49, 0x64, 0x22, 0x34, 0x0a, 0x18, 0x44, 0x65, + 0x6c, 0x65, 0x74, 0x65, 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x49, 0x44, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, + 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, + 0x22, 0x55, 0x0a, 0x16, 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x50, 0x65, 0x72, 0x6d, + 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x3b, 0x0a, 0x0b, 0x70, 0x65, + 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x0b, 0x70, 0x65, 0x72, 0x6d, - 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x1e, 0x0a, 0x0a, 0x66, 0x75, 0x6c, 0x6c, 0x4d, - 0x65, 0x74, 0x68, 0x6f, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x66, 0x75, 0x6c, - 0x6c, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x22, 0x2c, 0x0a, 0x14, 0x43, 0x68, 0x65, 0x63, 0x6b, - 0x4d, 0x61, 0x63, 0x50, 0x65, 0x72, 0x6d, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, - 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, - 0x76, 0x61, 0x6c, 0x69, 0x64, 0x22, 0xf4, 0x02, 0x0a, 0x14, 0x52, 0x50, 0x43, 0x4d, 0x69, 0x64, - 0x64, 0x6c, 0x65, 0x77, 0x61, 0x72, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, - 0x0a, 0x0a, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x04, 0x52, 0x09, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x49, 0x64, 0x12, 0x21, 0x0a, - 0x0c, 0x72, 0x61, 0x77, 0x5f, 0x6d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x72, 0x61, 0x77, 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, - 0x12, 0x36, 0x0a, 0x17, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5f, 0x63, 0x61, 0x76, 0x65, 0x61, - 0x74, 0x5f, 0x63, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x15, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x43, 0x61, 0x76, 0x65, 0x61, 0x74, 0x43, - 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x34, 0x0a, 0x0b, 0x73, 0x74, 0x72, 0x65, - 0x61, 0x6d, 0x5f, 0x61, 0x75, 0x74, 0x68, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, - 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x41, 0x75, 0x74, 0x68, - 0x48, 0x00, 0x52, 0x0a, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x41, 0x75, 0x74, 0x68, 0x12, 0x2d, - 0x0a, 0x07, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x11, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x50, 0x43, 0x4d, 0x65, 0x73, 0x73, 0x61, - 0x67, 0x65, 0x48, 0x00, 0x52, 0x07, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2f, 0x0a, - 0x08, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x11, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x50, 0x43, 0x4d, 0x65, 0x73, 0x73, 0x61, - 0x67, 0x65, 0x48, 0x00, 0x52, 0x08, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x23, - 0x0a, 0x0c, 0x72, 0x65, 0x67, 0x5f, 0x63, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x18, 0x08, - 0x20, 0x01, 0x28, 0x08, 0x48, 0x00, 0x52, 0x0b, 0x72, 0x65, 0x67, 0x43, 0x6f, 0x6d, 0x70, 0x6c, - 0x65, 0x74, 0x65, 0x12, 0x15, 0x0a, 0x06, 0x6d, 0x73, 0x67, 0x5f, 0x69, 0x64, 0x18, 0x07, 0x20, - 0x01, 0x28, 0x04, 0x52, 0x05, 0x6d, 0x73, 0x67, 0x49, 0x64, 0x42, 0x10, 0x0a, 0x0e, 0x69, 0x6e, - 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x22, 0x34, 0x0a, 0x0a, - 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x41, 0x75, 0x74, 0x68, 0x12, 0x26, 0x0a, 0x0f, 0x6d, 0x65, - 0x74, 0x68, 0x6f, 0x64, 0x5f, 0x66, 0x75, 0x6c, 0x6c, 0x5f, 0x75, 0x72, 0x69, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x0d, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x46, 0x75, 0x6c, 0x6c, 0x55, - 0x72, 0x69, 0x22, 0xab, 0x01, 0x0a, 0x0a, 0x52, 0x50, 0x43, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, - 0x65, 0x12, 0x26, 0x0a, 0x0f, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x5f, 0x66, 0x75, 0x6c, 0x6c, - 0x5f, 0x75, 0x72, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x6d, 0x65, 0x74, 0x68, - 0x6f, 0x64, 0x46, 0x75, 0x6c, 0x6c, 0x55, 0x72, 0x69, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x74, 0x72, - 0x65, 0x61, 0x6d, 0x5f, 0x72, 0x70, 0x63, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x73, - 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x70, 0x63, 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x79, 0x70, 0x65, - 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x79, 0x70, - 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, - 0x7a, 0x65, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x73, 0x65, 0x72, 0x69, 0x61, - 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x12, 0x19, 0x0a, 0x08, 0x69, 0x73, 0x5f, 0x65, 0x72, 0x72, 0x6f, - 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x69, 0x73, 0x45, 0x72, 0x72, 0x6f, 0x72, - 0x22, 0xc0, 0x01, 0x0a, 0x15, 0x52, 0x50, 0x43, 0x4d, 0x69, 0x64, 0x64, 0x6c, 0x65, 0x77, 0x61, - 0x72, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1c, 0x0a, 0x0a, 0x72, 0x65, - 0x66, 0x5f, 0x6d, 0x73, 0x67, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, - 0x72, 0x65, 0x66, 0x4d, 0x73, 0x67, 0x49, 0x64, 0x12, 0x3b, 0x0a, 0x08, 0x72, 0x65, 0x67, 0x69, - 0x73, 0x74, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x6c, 0x6e, 0x72, - 0x70, 0x63, 0x2e, 0x4d, 0x69, 0x64, 0x64, 0x6c, 0x65, 0x77, 0x61, 0x72, 0x65, 0x52, 0x65, 0x67, - 0x69, 0x73, 0x74, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x00, 0x52, 0x08, 0x72, 0x65, 0x67, - 0x69, 0x73, 0x74, 0x65, 0x72, 0x12, 0x36, 0x0a, 0x08, 0x66, 0x65, 0x65, 0x64, 0x62, 0x61, 0x63, - 0x6b, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, - 0x49, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x46, 0x65, 0x65, 0x64, 0x62, 0x61, 0x63, - 0x6b, 0x48, 0x00, 0x52, 0x08, 0x66, 0x65, 0x65, 0x64, 0x62, 0x61, 0x63, 0x6b, 0x42, 0x14, 0x0a, - 0x12, 0x6d, 0x69, 0x64, 0x64, 0x6c, 0x65, 0x77, 0x61, 0x72, 0x65, 0x5f, 0x6d, 0x65, 0x73, 0x73, - 0x61, 0x67, 0x65, 0x22, 0xa6, 0x01, 0x0a, 0x16, 0x4d, 0x69, 0x64, 0x64, 0x6c, 0x65, 0x77, 0x61, - 0x72, 0x65, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x27, - 0x0a, 0x0f, 0x6d, 0x69, 0x64, 0x64, 0x6c, 0x65, 0x77, 0x61, 0x72, 0x65, 0x5f, 0x6e, 0x61, 0x6d, - 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x6d, 0x69, 0x64, 0x64, 0x6c, 0x65, 0x77, - 0x61, 0x72, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x3d, 0x0a, 0x1b, 0x63, 0x75, 0x73, 0x74, 0x6f, - 0x6d, 0x5f, 0x6d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x5f, 0x63, 0x61, 0x76, 0x65, 0x61, - 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x18, 0x63, 0x75, - 0x73, 0x74, 0x6f, 0x6d, 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x43, 0x61, 0x76, 0x65, - 0x61, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x24, 0x0a, 0x0e, 0x72, 0x65, 0x61, 0x64, 0x5f, 0x6f, - 0x6e, 0x6c, 0x79, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, - 0x72, 0x65, 0x61, 0x64, 0x4f, 0x6e, 0x6c, 0x79, 0x4d, 0x6f, 0x64, 0x65, 0x22, 0x8b, 0x01, 0x0a, - 0x11, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x46, 0x65, 0x65, 0x64, 0x62, 0x61, - 0x63, 0x6b, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x29, 0x0a, 0x10, 0x72, 0x65, 0x70, 0x6c, - 0x61, 0x63, 0x65, 0x5f, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x08, 0x52, 0x0f, 0x72, 0x65, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x35, 0x0a, 0x16, 0x72, 0x65, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x6d, 0x65, - 0x6e, 0x74, 0x5f, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x0c, 0x52, 0x15, 0x72, 0x65, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x6d, 0x65, 0x6e, 0x74, - 0x53, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x2a, 0xcb, 0x02, 0x0a, 0x10, 0x4f, - 0x75, 0x74, 0x70, 0x75, 0x74, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, - 0x1b, 0x0a, 0x17, 0x53, 0x43, 0x52, 0x49, 0x50, 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x50, - 0x55, 0x42, 0x4b, 0x45, 0x59, 0x5f, 0x48, 0x41, 0x53, 0x48, 0x10, 0x00, 0x12, 0x1b, 0x0a, 0x17, - 0x53, 0x43, 0x52, 0x49, 0x50, 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x53, 0x43, 0x52, 0x49, - 0x50, 0x54, 0x5f, 0x48, 0x41, 0x53, 0x48, 0x10, 0x01, 0x12, 0x26, 0x0a, 0x22, 0x53, 0x43, 0x52, - 0x49, 0x50, 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x57, 0x49, 0x54, 0x4e, 0x45, 0x53, 0x53, - 0x5f, 0x56, 0x30, 0x5f, 0x50, 0x55, 0x42, 0x4b, 0x45, 0x59, 0x5f, 0x48, 0x41, 0x53, 0x48, 0x10, - 0x02, 0x12, 0x26, 0x0a, 0x22, 0x53, 0x43, 0x52, 0x49, 0x50, 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, - 0x5f, 0x57, 0x49, 0x54, 0x4e, 0x45, 0x53, 0x53, 0x5f, 0x56, 0x30, 0x5f, 0x53, 0x43, 0x52, 0x49, - 0x50, 0x54, 0x5f, 0x48, 0x41, 0x53, 0x48, 0x10, 0x03, 0x12, 0x16, 0x0a, 0x12, 0x53, 0x43, 0x52, - 0x49, 0x50, 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x50, 0x55, 0x42, 0x4b, 0x45, 0x59, 0x10, - 0x04, 0x12, 0x18, 0x0a, 0x14, 0x53, 0x43, 0x52, 0x49, 0x50, 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, - 0x5f, 0x4d, 0x55, 0x4c, 0x54, 0x49, 0x53, 0x49, 0x47, 0x10, 0x05, 0x12, 0x18, 0x0a, 0x14, 0x53, - 0x43, 0x52, 0x49, 0x50, 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x4e, 0x55, 0x4c, 0x4c, 0x44, - 0x41, 0x54, 0x41, 0x10, 0x06, 0x12, 0x1c, 0x0a, 0x18, 0x53, 0x43, 0x52, 0x49, 0x50, 0x54, 0x5f, - 0x54, 0x59, 0x50, 0x45, 0x5f, 0x4e, 0x4f, 0x4e, 0x5f, 0x53, 0x54, 0x41, 0x4e, 0x44, 0x41, 0x52, - 0x44, 0x10, 0x07, 0x12, 0x1f, 0x0a, 0x1b, 0x53, 0x43, 0x52, 0x49, 0x50, 0x54, 0x5f, 0x54, 0x59, - 0x50, 0x45, 0x5f, 0x57, 0x49, 0x54, 0x4e, 0x45, 0x53, 0x53, 0x5f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, - 0x57, 0x4e, 0x10, 0x08, 0x12, 0x22, 0x0a, 0x1e, 0x53, 0x43, 0x52, 0x49, 0x50, 0x54, 0x5f, 0x54, - 0x59, 0x50, 0x45, 0x5f, 0x57, 0x49, 0x54, 0x4e, 0x45, 0x53, 0x53, 0x5f, 0x56, 0x31, 0x5f, 0x54, - 0x41, 0x50, 0x52, 0x4f, 0x4f, 0x54, 0x10, 0x09, 0x2a, 0x62, 0x0a, 0x15, 0x43, 0x6f, 0x69, 0x6e, - 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, - 0x79, 0x12, 0x1e, 0x0a, 0x1a, 0x53, 0x54, 0x52, 0x41, 0x54, 0x45, 0x47, 0x59, 0x5f, 0x55, 0x53, - 0x45, 0x5f, 0x47, 0x4c, 0x4f, 0x42, 0x41, 0x4c, 0x5f, 0x43, 0x4f, 0x4e, 0x46, 0x49, 0x47, 0x10, - 0x00, 0x12, 0x14, 0x0a, 0x10, 0x53, 0x54, 0x52, 0x41, 0x54, 0x45, 0x47, 0x59, 0x5f, 0x4c, 0x41, - 0x52, 0x47, 0x45, 0x53, 0x54, 0x10, 0x01, 0x12, 0x13, 0x0a, 0x0f, 0x53, 0x54, 0x52, 0x41, 0x54, - 0x45, 0x47, 0x59, 0x5f, 0x52, 0x41, 0x4e, 0x44, 0x4f, 0x4d, 0x10, 0x02, 0x2a, 0xac, 0x01, 0x0a, - 0x0b, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x54, 0x79, 0x70, 0x65, 0x12, 0x17, 0x0a, 0x13, - 0x57, 0x49, 0x54, 0x4e, 0x45, 0x53, 0x53, 0x5f, 0x50, 0x55, 0x42, 0x4b, 0x45, 0x59, 0x5f, 0x48, - 0x41, 0x53, 0x48, 0x10, 0x00, 0x12, 0x16, 0x0a, 0x12, 0x4e, 0x45, 0x53, 0x54, 0x45, 0x44, 0x5f, - 0x50, 0x55, 0x42, 0x4b, 0x45, 0x59, 0x5f, 0x48, 0x41, 0x53, 0x48, 0x10, 0x01, 0x12, 0x1e, 0x0a, - 0x1a, 0x55, 0x4e, 0x55, 0x53, 0x45, 0x44, 0x5f, 0x57, 0x49, 0x54, 0x4e, 0x45, 0x53, 0x53, 0x5f, - 0x50, 0x55, 0x42, 0x4b, 0x45, 0x59, 0x5f, 0x48, 0x41, 0x53, 0x48, 0x10, 0x02, 0x12, 0x1d, 0x0a, - 0x19, 0x55, 0x4e, 0x55, 0x53, 0x45, 0x44, 0x5f, 0x4e, 0x45, 0x53, 0x54, 0x45, 0x44, 0x5f, 0x50, - 0x55, 0x42, 0x4b, 0x45, 0x59, 0x5f, 0x48, 0x41, 0x53, 0x48, 0x10, 0x03, 0x12, 0x12, 0x0a, 0x0e, - 0x54, 0x41, 0x50, 0x52, 0x4f, 0x4f, 0x54, 0x5f, 0x50, 0x55, 0x42, 0x4b, 0x45, 0x59, 0x10, 0x04, - 0x12, 0x19, 0x0a, 0x15, 0x55, 0x4e, 0x55, 0x53, 0x45, 0x44, 0x5f, 0x54, 0x41, 0x50, 0x52, 0x4f, - 0x4f, 0x54, 0x5f, 0x50, 0x55, 0x42, 0x4b, 0x45, 0x59, 0x10, 0x05, 0x2a, 0x8c, 0x01, 0x0a, 0x0e, - 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x6d, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1b, - 0x0a, 0x17, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x5f, 0x43, 0x4f, 0x4d, 0x4d, 0x49, 0x54, - 0x4d, 0x45, 0x4e, 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x4c, - 0x45, 0x47, 0x41, 0x43, 0x59, 0x10, 0x01, 0x12, 0x15, 0x0a, 0x11, 0x53, 0x54, 0x41, 0x54, 0x49, - 0x43, 0x5f, 0x52, 0x45, 0x4d, 0x4f, 0x54, 0x45, 0x5f, 0x4b, 0x45, 0x59, 0x10, 0x02, 0x12, 0x0b, - 0x0a, 0x07, 0x41, 0x4e, 0x43, 0x48, 0x4f, 0x52, 0x53, 0x10, 0x03, 0x12, 0x19, 0x0a, 0x15, 0x53, - 0x43, 0x52, 0x49, 0x50, 0x54, 0x5f, 0x45, 0x4e, 0x46, 0x4f, 0x52, 0x43, 0x45, 0x44, 0x5f, 0x4c, - 0x45, 0x41, 0x53, 0x45, 0x10, 0x04, 0x12, 0x12, 0x0a, 0x0e, 0x53, 0x49, 0x4d, 0x50, 0x4c, 0x45, - 0x5f, 0x54, 0x41, 0x50, 0x52, 0x4f, 0x4f, 0x54, 0x10, 0x05, 0x2a, 0x61, 0x0a, 0x09, 0x49, 0x6e, - 0x69, 0x74, 0x69, 0x61, 0x74, 0x6f, 0x72, 0x12, 0x15, 0x0a, 0x11, 0x49, 0x4e, 0x49, 0x54, 0x49, - 0x41, 0x54, 0x4f, 0x52, 0x5f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x13, - 0x0a, 0x0f, 0x49, 0x4e, 0x49, 0x54, 0x49, 0x41, 0x54, 0x4f, 0x52, 0x5f, 0x4c, 0x4f, 0x43, 0x41, - 0x4c, 0x10, 0x01, 0x12, 0x14, 0x0a, 0x10, 0x49, 0x4e, 0x49, 0x54, 0x49, 0x41, 0x54, 0x4f, 0x52, - 0x5f, 0x52, 0x45, 0x4d, 0x4f, 0x54, 0x45, 0x10, 0x02, 0x12, 0x12, 0x0a, 0x0e, 0x49, 0x4e, 0x49, - 0x54, 0x49, 0x41, 0x54, 0x4f, 0x52, 0x5f, 0x42, 0x4f, 0x54, 0x48, 0x10, 0x03, 0x2a, 0x60, 0x0a, - 0x0e, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x12, - 0x10, 0x0a, 0x0c, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, - 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x41, 0x4e, 0x43, 0x48, 0x4f, 0x52, 0x10, 0x01, 0x12, 0x11, 0x0a, - 0x0d, 0x49, 0x4e, 0x43, 0x4f, 0x4d, 0x49, 0x4e, 0x47, 0x5f, 0x48, 0x54, 0x4c, 0x43, 0x10, 0x02, - 0x12, 0x11, 0x0a, 0x0d, 0x4f, 0x55, 0x54, 0x47, 0x4f, 0x49, 0x4e, 0x47, 0x5f, 0x48, 0x54, 0x4c, - 0x43, 0x10, 0x03, 0x12, 0x0a, 0x0a, 0x06, 0x43, 0x4f, 0x4d, 0x4d, 0x49, 0x54, 0x10, 0x04, 0x2a, - 0x71, 0x0a, 0x11, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x4f, 0x75, 0x74, - 0x63, 0x6f, 0x6d, 0x65, 0x12, 0x13, 0x0a, 0x0f, 0x4f, 0x55, 0x54, 0x43, 0x4f, 0x4d, 0x45, 0x5f, - 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x43, 0x4c, 0x41, - 0x49, 0x4d, 0x45, 0x44, 0x10, 0x01, 0x12, 0x0d, 0x0a, 0x09, 0x55, 0x4e, 0x43, 0x4c, 0x41, 0x49, - 0x4d, 0x45, 0x44, 0x10, 0x02, 0x12, 0x0d, 0x0a, 0x09, 0x41, 0x42, 0x41, 0x4e, 0x44, 0x4f, 0x4e, - 0x45, 0x44, 0x10, 0x03, 0x12, 0x0f, 0x0a, 0x0b, 0x46, 0x49, 0x52, 0x53, 0x54, 0x5f, 0x53, 0x54, - 0x41, 0x47, 0x45, 0x10, 0x04, 0x12, 0x0b, 0x0a, 0x07, 0x54, 0x49, 0x4d, 0x45, 0x4f, 0x55, 0x54, - 0x10, 0x05, 0x2a, 0x39, 0x0a, 0x0e, 0x4e, 0x6f, 0x64, 0x65, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, - 0x54, 0x79, 0x70, 0x65, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, - 0x00, 0x12, 0x1a, 0x0a, 0x16, 0x42, 0x45, 0x54, 0x57, 0x45, 0x45, 0x4e, 0x4e, 0x45, 0x53, 0x53, - 0x5f, 0x43, 0x45, 0x4e, 0x54, 0x52, 0x41, 0x4c, 0x49, 0x54, 0x59, 0x10, 0x01, 0x2a, 0x3b, 0x0a, - 0x10, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x48, 0x54, 0x4c, 0x43, 0x53, 0x74, 0x61, 0x74, - 0x65, 0x12, 0x0c, 0x0a, 0x08, 0x41, 0x43, 0x43, 0x45, 0x50, 0x54, 0x45, 0x44, 0x10, 0x00, 0x12, - 0x0b, 0x0a, 0x07, 0x53, 0x45, 0x54, 0x54, 0x4c, 0x45, 0x44, 0x10, 0x01, 0x12, 0x0c, 0x0a, 0x08, - 0x43, 0x41, 0x4e, 0x43, 0x45, 0x4c, 0x45, 0x44, 0x10, 0x02, 0x2a, 0xf6, 0x01, 0x0a, 0x14, 0x50, - 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x46, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x52, 0x65, 0x61, - 0x73, 0x6f, 0x6e, 0x12, 0x17, 0x0a, 0x13, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x52, - 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x4e, 0x4f, 0x4e, 0x45, 0x10, 0x00, 0x12, 0x1a, 0x0a, 0x16, - 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x54, - 0x49, 0x4d, 0x45, 0x4f, 0x55, 0x54, 0x10, 0x01, 0x12, 0x1b, 0x0a, 0x17, 0x46, 0x41, 0x49, 0x4c, - 0x55, 0x52, 0x45, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x4e, 0x4f, 0x5f, 0x52, 0x4f, - 0x55, 0x54, 0x45, 0x10, 0x02, 0x12, 0x18, 0x0a, 0x14, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, - 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, 0x03, 0x12, - 0x2c, 0x0a, 0x28, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, - 0x4e, 0x5f, 0x49, 0x4e, 0x43, 0x4f, 0x52, 0x52, 0x45, 0x43, 0x54, 0x5f, 0x50, 0x41, 0x59, 0x4d, - 0x45, 0x4e, 0x54, 0x5f, 0x44, 0x45, 0x54, 0x41, 0x49, 0x4c, 0x53, 0x10, 0x04, 0x12, 0x27, 0x0a, - 0x23, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, - 0x49, 0x4e, 0x53, 0x55, 0x46, 0x46, 0x49, 0x43, 0x49, 0x45, 0x4e, 0x54, 0x5f, 0x42, 0x41, 0x4c, - 0x41, 0x4e, 0x43, 0x45, 0x10, 0x05, 0x12, 0x1b, 0x0a, 0x17, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, - 0x45, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x43, 0x41, 0x4e, 0x43, 0x45, 0x4c, 0x45, - 0x44, 0x10, 0x06, 0x2a, 0x89, 0x05, 0x0a, 0x0a, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x42, - 0x69, 0x74, 0x12, 0x18, 0x0a, 0x14, 0x44, 0x41, 0x54, 0x41, 0x4c, 0x4f, 0x53, 0x53, 0x5f, 0x50, - 0x52, 0x4f, 0x54, 0x45, 0x43, 0x54, 0x5f, 0x52, 0x45, 0x51, 0x10, 0x00, 0x12, 0x18, 0x0a, 0x14, - 0x44, 0x41, 0x54, 0x41, 0x4c, 0x4f, 0x53, 0x53, 0x5f, 0x50, 0x52, 0x4f, 0x54, 0x45, 0x43, 0x54, - 0x5f, 0x4f, 0x50, 0x54, 0x10, 0x01, 0x12, 0x17, 0x0a, 0x13, 0x49, 0x4e, 0x49, 0x54, 0x49, 0x41, - 0x4c, 0x5f, 0x52, 0x4f, 0x55, 0x49, 0x4e, 0x47, 0x5f, 0x53, 0x59, 0x4e, 0x43, 0x10, 0x03, 0x12, - 0x1f, 0x0a, 0x1b, 0x55, 0x50, 0x46, 0x52, 0x4f, 0x4e, 0x54, 0x5f, 0x53, 0x48, 0x55, 0x54, 0x44, - 0x4f, 0x57, 0x4e, 0x5f, 0x53, 0x43, 0x52, 0x49, 0x50, 0x54, 0x5f, 0x52, 0x45, 0x51, 0x10, 0x04, - 0x12, 0x1f, 0x0a, 0x1b, 0x55, 0x50, 0x46, 0x52, 0x4f, 0x4e, 0x54, 0x5f, 0x53, 0x48, 0x55, 0x54, - 0x44, 0x4f, 0x57, 0x4e, 0x5f, 0x53, 0x43, 0x52, 0x49, 0x50, 0x54, 0x5f, 0x4f, 0x50, 0x54, 0x10, - 0x05, 0x12, 0x16, 0x0a, 0x12, 0x47, 0x4f, 0x53, 0x53, 0x49, 0x50, 0x5f, 0x51, 0x55, 0x45, 0x52, - 0x49, 0x45, 0x53, 0x5f, 0x52, 0x45, 0x51, 0x10, 0x06, 0x12, 0x16, 0x0a, 0x12, 0x47, 0x4f, 0x53, - 0x53, 0x49, 0x50, 0x5f, 0x51, 0x55, 0x45, 0x52, 0x49, 0x45, 0x53, 0x5f, 0x4f, 0x50, 0x54, 0x10, - 0x07, 0x12, 0x11, 0x0a, 0x0d, 0x54, 0x4c, 0x56, 0x5f, 0x4f, 0x4e, 0x49, 0x4f, 0x4e, 0x5f, 0x52, - 0x45, 0x51, 0x10, 0x08, 0x12, 0x11, 0x0a, 0x0d, 0x54, 0x4c, 0x56, 0x5f, 0x4f, 0x4e, 0x49, 0x4f, - 0x4e, 0x5f, 0x4f, 0x50, 0x54, 0x10, 0x09, 0x12, 0x1a, 0x0a, 0x16, 0x45, 0x58, 0x54, 0x5f, 0x47, - 0x4f, 0x53, 0x53, 0x49, 0x50, 0x5f, 0x51, 0x55, 0x45, 0x52, 0x49, 0x45, 0x53, 0x5f, 0x52, 0x45, - 0x51, 0x10, 0x0a, 0x12, 0x1a, 0x0a, 0x16, 0x45, 0x58, 0x54, 0x5f, 0x47, 0x4f, 0x53, 0x53, 0x49, - 0x50, 0x5f, 0x51, 0x55, 0x45, 0x52, 0x49, 0x45, 0x53, 0x5f, 0x4f, 0x50, 0x54, 0x10, 0x0b, 0x12, - 0x19, 0x0a, 0x15, 0x53, 0x54, 0x41, 0x54, 0x49, 0x43, 0x5f, 0x52, 0x45, 0x4d, 0x4f, 0x54, 0x45, - 0x5f, 0x4b, 0x45, 0x59, 0x5f, 0x52, 0x45, 0x51, 0x10, 0x0c, 0x12, 0x19, 0x0a, 0x15, 0x53, 0x54, - 0x41, 0x54, 0x49, 0x43, 0x5f, 0x52, 0x45, 0x4d, 0x4f, 0x54, 0x45, 0x5f, 0x4b, 0x45, 0x59, 0x5f, - 0x4f, 0x50, 0x54, 0x10, 0x0d, 0x12, 0x14, 0x0a, 0x10, 0x50, 0x41, 0x59, 0x4d, 0x45, 0x4e, 0x54, - 0x5f, 0x41, 0x44, 0x44, 0x52, 0x5f, 0x52, 0x45, 0x51, 0x10, 0x0e, 0x12, 0x14, 0x0a, 0x10, 0x50, - 0x41, 0x59, 0x4d, 0x45, 0x4e, 0x54, 0x5f, 0x41, 0x44, 0x44, 0x52, 0x5f, 0x4f, 0x50, 0x54, 0x10, - 0x0f, 0x12, 0x0b, 0x0a, 0x07, 0x4d, 0x50, 0x50, 0x5f, 0x52, 0x45, 0x51, 0x10, 0x10, 0x12, 0x0b, - 0x0a, 0x07, 0x4d, 0x50, 0x50, 0x5f, 0x4f, 0x50, 0x54, 0x10, 0x11, 0x12, 0x16, 0x0a, 0x12, 0x57, - 0x55, 0x4d, 0x42, 0x4f, 0x5f, 0x43, 0x48, 0x41, 0x4e, 0x4e, 0x45, 0x4c, 0x53, 0x5f, 0x52, 0x45, - 0x51, 0x10, 0x12, 0x12, 0x16, 0x0a, 0x12, 0x57, 0x55, 0x4d, 0x42, 0x4f, 0x5f, 0x43, 0x48, 0x41, - 0x4e, 0x4e, 0x45, 0x4c, 0x53, 0x5f, 0x4f, 0x50, 0x54, 0x10, 0x13, 0x12, 0x0f, 0x0a, 0x0b, 0x41, - 0x4e, 0x43, 0x48, 0x4f, 0x52, 0x53, 0x5f, 0x52, 0x45, 0x51, 0x10, 0x14, 0x12, 0x0f, 0x0a, 0x0b, - 0x41, 0x4e, 0x43, 0x48, 0x4f, 0x52, 0x53, 0x5f, 0x4f, 0x50, 0x54, 0x10, 0x15, 0x12, 0x1d, 0x0a, - 0x19, 0x41, 0x4e, 0x43, 0x48, 0x4f, 0x52, 0x53, 0x5f, 0x5a, 0x45, 0x52, 0x4f, 0x5f, 0x46, 0x45, - 0x45, 0x5f, 0x48, 0x54, 0x4c, 0x43, 0x5f, 0x52, 0x45, 0x51, 0x10, 0x16, 0x12, 0x1d, 0x0a, 0x19, - 0x41, 0x4e, 0x43, 0x48, 0x4f, 0x52, 0x53, 0x5f, 0x5a, 0x45, 0x52, 0x4f, 0x5f, 0x46, 0x45, 0x45, - 0x5f, 0x48, 0x54, 0x4c, 0x43, 0x5f, 0x4f, 0x50, 0x54, 0x10, 0x17, 0x12, 0x1b, 0x0a, 0x17, 0x52, - 0x4f, 0x55, 0x54, 0x45, 0x5f, 0x42, 0x4c, 0x49, 0x4e, 0x44, 0x49, 0x4e, 0x47, 0x5f, 0x52, 0x45, - 0x51, 0x55, 0x49, 0x52, 0x45, 0x44, 0x10, 0x18, 0x12, 0x1b, 0x0a, 0x17, 0x52, 0x4f, 0x55, 0x54, - 0x45, 0x5f, 0x42, 0x4c, 0x49, 0x4e, 0x44, 0x49, 0x4e, 0x47, 0x5f, 0x4f, 0x50, 0x54, 0x49, 0x4f, - 0x4e, 0x41, 0x4c, 0x10, 0x19, 0x12, 0x0b, 0x0a, 0x07, 0x41, 0x4d, 0x50, 0x5f, 0x52, 0x45, 0x51, - 0x10, 0x1e, 0x12, 0x0b, 0x0a, 0x07, 0x41, 0x4d, 0x50, 0x5f, 0x4f, 0x50, 0x54, 0x10, 0x1f, 0x2a, - 0xac, 0x01, 0x0a, 0x0d, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x46, 0x61, 0x69, 0x6c, 0x75, 0x72, - 0x65, 0x12, 0x1a, 0x0a, 0x16, 0x55, 0x50, 0x44, 0x41, 0x54, 0x45, 0x5f, 0x46, 0x41, 0x49, 0x4c, - 0x55, 0x52, 0x45, 0x5f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x1a, 0x0a, - 0x16, 0x55, 0x50, 0x44, 0x41, 0x54, 0x45, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, - 0x50, 0x45, 0x4e, 0x44, 0x49, 0x4e, 0x47, 0x10, 0x01, 0x12, 0x1c, 0x0a, 0x18, 0x55, 0x50, 0x44, - 0x41, 0x54, 0x45, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x4e, 0x4f, 0x54, 0x5f, - 0x46, 0x4f, 0x55, 0x4e, 0x44, 0x10, 0x02, 0x12, 0x1f, 0x0a, 0x1b, 0x55, 0x50, 0x44, 0x41, 0x54, - 0x45, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x49, 0x4e, 0x54, 0x45, 0x52, 0x4e, - 0x41, 0x4c, 0x5f, 0x45, 0x52, 0x52, 0x10, 0x03, 0x12, 0x24, 0x0a, 0x20, 0x55, 0x50, 0x44, 0x41, - 0x54, 0x45, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x49, 0x4e, 0x56, 0x41, 0x4c, - 0x49, 0x44, 0x5f, 0x50, 0x41, 0x52, 0x41, 0x4d, 0x45, 0x54, 0x45, 0x52, 0x10, 0x04, 0x32, 0xb9, - 0x27, 0x0a, 0x09, 0x4c, 0x69, 0x67, 0x68, 0x74, 0x6e, 0x69, 0x6e, 0x67, 0x12, 0x4a, 0x0a, 0x0d, - 0x57, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x12, 0x1b, 0x2e, - 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x42, 0x61, 0x6c, 0x61, - 0x6e, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x6c, 0x6e, 0x72, - 0x70, 0x63, 0x2e, 0x57, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4d, 0x0a, 0x0e, 0x43, 0x68, 0x61, 0x6e, - 0x6e, 0x65, 0x6c, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x12, 0x1c, 0x2e, 0x6c, 0x6e, 0x72, - 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, - 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, + 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x18, 0x0a, 0x16, 0x4c, 0x69, 0x73, 0x74, 0x50, + 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x22, 0xe4, 0x01, 0x0a, 0x17, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, + 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x64, 0x0a, + 0x12, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x5f, 0x70, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, + 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x35, 0x2e, 0x6c, 0x6e, 0x72, 0x70, + 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, + 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, + 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, + 0x52, 0x11, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, + 0x6f, 0x6e, 0x73, 0x1a, 0x63, 0x0a, 0x16, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x50, 0x65, 0x72, + 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, + 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, + 0x33, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, + 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x50, + 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x05, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xcc, 0x08, 0x0a, 0x07, 0x46, 0x61, 0x69, + 0x6c, 0x75, 0x72, 0x65, 0x12, 0x2e, 0x0a, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0e, 0x32, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x61, 0x69, 0x6c, 0x75, + 0x72, 0x65, 0x2e, 0x46, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x43, 0x6f, 0x64, 0x65, 0x52, 0x04, + 0x63, 0x6f, 0x64, 0x65, 0x12, 0x3b, 0x0a, 0x0e, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x5f, + 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x6c, + 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x55, 0x70, 0x64, 0x61, + 0x74, 0x65, 0x52, 0x0d, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x55, 0x70, 0x64, 0x61, 0x74, + 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x68, 0x74, 0x6c, 0x63, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x68, 0x74, 0x6c, 0x63, 0x4d, 0x73, 0x61, 0x74, 0x12, 0x22, + 0x0a, 0x0d, 0x6f, 0x6e, 0x69, 0x6f, 0x6e, 0x5f, 0x73, 0x68, 0x61, 0x5f, 0x32, 0x35, 0x36, 0x18, + 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x6f, 0x6e, 0x69, 0x6f, 0x6e, 0x53, 0x68, 0x61, 0x32, + 0x35, 0x36, 0x12, 0x1f, 0x0a, 0x0b, 0x63, 0x6c, 0x74, 0x76, 0x5f, 0x65, 0x78, 0x70, 0x69, 0x72, + 0x79, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x63, 0x6c, 0x74, 0x76, 0x45, 0x78, 0x70, + 0x69, 0x72, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x18, 0x07, 0x20, 0x01, + 0x28, 0x0d, 0x52, 0x05, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x12, 0x30, 0x0a, 0x14, 0x66, 0x61, 0x69, + 0x6c, 0x75, 0x72, 0x65, 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x69, 0x6e, 0x64, 0x65, + 0x78, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x12, 0x66, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, + 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x16, 0x0a, 0x06, 0x68, + 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x68, 0x65, 0x69, + 0x67, 0x68, 0x74, 0x22, 0x8b, 0x06, 0x0a, 0x0b, 0x46, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x43, + 0x6f, 0x64, 0x65, 0x12, 0x0c, 0x0a, 0x08, 0x52, 0x45, 0x53, 0x45, 0x52, 0x56, 0x45, 0x44, 0x10, + 0x00, 0x12, 0x28, 0x0a, 0x24, 0x49, 0x4e, 0x43, 0x4f, 0x52, 0x52, 0x45, 0x43, 0x54, 0x5f, 0x4f, + 0x52, 0x5f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x5f, 0x50, 0x41, 0x59, 0x4d, 0x45, 0x4e, + 0x54, 0x5f, 0x44, 0x45, 0x54, 0x41, 0x49, 0x4c, 0x53, 0x10, 0x01, 0x12, 0x1c, 0x0a, 0x18, 0x49, + 0x4e, 0x43, 0x4f, 0x52, 0x52, 0x45, 0x43, 0x54, 0x5f, 0x50, 0x41, 0x59, 0x4d, 0x45, 0x4e, 0x54, + 0x5f, 0x41, 0x4d, 0x4f, 0x55, 0x4e, 0x54, 0x10, 0x02, 0x12, 0x1f, 0x0a, 0x1b, 0x46, 0x49, 0x4e, + 0x41, 0x4c, 0x5f, 0x49, 0x4e, 0x43, 0x4f, 0x52, 0x52, 0x45, 0x43, 0x54, 0x5f, 0x43, 0x4c, 0x54, + 0x56, 0x5f, 0x45, 0x58, 0x50, 0x49, 0x52, 0x59, 0x10, 0x03, 0x12, 0x1f, 0x0a, 0x1b, 0x46, 0x49, + 0x4e, 0x41, 0x4c, 0x5f, 0x49, 0x4e, 0x43, 0x4f, 0x52, 0x52, 0x45, 0x43, 0x54, 0x5f, 0x48, 0x54, + 0x4c, 0x43, 0x5f, 0x41, 0x4d, 0x4f, 0x55, 0x4e, 0x54, 0x10, 0x04, 0x12, 0x19, 0x0a, 0x15, 0x46, + 0x49, 0x4e, 0x41, 0x4c, 0x5f, 0x45, 0x58, 0x50, 0x49, 0x52, 0x59, 0x5f, 0x54, 0x4f, 0x4f, 0x5f, + 0x53, 0x4f, 0x4f, 0x4e, 0x10, 0x05, 0x12, 0x11, 0x0a, 0x0d, 0x49, 0x4e, 0x56, 0x41, 0x4c, 0x49, + 0x44, 0x5f, 0x52, 0x45, 0x41, 0x4c, 0x4d, 0x10, 0x06, 0x12, 0x13, 0x0a, 0x0f, 0x45, 0x58, 0x50, + 0x49, 0x52, 0x59, 0x5f, 0x54, 0x4f, 0x4f, 0x5f, 0x53, 0x4f, 0x4f, 0x4e, 0x10, 0x07, 0x12, 0x19, + 0x0a, 0x15, 0x49, 0x4e, 0x56, 0x41, 0x4c, 0x49, 0x44, 0x5f, 0x4f, 0x4e, 0x49, 0x4f, 0x4e, 0x5f, + 0x56, 0x45, 0x52, 0x53, 0x49, 0x4f, 0x4e, 0x10, 0x08, 0x12, 0x16, 0x0a, 0x12, 0x49, 0x4e, 0x56, + 0x41, 0x4c, 0x49, 0x44, 0x5f, 0x4f, 0x4e, 0x49, 0x4f, 0x4e, 0x5f, 0x48, 0x4d, 0x41, 0x43, 0x10, + 0x09, 0x12, 0x15, 0x0a, 0x11, 0x49, 0x4e, 0x56, 0x41, 0x4c, 0x49, 0x44, 0x5f, 0x4f, 0x4e, 0x49, + 0x4f, 0x4e, 0x5f, 0x4b, 0x45, 0x59, 0x10, 0x0a, 0x12, 0x18, 0x0a, 0x14, 0x41, 0x4d, 0x4f, 0x55, + 0x4e, 0x54, 0x5f, 0x42, 0x45, 0x4c, 0x4f, 0x57, 0x5f, 0x4d, 0x49, 0x4e, 0x49, 0x4d, 0x55, 0x4d, + 0x10, 0x0b, 0x12, 0x14, 0x0a, 0x10, 0x46, 0x45, 0x45, 0x5f, 0x49, 0x4e, 0x53, 0x55, 0x46, 0x46, + 0x49, 0x43, 0x49, 0x45, 0x4e, 0x54, 0x10, 0x0c, 0x12, 0x19, 0x0a, 0x15, 0x49, 0x4e, 0x43, 0x4f, + 0x52, 0x52, 0x45, 0x43, 0x54, 0x5f, 0x43, 0x4c, 0x54, 0x56, 0x5f, 0x45, 0x58, 0x50, 0x49, 0x52, + 0x59, 0x10, 0x0d, 0x12, 0x14, 0x0a, 0x10, 0x43, 0x48, 0x41, 0x4e, 0x4e, 0x45, 0x4c, 0x5f, 0x44, + 0x49, 0x53, 0x41, 0x42, 0x4c, 0x45, 0x44, 0x10, 0x0e, 0x12, 0x1d, 0x0a, 0x19, 0x54, 0x45, 0x4d, + 0x50, 0x4f, 0x52, 0x41, 0x52, 0x59, 0x5f, 0x43, 0x48, 0x41, 0x4e, 0x4e, 0x45, 0x4c, 0x5f, 0x46, + 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x10, 0x0f, 0x12, 0x21, 0x0a, 0x1d, 0x52, 0x45, 0x51, 0x55, + 0x49, 0x52, 0x45, 0x44, 0x5f, 0x4e, 0x4f, 0x44, 0x45, 0x5f, 0x46, 0x45, 0x41, 0x54, 0x55, 0x52, + 0x45, 0x5f, 0x4d, 0x49, 0x53, 0x53, 0x49, 0x4e, 0x47, 0x10, 0x10, 0x12, 0x24, 0x0a, 0x20, 0x52, + 0x45, 0x51, 0x55, 0x49, 0x52, 0x45, 0x44, 0x5f, 0x43, 0x48, 0x41, 0x4e, 0x4e, 0x45, 0x4c, 0x5f, + 0x46, 0x45, 0x41, 0x54, 0x55, 0x52, 0x45, 0x5f, 0x4d, 0x49, 0x53, 0x53, 0x49, 0x4e, 0x47, 0x10, + 0x11, 0x12, 0x15, 0x0a, 0x11, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x5f, 0x4e, 0x45, 0x58, + 0x54, 0x5f, 0x50, 0x45, 0x45, 0x52, 0x10, 0x12, 0x12, 0x1a, 0x0a, 0x16, 0x54, 0x45, 0x4d, 0x50, + 0x4f, 0x52, 0x41, 0x52, 0x59, 0x5f, 0x4e, 0x4f, 0x44, 0x45, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x55, + 0x52, 0x45, 0x10, 0x13, 0x12, 0x1a, 0x0a, 0x16, 0x50, 0x45, 0x52, 0x4d, 0x41, 0x4e, 0x45, 0x4e, + 0x54, 0x5f, 0x4e, 0x4f, 0x44, 0x45, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x10, 0x14, + 0x12, 0x1d, 0x0a, 0x19, 0x50, 0x45, 0x52, 0x4d, 0x41, 0x4e, 0x45, 0x4e, 0x54, 0x5f, 0x43, 0x48, + 0x41, 0x4e, 0x4e, 0x45, 0x4c, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x10, 0x15, 0x12, + 0x12, 0x0a, 0x0e, 0x45, 0x58, 0x50, 0x49, 0x52, 0x59, 0x5f, 0x54, 0x4f, 0x4f, 0x5f, 0x46, 0x41, + 0x52, 0x10, 0x16, 0x12, 0x0f, 0x0a, 0x0b, 0x4d, 0x50, 0x50, 0x5f, 0x54, 0x49, 0x4d, 0x45, 0x4f, + 0x55, 0x54, 0x10, 0x17, 0x12, 0x19, 0x0a, 0x15, 0x49, 0x4e, 0x56, 0x41, 0x4c, 0x49, 0x44, 0x5f, + 0x4f, 0x4e, 0x49, 0x4f, 0x4e, 0x5f, 0x50, 0x41, 0x59, 0x4c, 0x4f, 0x41, 0x44, 0x10, 0x18, 0x12, + 0x1a, 0x0a, 0x16, 0x49, 0x4e, 0x56, 0x41, 0x4c, 0x49, 0x44, 0x5f, 0x4f, 0x4e, 0x49, 0x4f, 0x4e, + 0x5f, 0x42, 0x4c, 0x49, 0x4e, 0x44, 0x49, 0x4e, 0x47, 0x10, 0x19, 0x12, 0x15, 0x0a, 0x10, 0x49, + 0x4e, 0x54, 0x45, 0x52, 0x4e, 0x41, 0x4c, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x10, + 0xe5, 0x07, 0x12, 0x14, 0x0a, 0x0f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x5f, 0x46, 0x41, + 0x49, 0x4c, 0x55, 0x52, 0x45, 0x10, 0xe6, 0x07, 0x12, 0x17, 0x0a, 0x12, 0x55, 0x4e, 0x52, 0x45, + 0x41, 0x44, 0x41, 0x42, 0x4c, 0x45, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x10, 0xe7, + 0x07, 0x4a, 0x04, 0x08, 0x02, 0x10, 0x03, 0x22, 0xb3, 0x03, 0x0a, 0x0d, 0x43, 0x68, 0x61, 0x6e, + 0x6e, 0x65, 0x6c, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x69, 0x67, + 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x73, 0x69, + 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x68, 0x61, 0x69, 0x6e, + 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x63, 0x68, 0x61, + 0x69, 0x6e, 0x48, 0x61, 0x73, 0x68, 0x12, 0x1b, 0x0a, 0x07, 0x63, 0x68, 0x61, 0x6e, 0x5f, 0x69, + 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x42, 0x02, 0x30, 0x01, 0x52, 0x06, 0x63, 0x68, 0x61, + 0x6e, 0x49, 0x64, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, + 0x70, 0x12, 0x23, 0x0a, 0x0d, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x66, 0x6c, 0x61, + 0x67, 0x73, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0c, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, + 0x65, 0x46, 0x6c, 0x61, 0x67, 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, + 0x6c, 0x5f, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0c, 0x63, + 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x46, 0x6c, 0x61, 0x67, 0x73, 0x12, 0x26, 0x0a, 0x0f, 0x74, + 0x69, 0x6d, 0x65, 0x5f, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x18, 0x06, + 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0d, 0x74, 0x69, 0x6d, 0x65, 0x4c, 0x6f, 0x63, 0x6b, 0x44, 0x65, + 0x6c, 0x74, 0x61, 0x12, 0x2a, 0x0a, 0x11, 0x68, 0x74, 0x6c, 0x63, 0x5f, 0x6d, 0x69, 0x6e, 0x69, + 0x6d, 0x75, 0x6d, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0f, + 0x68, 0x74, 0x6c, 0x63, 0x4d, 0x69, 0x6e, 0x69, 0x6d, 0x75, 0x6d, 0x4d, 0x73, 0x61, 0x74, 0x12, + 0x19, 0x0a, 0x08, 0x62, 0x61, 0x73, 0x65, 0x5f, 0x66, 0x65, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, + 0x0d, 0x52, 0x07, 0x62, 0x61, 0x73, 0x65, 0x46, 0x65, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x66, 0x65, + 0x65, 0x5f, 0x72, 0x61, 0x74, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x66, 0x65, + 0x65, 0x52, 0x61, 0x74, 0x65, 0x12, 0x2a, 0x0a, 0x11, 0x68, 0x74, 0x6c, 0x63, 0x5f, 0x6d, 0x61, + 0x78, 0x69, 0x6d, 0x75, 0x6d, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x04, + 0x52, 0x0f, 0x68, 0x74, 0x6c, 0x63, 0x4d, 0x61, 0x78, 0x69, 0x6d, 0x75, 0x6d, 0x4d, 0x73, 0x61, + 0x74, 0x12, 0x2a, 0x0a, 0x11, 0x65, 0x78, 0x74, 0x72, 0x61, 0x5f, 0x6f, 0x70, 0x61, 0x71, 0x75, + 0x65, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0f, 0x65, 0x78, + 0x74, 0x72, 0x61, 0x4f, 0x70, 0x61, 0x71, 0x75, 0x65, 0x44, 0x61, 0x74, 0x61, 0x22, 0x5d, 0x0a, + 0x0a, 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x6e, + 0x6f, 0x6e, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x6e, 0x6f, 0x6e, 0x63, + 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x49, 0x64, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x49, 0x64, 0x12, + 0x1b, 0x0a, 0x03, 0x6f, 0x70, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x6c, + 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4f, 0x70, 0x52, 0x03, 0x6f, 0x70, 0x73, 0x22, 0x36, 0x0a, 0x02, + 0x4f, 0x70, 0x12, 0x16, 0x0a, 0x06, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x06, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x63, + 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x61, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x8e, 0x01, 0x0a, 0x13, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x4d, 0x61, + 0x63, 0x50, 0x65, 0x72, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, + 0x6d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, + 0x6d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x12, 0x3b, 0x0a, 0x0b, 0x70, 0x65, 0x72, 0x6d, + 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, + 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x50, 0x65, + 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x0b, 0x70, 0x65, 0x72, 0x6d, 0x69, 0x73, + 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x1e, 0x0a, 0x0a, 0x66, 0x75, 0x6c, 0x6c, 0x4d, 0x65, 0x74, + 0x68, 0x6f, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x66, 0x75, 0x6c, 0x6c, 0x4d, + 0x65, 0x74, 0x68, 0x6f, 0x64, 0x22, 0x2c, 0x0a, 0x14, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x4d, 0x61, + 0x63, 0x50, 0x65, 0x72, 0x6d, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x14, 0x0a, + 0x05, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x76, 0x61, + 0x6c, 0x69, 0x64, 0x22, 0xf4, 0x02, 0x0a, 0x14, 0x52, 0x50, 0x43, 0x4d, 0x69, 0x64, 0x64, 0x6c, + 0x65, 0x77, 0x61, 0x72, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, + 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, + 0x52, 0x09, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x49, 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x72, + 0x61, 0x77, 0x5f, 0x6d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0c, 0x52, 0x0b, 0x72, 0x61, 0x77, 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x12, 0x36, + 0x0a, 0x17, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5f, 0x63, 0x61, 0x76, 0x65, 0x61, 0x74, 0x5f, + 0x63, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x15, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x43, 0x61, 0x76, 0x65, 0x61, 0x74, 0x43, 0x6f, 0x6e, + 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x34, 0x0a, 0x0b, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, + 0x5f, 0x61, 0x75, 0x74, 0x68, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x6c, 0x6e, + 0x72, 0x70, 0x63, 0x2e, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x41, 0x75, 0x74, 0x68, 0x48, 0x00, + 0x52, 0x0a, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x41, 0x75, 0x74, 0x68, 0x12, 0x2d, 0x0a, 0x07, + 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, + 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x50, 0x43, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, + 0x48, 0x00, 0x52, 0x07, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2f, 0x0a, 0x08, 0x72, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, + 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x50, 0x43, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, + 0x48, 0x00, 0x52, 0x08, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x23, 0x0a, 0x0c, + 0x72, 0x65, 0x67, 0x5f, 0x63, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x18, 0x08, 0x20, 0x01, + 0x28, 0x08, 0x48, 0x00, 0x52, 0x0b, 0x72, 0x65, 0x67, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, + 0x65, 0x12, 0x15, 0x0a, 0x06, 0x6d, 0x73, 0x67, 0x5f, 0x69, 0x64, 0x18, 0x07, 0x20, 0x01, 0x28, + 0x04, 0x52, 0x05, 0x6d, 0x73, 0x67, 0x49, 0x64, 0x42, 0x10, 0x0a, 0x0e, 0x69, 0x6e, 0x74, 0x65, + 0x72, 0x63, 0x65, 0x70, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x22, 0x34, 0x0a, 0x0a, 0x53, 0x74, + 0x72, 0x65, 0x61, 0x6d, 0x41, 0x75, 0x74, 0x68, 0x12, 0x26, 0x0a, 0x0f, 0x6d, 0x65, 0x74, 0x68, + 0x6f, 0x64, 0x5f, 0x66, 0x75, 0x6c, 0x6c, 0x5f, 0x75, 0x72, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0d, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x46, 0x75, 0x6c, 0x6c, 0x55, 0x72, 0x69, + 0x22, 0xab, 0x01, 0x0a, 0x0a, 0x52, 0x50, 0x43, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, + 0x26, 0x0a, 0x0f, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x5f, 0x66, 0x75, 0x6c, 0x6c, 0x5f, 0x75, + 0x72, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, + 0x46, 0x75, 0x6c, 0x6c, 0x55, 0x72, 0x69, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x74, 0x72, 0x65, 0x61, + 0x6d, 0x5f, 0x72, 0x70, 0x63, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x73, 0x74, 0x72, + 0x65, 0x61, 0x6d, 0x52, 0x70, 0x63, 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x6e, + 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x79, 0x70, 0x65, 0x4e, + 0x61, 0x6d, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, + 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, + 0x7a, 0x65, 0x64, 0x12, 0x19, 0x0a, 0x08, 0x69, 0x73, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, + 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x69, 0x73, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x22, 0xc0, + 0x01, 0x0a, 0x15, 0x52, 0x50, 0x43, 0x4d, 0x69, 0x64, 0x64, 0x6c, 0x65, 0x77, 0x61, 0x72, 0x65, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1c, 0x0a, 0x0a, 0x72, 0x65, 0x66, 0x5f, + 0x6d, 0x73, 0x67, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x72, 0x65, + 0x66, 0x4d, 0x73, 0x67, 0x49, 0x64, 0x12, 0x3b, 0x0a, 0x08, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, + 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, + 0x2e, 0x4d, 0x69, 0x64, 0x64, 0x6c, 0x65, 0x77, 0x61, 0x72, 0x65, 0x52, 0x65, 0x67, 0x69, 0x73, + 0x74, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x00, 0x52, 0x08, 0x72, 0x65, 0x67, 0x69, 0x73, + 0x74, 0x65, 0x72, 0x12, 0x36, 0x0a, 0x08, 0x66, 0x65, 0x65, 0x64, 0x62, 0x61, 0x63, 0x6b, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x49, 0x6e, + 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x46, 0x65, 0x65, 0x64, 0x62, 0x61, 0x63, 0x6b, 0x48, + 0x00, 0x52, 0x08, 0x66, 0x65, 0x65, 0x64, 0x62, 0x61, 0x63, 0x6b, 0x42, 0x14, 0x0a, 0x12, 0x6d, + 0x69, 0x64, 0x64, 0x6c, 0x65, 0x77, 0x61, 0x72, 0x65, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, + 0x65, 0x22, 0xa6, 0x01, 0x0a, 0x16, 0x4d, 0x69, 0x64, 0x64, 0x6c, 0x65, 0x77, 0x61, 0x72, 0x65, + 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x27, 0x0a, 0x0f, + 0x6d, 0x69, 0x64, 0x64, 0x6c, 0x65, 0x77, 0x61, 0x72, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x6d, 0x69, 0x64, 0x64, 0x6c, 0x65, 0x77, 0x61, 0x72, + 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x3d, 0x0a, 0x1b, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5f, + 0x6d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x5f, 0x63, 0x61, 0x76, 0x65, 0x61, 0x74, 0x5f, + 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x18, 0x63, 0x75, 0x73, 0x74, + 0x6f, 0x6d, 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x43, 0x61, 0x76, 0x65, 0x61, 0x74, + 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x24, 0x0a, 0x0e, 0x72, 0x65, 0x61, 0x64, 0x5f, 0x6f, 0x6e, 0x6c, + 0x79, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x72, 0x65, + 0x61, 0x64, 0x4f, 0x6e, 0x6c, 0x79, 0x4d, 0x6f, 0x64, 0x65, 0x22, 0x8b, 0x01, 0x0a, 0x11, 0x49, + 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x46, 0x65, 0x65, 0x64, 0x62, 0x61, 0x63, 0x6b, + 0x12, 0x14, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x29, 0x0a, 0x10, 0x72, 0x65, 0x70, 0x6c, 0x61, 0x63, + 0x65, 0x5f, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, + 0x52, 0x0f, 0x72, 0x65, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x35, 0x0a, 0x16, 0x72, 0x65, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x6d, 0x65, 0x6e, 0x74, + 0x5f, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x0c, 0x52, 0x15, 0x72, 0x65, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x53, 0x65, + 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x2a, 0xcb, 0x02, 0x0a, 0x10, 0x4f, 0x75, 0x74, + 0x70, 0x75, 0x74, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1b, 0x0a, + 0x17, 0x53, 0x43, 0x52, 0x49, 0x50, 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x50, 0x55, 0x42, + 0x4b, 0x45, 0x59, 0x5f, 0x48, 0x41, 0x53, 0x48, 0x10, 0x00, 0x12, 0x1b, 0x0a, 0x17, 0x53, 0x43, + 0x52, 0x49, 0x50, 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x53, 0x43, 0x52, 0x49, 0x50, 0x54, + 0x5f, 0x48, 0x41, 0x53, 0x48, 0x10, 0x01, 0x12, 0x26, 0x0a, 0x22, 0x53, 0x43, 0x52, 0x49, 0x50, + 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x57, 0x49, 0x54, 0x4e, 0x45, 0x53, 0x53, 0x5f, 0x56, + 0x30, 0x5f, 0x50, 0x55, 0x42, 0x4b, 0x45, 0x59, 0x5f, 0x48, 0x41, 0x53, 0x48, 0x10, 0x02, 0x12, + 0x26, 0x0a, 0x22, 0x53, 0x43, 0x52, 0x49, 0x50, 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x57, + 0x49, 0x54, 0x4e, 0x45, 0x53, 0x53, 0x5f, 0x56, 0x30, 0x5f, 0x53, 0x43, 0x52, 0x49, 0x50, 0x54, + 0x5f, 0x48, 0x41, 0x53, 0x48, 0x10, 0x03, 0x12, 0x16, 0x0a, 0x12, 0x53, 0x43, 0x52, 0x49, 0x50, + 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x50, 0x55, 0x42, 0x4b, 0x45, 0x59, 0x10, 0x04, 0x12, + 0x18, 0x0a, 0x14, 0x53, 0x43, 0x52, 0x49, 0x50, 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x4d, + 0x55, 0x4c, 0x54, 0x49, 0x53, 0x49, 0x47, 0x10, 0x05, 0x12, 0x18, 0x0a, 0x14, 0x53, 0x43, 0x52, + 0x49, 0x50, 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x4e, 0x55, 0x4c, 0x4c, 0x44, 0x41, 0x54, + 0x41, 0x10, 0x06, 0x12, 0x1c, 0x0a, 0x18, 0x53, 0x43, 0x52, 0x49, 0x50, 0x54, 0x5f, 0x54, 0x59, + 0x50, 0x45, 0x5f, 0x4e, 0x4f, 0x4e, 0x5f, 0x53, 0x54, 0x41, 0x4e, 0x44, 0x41, 0x52, 0x44, 0x10, + 0x07, 0x12, 0x1f, 0x0a, 0x1b, 0x53, 0x43, 0x52, 0x49, 0x50, 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, + 0x5f, 0x57, 0x49, 0x54, 0x4e, 0x45, 0x53, 0x53, 0x5f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, + 0x10, 0x08, 0x12, 0x22, 0x0a, 0x1e, 0x53, 0x43, 0x52, 0x49, 0x50, 0x54, 0x5f, 0x54, 0x59, 0x50, + 0x45, 0x5f, 0x57, 0x49, 0x54, 0x4e, 0x45, 0x53, 0x53, 0x5f, 0x56, 0x31, 0x5f, 0x54, 0x41, 0x50, + 0x52, 0x4f, 0x4f, 0x54, 0x10, 0x09, 0x2a, 0x62, 0x0a, 0x15, 0x43, 0x6f, 0x69, 0x6e, 0x53, 0x65, + 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x12, + 0x1e, 0x0a, 0x1a, 0x53, 0x54, 0x52, 0x41, 0x54, 0x45, 0x47, 0x59, 0x5f, 0x55, 0x53, 0x45, 0x5f, + 0x47, 0x4c, 0x4f, 0x42, 0x41, 0x4c, 0x5f, 0x43, 0x4f, 0x4e, 0x46, 0x49, 0x47, 0x10, 0x00, 0x12, + 0x14, 0x0a, 0x10, 0x53, 0x54, 0x52, 0x41, 0x54, 0x45, 0x47, 0x59, 0x5f, 0x4c, 0x41, 0x52, 0x47, + 0x45, 0x53, 0x54, 0x10, 0x01, 0x12, 0x13, 0x0a, 0x0f, 0x53, 0x54, 0x52, 0x41, 0x54, 0x45, 0x47, + 0x59, 0x5f, 0x52, 0x41, 0x4e, 0x44, 0x4f, 0x4d, 0x10, 0x02, 0x2a, 0xac, 0x01, 0x0a, 0x0b, 0x41, + 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x54, 0x79, 0x70, 0x65, 0x12, 0x17, 0x0a, 0x13, 0x57, 0x49, + 0x54, 0x4e, 0x45, 0x53, 0x53, 0x5f, 0x50, 0x55, 0x42, 0x4b, 0x45, 0x59, 0x5f, 0x48, 0x41, 0x53, + 0x48, 0x10, 0x00, 0x12, 0x16, 0x0a, 0x12, 0x4e, 0x45, 0x53, 0x54, 0x45, 0x44, 0x5f, 0x50, 0x55, + 0x42, 0x4b, 0x45, 0x59, 0x5f, 0x48, 0x41, 0x53, 0x48, 0x10, 0x01, 0x12, 0x1e, 0x0a, 0x1a, 0x55, + 0x4e, 0x55, 0x53, 0x45, 0x44, 0x5f, 0x57, 0x49, 0x54, 0x4e, 0x45, 0x53, 0x53, 0x5f, 0x50, 0x55, + 0x42, 0x4b, 0x45, 0x59, 0x5f, 0x48, 0x41, 0x53, 0x48, 0x10, 0x02, 0x12, 0x1d, 0x0a, 0x19, 0x55, + 0x4e, 0x55, 0x53, 0x45, 0x44, 0x5f, 0x4e, 0x45, 0x53, 0x54, 0x45, 0x44, 0x5f, 0x50, 0x55, 0x42, + 0x4b, 0x45, 0x59, 0x5f, 0x48, 0x41, 0x53, 0x48, 0x10, 0x03, 0x12, 0x12, 0x0a, 0x0e, 0x54, 0x41, + 0x50, 0x52, 0x4f, 0x4f, 0x54, 0x5f, 0x50, 0x55, 0x42, 0x4b, 0x45, 0x59, 0x10, 0x04, 0x12, 0x19, + 0x0a, 0x15, 0x55, 0x4e, 0x55, 0x53, 0x45, 0x44, 0x5f, 0x54, 0x41, 0x50, 0x52, 0x4f, 0x4f, 0x54, + 0x5f, 0x50, 0x55, 0x42, 0x4b, 0x45, 0x59, 0x10, 0x05, 0x2a, 0x8c, 0x01, 0x0a, 0x0e, 0x43, 0x6f, + 0x6d, 0x6d, 0x69, 0x74, 0x6d, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1b, 0x0a, 0x17, + 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x5f, 0x43, 0x4f, 0x4d, 0x4d, 0x49, 0x54, 0x4d, 0x45, + 0x4e, 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x4c, 0x45, 0x47, + 0x41, 0x43, 0x59, 0x10, 0x01, 0x12, 0x15, 0x0a, 0x11, 0x53, 0x54, 0x41, 0x54, 0x49, 0x43, 0x5f, + 0x52, 0x45, 0x4d, 0x4f, 0x54, 0x45, 0x5f, 0x4b, 0x45, 0x59, 0x10, 0x02, 0x12, 0x0b, 0x0a, 0x07, + 0x41, 0x4e, 0x43, 0x48, 0x4f, 0x52, 0x53, 0x10, 0x03, 0x12, 0x19, 0x0a, 0x15, 0x53, 0x43, 0x52, + 0x49, 0x50, 0x54, 0x5f, 0x45, 0x4e, 0x46, 0x4f, 0x52, 0x43, 0x45, 0x44, 0x5f, 0x4c, 0x45, 0x41, + 0x53, 0x45, 0x10, 0x04, 0x12, 0x12, 0x0a, 0x0e, 0x53, 0x49, 0x4d, 0x50, 0x4c, 0x45, 0x5f, 0x54, + 0x41, 0x50, 0x52, 0x4f, 0x4f, 0x54, 0x10, 0x05, 0x2a, 0x61, 0x0a, 0x09, 0x49, 0x6e, 0x69, 0x74, + 0x69, 0x61, 0x74, 0x6f, 0x72, 0x12, 0x15, 0x0a, 0x11, 0x49, 0x4e, 0x49, 0x54, 0x49, 0x41, 0x54, + 0x4f, 0x52, 0x5f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x13, 0x0a, 0x0f, + 0x49, 0x4e, 0x49, 0x54, 0x49, 0x41, 0x54, 0x4f, 0x52, 0x5f, 0x4c, 0x4f, 0x43, 0x41, 0x4c, 0x10, + 0x01, 0x12, 0x14, 0x0a, 0x10, 0x49, 0x4e, 0x49, 0x54, 0x49, 0x41, 0x54, 0x4f, 0x52, 0x5f, 0x52, + 0x45, 0x4d, 0x4f, 0x54, 0x45, 0x10, 0x02, 0x12, 0x12, 0x0a, 0x0e, 0x49, 0x4e, 0x49, 0x54, 0x49, + 0x41, 0x54, 0x4f, 0x52, 0x5f, 0x42, 0x4f, 0x54, 0x48, 0x10, 0x03, 0x2a, 0x60, 0x0a, 0x0e, 0x52, + 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x12, 0x10, 0x0a, + 0x0c, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, + 0x0a, 0x0a, 0x06, 0x41, 0x4e, 0x43, 0x48, 0x4f, 0x52, 0x10, 0x01, 0x12, 0x11, 0x0a, 0x0d, 0x49, + 0x4e, 0x43, 0x4f, 0x4d, 0x49, 0x4e, 0x47, 0x5f, 0x48, 0x54, 0x4c, 0x43, 0x10, 0x02, 0x12, 0x11, + 0x0a, 0x0d, 0x4f, 0x55, 0x54, 0x47, 0x4f, 0x49, 0x4e, 0x47, 0x5f, 0x48, 0x54, 0x4c, 0x43, 0x10, + 0x03, 0x12, 0x0a, 0x0a, 0x06, 0x43, 0x4f, 0x4d, 0x4d, 0x49, 0x54, 0x10, 0x04, 0x2a, 0x71, 0x0a, + 0x11, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x4f, 0x75, 0x74, 0x63, 0x6f, + 0x6d, 0x65, 0x12, 0x13, 0x0a, 0x0f, 0x4f, 0x55, 0x54, 0x43, 0x4f, 0x4d, 0x45, 0x5f, 0x55, 0x4e, + 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x43, 0x4c, 0x41, 0x49, 0x4d, + 0x45, 0x44, 0x10, 0x01, 0x12, 0x0d, 0x0a, 0x09, 0x55, 0x4e, 0x43, 0x4c, 0x41, 0x49, 0x4d, 0x45, + 0x44, 0x10, 0x02, 0x12, 0x0d, 0x0a, 0x09, 0x41, 0x42, 0x41, 0x4e, 0x44, 0x4f, 0x4e, 0x45, 0x44, + 0x10, 0x03, 0x12, 0x0f, 0x0a, 0x0b, 0x46, 0x49, 0x52, 0x53, 0x54, 0x5f, 0x53, 0x54, 0x41, 0x47, + 0x45, 0x10, 0x04, 0x12, 0x0b, 0x0a, 0x07, 0x54, 0x49, 0x4d, 0x45, 0x4f, 0x55, 0x54, 0x10, 0x05, + 0x2a, 0x39, 0x0a, 0x0e, 0x4e, 0x6f, 0x64, 0x65, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x54, 0x79, + 0x70, 0x65, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, + 0x1a, 0x0a, 0x16, 0x42, 0x45, 0x54, 0x57, 0x45, 0x45, 0x4e, 0x4e, 0x45, 0x53, 0x53, 0x5f, 0x43, + 0x45, 0x4e, 0x54, 0x52, 0x41, 0x4c, 0x49, 0x54, 0x59, 0x10, 0x01, 0x2a, 0x3b, 0x0a, 0x10, 0x49, + 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x48, 0x54, 0x4c, 0x43, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, + 0x0c, 0x0a, 0x08, 0x41, 0x43, 0x43, 0x45, 0x50, 0x54, 0x45, 0x44, 0x10, 0x00, 0x12, 0x0b, 0x0a, + 0x07, 0x53, 0x45, 0x54, 0x54, 0x4c, 0x45, 0x44, 0x10, 0x01, 0x12, 0x0c, 0x0a, 0x08, 0x43, 0x41, + 0x4e, 0x43, 0x45, 0x4c, 0x45, 0x44, 0x10, 0x02, 0x2a, 0xf6, 0x01, 0x0a, 0x14, 0x50, 0x61, 0x79, + 0x6d, 0x65, 0x6e, 0x74, 0x46, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x52, 0x65, 0x61, 0x73, 0x6f, + 0x6e, 0x12, 0x17, 0x0a, 0x13, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x52, 0x45, 0x41, + 0x53, 0x4f, 0x4e, 0x5f, 0x4e, 0x4f, 0x4e, 0x45, 0x10, 0x00, 0x12, 0x1a, 0x0a, 0x16, 0x46, 0x41, + 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x54, 0x49, 0x4d, + 0x45, 0x4f, 0x55, 0x54, 0x10, 0x01, 0x12, 0x1b, 0x0a, 0x17, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, + 0x45, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x4e, 0x4f, 0x5f, 0x52, 0x4f, 0x55, 0x54, + 0x45, 0x10, 0x02, 0x12, 0x18, 0x0a, 0x14, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x52, + 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, 0x03, 0x12, 0x2c, 0x0a, + 0x28, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, + 0x49, 0x4e, 0x43, 0x4f, 0x52, 0x52, 0x45, 0x43, 0x54, 0x5f, 0x50, 0x41, 0x59, 0x4d, 0x45, 0x4e, + 0x54, 0x5f, 0x44, 0x45, 0x54, 0x41, 0x49, 0x4c, 0x53, 0x10, 0x04, 0x12, 0x27, 0x0a, 0x23, 0x46, + 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x49, 0x4e, + 0x53, 0x55, 0x46, 0x46, 0x49, 0x43, 0x49, 0x45, 0x4e, 0x54, 0x5f, 0x42, 0x41, 0x4c, 0x41, 0x4e, + 0x43, 0x45, 0x10, 0x05, 0x12, 0x1b, 0x0a, 0x17, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, + 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x43, 0x41, 0x4e, 0x43, 0x45, 0x4c, 0x45, 0x44, 0x10, + 0x06, 0x2a, 0x89, 0x05, 0x0a, 0x0a, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x42, 0x69, 0x74, + 0x12, 0x18, 0x0a, 0x14, 0x44, 0x41, 0x54, 0x41, 0x4c, 0x4f, 0x53, 0x53, 0x5f, 0x50, 0x52, 0x4f, + 0x54, 0x45, 0x43, 0x54, 0x5f, 0x52, 0x45, 0x51, 0x10, 0x00, 0x12, 0x18, 0x0a, 0x14, 0x44, 0x41, + 0x54, 0x41, 0x4c, 0x4f, 0x53, 0x53, 0x5f, 0x50, 0x52, 0x4f, 0x54, 0x45, 0x43, 0x54, 0x5f, 0x4f, + 0x50, 0x54, 0x10, 0x01, 0x12, 0x17, 0x0a, 0x13, 0x49, 0x4e, 0x49, 0x54, 0x49, 0x41, 0x4c, 0x5f, + 0x52, 0x4f, 0x55, 0x49, 0x4e, 0x47, 0x5f, 0x53, 0x59, 0x4e, 0x43, 0x10, 0x03, 0x12, 0x1f, 0x0a, + 0x1b, 0x55, 0x50, 0x46, 0x52, 0x4f, 0x4e, 0x54, 0x5f, 0x53, 0x48, 0x55, 0x54, 0x44, 0x4f, 0x57, + 0x4e, 0x5f, 0x53, 0x43, 0x52, 0x49, 0x50, 0x54, 0x5f, 0x52, 0x45, 0x51, 0x10, 0x04, 0x12, 0x1f, + 0x0a, 0x1b, 0x55, 0x50, 0x46, 0x52, 0x4f, 0x4e, 0x54, 0x5f, 0x53, 0x48, 0x55, 0x54, 0x44, 0x4f, + 0x57, 0x4e, 0x5f, 0x53, 0x43, 0x52, 0x49, 0x50, 0x54, 0x5f, 0x4f, 0x50, 0x54, 0x10, 0x05, 0x12, + 0x16, 0x0a, 0x12, 0x47, 0x4f, 0x53, 0x53, 0x49, 0x50, 0x5f, 0x51, 0x55, 0x45, 0x52, 0x49, 0x45, + 0x53, 0x5f, 0x52, 0x45, 0x51, 0x10, 0x06, 0x12, 0x16, 0x0a, 0x12, 0x47, 0x4f, 0x53, 0x53, 0x49, + 0x50, 0x5f, 0x51, 0x55, 0x45, 0x52, 0x49, 0x45, 0x53, 0x5f, 0x4f, 0x50, 0x54, 0x10, 0x07, 0x12, + 0x11, 0x0a, 0x0d, 0x54, 0x4c, 0x56, 0x5f, 0x4f, 0x4e, 0x49, 0x4f, 0x4e, 0x5f, 0x52, 0x45, 0x51, + 0x10, 0x08, 0x12, 0x11, 0x0a, 0x0d, 0x54, 0x4c, 0x56, 0x5f, 0x4f, 0x4e, 0x49, 0x4f, 0x4e, 0x5f, + 0x4f, 0x50, 0x54, 0x10, 0x09, 0x12, 0x1a, 0x0a, 0x16, 0x45, 0x58, 0x54, 0x5f, 0x47, 0x4f, 0x53, + 0x53, 0x49, 0x50, 0x5f, 0x51, 0x55, 0x45, 0x52, 0x49, 0x45, 0x53, 0x5f, 0x52, 0x45, 0x51, 0x10, + 0x0a, 0x12, 0x1a, 0x0a, 0x16, 0x45, 0x58, 0x54, 0x5f, 0x47, 0x4f, 0x53, 0x53, 0x49, 0x50, 0x5f, + 0x51, 0x55, 0x45, 0x52, 0x49, 0x45, 0x53, 0x5f, 0x4f, 0x50, 0x54, 0x10, 0x0b, 0x12, 0x19, 0x0a, + 0x15, 0x53, 0x54, 0x41, 0x54, 0x49, 0x43, 0x5f, 0x52, 0x45, 0x4d, 0x4f, 0x54, 0x45, 0x5f, 0x4b, + 0x45, 0x59, 0x5f, 0x52, 0x45, 0x51, 0x10, 0x0c, 0x12, 0x19, 0x0a, 0x15, 0x53, 0x54, 0x41, 0x54, + 0x49, 0x43, 0x5f, 0x52, 0x45, 0x4d, 0x4f, 0x54, 0x45, 0x5f, 0x4b, 0x45, 0x59, 0x5f, 0x4f, 0x50, + 0x54, 0x10, 0x0d, 0x12, 0x14, 0x0a, 0x10, 0x50, 0x41, 0x59, 0x4d, 0x45, 0x4e, 0x54, 0x5f, 0x41, + 0x44, 0x44, 0x52, 0x5f, 0x52, 0x45, 0x51, 0x10, 0x0e, 0x12, 0x14, 0x0a, 0x10, 0x50, 0x41, 0x59, + 0x4d, 0x45, 0x4e, 0x54, 0x5f, 0x41, 0x44, 0x44, 0x52, 0x5f, 0x4f, 0x50, 0x54, 0x10, 0x0f, 0x12, + 0x0b, 0x0a, 0x07, 0x4d, 0x50, 0x50, 0x5f, 0x52, 0x45, 0x51, 0x10, 0x10, 0x12, 0x0b, 0x0a, 0x07, + 0x4d, 0x50, 0x50, 0x5f, 0x4f, 0x50, 0x54, 0x10, 0x11, 0x12, 0x16, 0x0a, 0x12, 0x57, 0x55, 0x4d, + 0x42, 0x4f, 0x5f, 0x43, 0x48, 0x41, 0x4e, 0x4e, 0x45, 0x4c, 0x53, 0x5f, 0x52, 0x45, 0x51, 0x10, + 0x12, 0x12, 0x16, 0x0a, 0x12, 0x57, 0x55, 0x4d, 0x42, 0x4f, 0x5f, 0x43, 0x48, 0x41, 0x4e, 0x4e, + 0x45, 0x4c, 0x53, 0x5f, 0x4f, 0x50, 0x54, 0x10, 0x13, 0x12, 0x0f, 0x0a, 0x0b, 0x41, 0x4e, 0x43, + 0x48, 0x4f, 0x52, 0x53, 0x5f, 0x52, 0x45, 0x51, 0x10, 0x14, 0x12, 0x0f, 0x0a, 0x0b, 0x41, 0x4e, + 0x43, 0x48, 0x4f, 0x52, 0x53, 0x5f, 0x4f, 0x50, 0x54, 0x10, 0x15, 0x12, 0x1d, 0x0a, 0x19, 0x41, + 0x4e, 0x43, 0x48, 0x4f, 0x52, 0x53, 0x5f, 0x5a, 0x45, 0x52, 0x4f, 0x5f, 0x46, 0x45, 0x45, 0x5f, + 0x48, 0x54, 0x4c, 0x43, 0x5f, 0x52, 0x45, 0x51, 0x10, 0x16, 0x12, 0x1d, 0x0a, 0x19, 0x41, 0x4e, + 0x43, 0x48, 0x4f, 0x52, 0x53, 0x5f, 0x5a, 0x45, 0x52, 0x4f, 0x5f, 0x46, 0x45, 0x45, 0x5f, 0x48, + 0x54, 0x4c, 0x43, 0x5f, 0x4f, 0x50, 0x54, 0x10, 0x17, 0x12, 0x1b, 0x0a, 0x17, 0x52, 0x4f, 0x55, + 0x54, 0x45, 0x5f, 0x42, 0x4c, 0x49, 0x4e, 0x44, 0x49, 0x4e, 0x47, 0x5f, 0x52, 0x45, 0x51, 0x55, + 0x49, 0x52, 0x45, 0x44, 0x10, 0x18, 0x12, 0x1b, 0x0a, 0x17, 0x52, 0x4f, 0x55, 0x54, 0x45, 0x5f, + 0x42, 0x4c, 0x49, 0x4e, 0x44, 0x49, 0x4e, 0x47, 0x5f, 0x4f, 0x50, 0x54, 0x49, 0x4f, 0x4e, 0x41, + 0x4c, 0x10, 0x19, 0x12, 0x0b, 0x0a, 0x07, 0x41, 0x4d, 0x50, 0x5f, 0x52, 0x45, 0x51, 0x10, 0x1e, + 0x12, 0x0b, 0x0a, 0x07, 0x41, 0x4d, 0x50, 0x5f, 0x4f, 0x50, 0x54, 0x10, 0x1f, 0x2a, 0xac, 0x01, + 0x0a, 0x0d, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x46, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x12, + 0x1a, 0x0a, 0x16, 0x55, 0x50, 0x44, 0x41, 0x54, 0x45, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, + 0x45, 0x5f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x1a, 0x0a, 0x16, 0x55, + 0x50, 0x44, 0x41, 0x54, 0x45, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x50, 0x45, + 0x4e, 0x44, 0x49, 0x4e, 0x47, 0x10, 0x01, 0x12, 0x1c, 0x0a, 0x18, 0x55, 0x50, 0x44, 0x41, 0x54, + 0x45, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x4e, 0x4f, 0x54, 0x5f, 0x46, 0x4f, + 0x55, 0x4e, 0x44, 0x10, 0x02, 0x12, 0x1f, 0x0a, 0x1b, 0x55, 0x50, 0x44, 0x41, 0x54, 0x45, 0x5f, + 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x49, 0x4e, 0x54, 0x45, 0x52, 0x4e, 0x41, 0x4c, + 0x5f, 0x45, 0x52, 0x52, 0x10, 0x03, 0x12, 0x24, 0x0a, 0x20, 0x55, 0x50, 0x44, 0x41, 0x54, 0x45, + 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x49, 0x4e, 0x56, 0x41, 0x4c, 0x49, 0x44, + 0x5f, 0x50, 0x41, 0x52, 0x41, 0x4d, 0x45, 0x54, 0x45, 0x52, 0x10, 0x04, 0x32, 0xb9, 0x27, 0x0a, + 0x09, 0x4c, 0x69, 0x67, 0x68, 0x74, 0x6e, 0x69, 0x6e, 0x67, 0x12, 0x4a, 0x0a, 0x0d, 0x57, 0x61, + 0x6c, 0x6c, 0x65, 0x74, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x12, 0x1b, 0x2e, 0x6c, 0x6e, + 0x72, 0x70, 0x63, 0x2e, 0x57, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, + 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, + 0x2e, 0x57, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4d, 0x0a, 0x0e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, + 0x6c, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x12, 0x1c, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4b, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x54, 0x72, - 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x1d, 0x2e, 0x6c, 0x6e, 0x72, - 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, - 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, - 0x63, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x65, 0x74, - 0x61, 0x69, 0x6c, 0x73, 0x12, 0x44, 0x0a, 0x0b, 0x45, 0x73, 0x74, 0x69, 0x6d, 0x61, 0x74, 0x65, - 0x46, 0x65, 0x65, 0x12, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x45, 0x73, 0x74, 0x69, - 0x6d, 0x61, 0x74, 0x65, 0x46, 0x65, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, - 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x45, 0x73, 0x74, 0x69, 0x6d, 0x61, 0x74, 0x65, 0x46, - 0x65, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3e, 0x0a, 0x09, 0x53, 0x65, - 0x6e, 0x64, 0x43, 0x6f, 0x69, 0x6e, 0x73, 0x12, 0x17, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, - 0x53, 0x65, 0x6e, 0x64, 0x43, 0x6f, 0x69, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x18, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x43, 0x6f, 0x69, - 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x44, 0x0a, 0x0b, 0x4c, 0x69, - 0x73, 0x74, 0x55, 0x6e, 0x73, 0x70, 0x65, 0x6e, 0x74, 0x12, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, - 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x55, 0x6e, 0x73, 0x70, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, - 0x74, 0x55, 0x6e, 0x73, 0x70, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x12, 0x4c, 0x0a, 0x15, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x54, 0x72, 0x61, - 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x1d, 0x2e, 0x6c, 0x6e, 0x72, 0x70, - 0x63, 0x2e, 0x47, 0x65, 0x74, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, - 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x12, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, - 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x30, 0x01, 0x12, 0x3b, - 0x0a, 0x08, 0x53, 0x65, 0x6e, 0x64, 0x4d, 0x61, 0x6e, 0x79, 0x12, 0x16, 0x2e, 0x6c, 0x6e, 0x72, - 0x70, 0x63, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x4d, 0x61, 0x6e, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x4d, - 0x61, 0x6e, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x41, 0x0a, 0x0a, 0x4e, - 0x65, 0x77, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x18, 0x2e, 0x6c, 0x6e, 0x72, 0x70, - 0x63, 0x2e, 0x4e, 0x65, 0x77, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4e, 0x65, 0x77, 0x41, - 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x44, - 0x0a, 0x0b, 0x53, 0x69, 0x67, 0x6e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x19, 0x2e, - 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, - 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, - 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4a, 0x0a, 0x0d, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x4d, 0x65, - 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x1b, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x56, 0x65, - 0x72, 0x69, 0x66, 0x79, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x56, 0x65, 0x72, 0x69, 0x66, - 0x79, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x12, 0x44, 0x0a, 0x0b, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x50, 0x65, 0x65, 0x72, 0x12, - 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x50, - 0x65, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, - 0x70, 0x63, 0x2e, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x50, 0x65, 0x65, 0x72, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4d, 0x0a, 0x0e, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x6e, - 0x6e, 0x65, 0x63, 0x74, 0x50, 0x65, 0x65, 0x72, 0x12, 0x1c, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, - 0x2e, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x50, 0x65, 0x65, 0x72, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x44, - 0x69, 0x73, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x50, 0x65, 0x65, 0x72, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3e, 0x0a, 0x09, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x65, 0x65, - 0x72, 0x73, 0x12, 0x17, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x50, - 0x65, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x6c, 0x6e, - 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x65, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x47, 0x0a, 0x13, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, - 0x62, 0x65, 0x50, 0x65, 0x65, 0x72, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x1c, 0x2e, 0x6c, - 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x65, 0x65, 0x72, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x53, 0x75, - 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x1a, 0x10, 0x2e, 0x6c, 0x6e, 0x72, - 0x70, 0x63, 0x2e, 0x50, 0x65, 0x65, 0x72, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x30, 0x01, 0x12, 0x38, - 0x0a, 0x07, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x15, 0x2e, 0x6c, 0x6e, 0x72, 0x70, - 0x63, 0x2e, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x16, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x47, 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x44, - 0x65, 0x62, 0x75, 0x67, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, - 0x2e, 0x47, 0x65, 0x74, 0x44, 0x65, 0x62, 0x75, 0x67, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, - 0x44, 0x65, 0x62, 0x75, 0x67, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x50, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x52, 0x65, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, - 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1d, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, - 0x52, 0x65, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x52, - 0x65, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x50, 0x0a, 0x0f, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x43, 0x68, - 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x12, 0x1d, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x50, - 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x65, - 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x47, 0x0a, 0x0c, 0x4c, 0x69, 0x73, 0x74, 0x43, 0x68, 0x61, - 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x12, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, - 0x73, 0x74, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x1b, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x43, 0x68, - 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x56, - 0x0a, 0x16, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x6e, - 0x65, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x1f, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, - 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x53, 0x75, 0x62, - 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x1a, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, - 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x55, 0x70, - 0x64, 0x61, 0x74, 0x65, 0x30, 0x01, 0x12, 0x4d, 0x0a, 0x0e, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x64, - 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x12, 0x1c, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, - 0x2e, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x64, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, - 0x6c, 0x6f, 0x73, 0x65, 0x64, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x41, 0x0a, 0x0f, 0x4f, 0x70, 0x65, 0x6e, 0x43, 0x68, 0x61, - 0x6e, 0x6e, 0x65, 0x6c, 0x53, 0x79, 0x6e, 0x63, 0x12, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, - 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x13, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, - 0x6e, 0x65, 0x6c, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x43, 0x0a, 0x0b, 0x4f, 0x70, 0x65, 0x6e, - 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x12, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, - 0x4f, 0x70, 0x65, 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x53, - 0x74, 0x61, 0x74, 0x75, 0x73, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x30, 0x01, 0x12, 0x53, 0x0a, - 0x10, 0x42, 0x61, 0x74, 0x63, 0x68, 0x4f, 0x70, 0x65, 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, - 0x6c, 0x12, 0x1e, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x42, 0x61, 0x74, 0x63, 0x68, 0x4f, + 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4b, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x54, 0x72, 0x61, 0x6e, + 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x1d, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, + 0x2e, 0x47, 0x65, 0x74, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, + 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x65, 0x74, 0x61, 0x69, + 0x6c, 0x73, 0x12, 0x44, 0x0a, 0x0b, 0x45, 0x73, 0x74, 0x69, 0x6d, 0x61, 0x74, 0x65, 0x46, 0x65, + 0x65, 0x12, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x45, 0x73, 0x74, 0x69, 0x6d, 0x61, + 0x74, 0x65, 0x46, 0x65, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x6c, + 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x45, 0x73, 0x74, 0x69, 0x6d, 0x61, 0x74, 0x65, 0x46, 0x65, 0x65, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3e, 0x0a, 0x09, 0x53, 0x65, 0x6e, 0x64, + 0x43, 0x6f, 0x69, 0x6e, 0x73, 0x12, 0x17, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, + 0x6e, 0x64, 0x43, 0x6f, 0x69, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, + 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x43, 0x6f, 0x69, 0x6e, 0x73, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x44, 0x0a, 0x0b, 0x4c, 0x69, 0x73, 0x74, + 0x55, 0x6e, 0x73, 0x70, 0x65, 0x6e, 0x74, 0x12, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, + 0x4c, 0x69, 0x73, 0x74, 0x55, 0x6e, 0x73, 0x70, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x55, + 0x6e, 0x73, 0x70, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4c, + 0x0a, 0x15, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x54, 0x72, 0x61, 0x6e, 0x73, + 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x1d, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, + 0x47, 0x65, 0x74, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x12, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x54, + 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x30, 0x01, 0x12, 0x3b, 0x0a, 0x08, + 0x53, 0x65, 0x6e, 0x64, 0x4d, 0x61, 0x6e, 0x79, 0x12, 0x16, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, + 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x4d, 0x61, 0x6e, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x17, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x4d, 0x61, 0x6e, + 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x41, 0x0a, 0x0a, 0x4e, 0x65, 0x77, + 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x18, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, + 0x4e, 0x65, 0x77, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4e, 0x65, 0x77, 0x41, 0x64, 0x64, + 0x72, 0x65, 0x73, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x44, 0x0a, 0x0b, + 0x53, 0x69, 0x67, 0x6e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x19, 0x2e, 0x6c, 0x6e, + 0x72, 0x70, 0x63, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, + 0x69, 0x67, 0x6e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x4a, 0x0a, 0x0d, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x4d, 0x65, 0x73, 0x73, + 0x61, 0x67, 0x65, 0x12, 0x1b, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x56, 0x65, 0x72, 0x69, + 0x66, 0x79, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x1c, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x4d, + 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x44, + 0x0a, 0x0b, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x50, 0x65, 0x65, 0x72, 0x12, 0x19, 0x2e, + 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x50, 0x65, 0x65, + 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, + 0x2e, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x50, 0x65, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4d, 0x0a, 0x0e, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x6e, 0x6e, 0x65, + 0x63, 0x74, 0x50, 0x65, 0x65, 0x72, 0x12, 0x1c, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x44, + 0x69, 0x73, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x50, 0x65, 0x65, 0x72, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x44, 0x69, 0x73, + 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x50, 0x65, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x3e, 0x0a, 0x09, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x65, 0x65, 0x72, 0x73, + 0x12, 0x17, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x65, 0x65, + 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x6c, 0x6e, 0x72, 0x70, + 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x65, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x47, 0x0a, 0x13, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, + 0x50, 0x65, 0x65, 0x72, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x1c, 0x2e, 0x6c, 0x6e, 0x72, + 0x70, 0x63, 0x2e, 0x50, 0x65, 0x65, 0x72, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x53, 0x75, 0x62, 0x73, + 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x1a, 0x10, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, + 0x2e, 0x50, 0x65, 0x65, 0x72, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x30, 0x01, 0x12, 0x38, 0x0a, 0x07, + 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x15, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, + 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, + 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x47, 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x44, 0x65, 0x62, + 0x75, 0x67, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x47, + 0x65, 0x74, 0x44, 0x65, 0x62, 0x75, 0x67, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x44, 0x65, + 0x62, 0x75, 0x67, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x50, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x52, 0x65, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x49, 0x6e, + 0x66, 0x6f, 0x12, 0x1d, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x65, + 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x1e, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x65, 0x63, + 0x6f, 0x76, 0x65, 0x72, 0x79, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x50, 0x0a, 0x0f, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x43, 0x68, 0x61, 0x6e, + 0x6e, 0x65, 0x6c, 0x73, 0x12, 0x1d, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x65, 0x6e, + 0x64, 0x69, 0x6e, 0x67, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x65, 0x6e, 0x64, + 0x69, 0x6e, 0x67, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x47, 0x0a, 0x0c, 0x4c, 0x69, 0x73, 0x74, 0x43, 0x68, 0x61, 0x6e, 0x6e, + 0x65, 0x6c, 0x73, 0x12, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, + 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x1b, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x43, 0x68, 0x61, 0x6e, + 0x6e, 0x65, 0x6c, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x56, 0x0a, 0x16, + 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, + 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x1f, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, + 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x53, 0x75, 0x62, 0x73, 0x63, + 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x1a, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, + 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x55, 0x70, 0x64, 0x61, + 0x74, 0x65, 0x30, 0x01, 0x12, 0x4d, 0x0a, 0x0e, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x64, 0x43, 0x68, + 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x12, 0x1c, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, + 0x6c, 0x6f, 0x73, 0x65, 0x64, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x6c, 0x6f, + 0x73, 0x65, 0x64, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x41, 0x0a, 0x0f, 0x4f, 0x70, 0x65, 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x6e, + 0x65, 0x6c, 0x53, 0x79, 0x6e, 0x63, 0x12, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x1f, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x42, 0x61, 0x74, 0x63, 0x68, 0x4f, - 0x70, 0x65, 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x4c, 0x0a, 0x10, 0x46, 0x75, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x53, 0x74, 0x61, - 0x74, 0x65, 0x53, 0x74, 0x65, 0x70, 0x12, 0x1b, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x46, - 0x75, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, - 0x4d, 0x73, 0x67, 0x1a, 0x1b, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x75, 0x6e, 0x64, - 0x69, 0x6e, 0x67, 0x53, 0x74, 0x61, 0x74, 0x65, 0x53, 0x74, 0x65, 0x70, 0x52, 0x65, 0x73, 0x70, - 0x12, 0x50, 0x0a, 0x0f, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x41, 0x63, 0x63, 0x65, 0x70, - 0x74, 0x6f, 0x72, 0x12, 0x1c, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, - 0x6e, 0x65, 0x6c, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x1a, 0x1b, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, - 0x6c, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x28, 0x01, - 0x30, 0x01, 0x12, 0x46, 0x0a, 0x0c, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x6e, - 0x65, 0x6c, 0x12, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x6c, 0x6f, 0x73, 0x65, - 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, - 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x53, 0x74, 0x61, 0x74, - 0x75, 0x73, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x30, 0x01, 0x12, 0x4d, 0x0a, 0x0e, 0x41, 0x62, - 0x61, 0x6e, 0x64, 0x6f, 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x12, 0x1c, 0x2e, 0x6c, - 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x62, 0x61, 0x6e, 0x64, 0x6f, 0x6e, 0x43, 0x68, 0x61, 0x6e, - 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x6c, 0x6e, 0x72, + 0x74, 0x1a, 0x13, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, + 0x6c, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x43, 0x0a, 0x0b, 0x4f, 0x70, 0x65, 0x6e, 0x43, 0x68, + 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x12, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4f, 0x70, + 0x65, 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x17, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x53, 0x74, 0x61, + 0x74, 0x75, 0x73, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x30, 0x01, 0x12, 0x53, 0x0a, 0x10, 0x42, + 0x61, 0x74, 0x63, 0x68, 0x4f, 0x70, 0x65, 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x12, + 0x1e, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x42, 0x61, 0x74, 0x63, 0x68, 0x4f, 0x70, 0x65, + 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x1f, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x42, 0x61, 0x74, 0x63, 0x68, 0x4f, 0x70, 0x65, + 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x4c, 0x0a, 0x10, 0x46, 0x75, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x53, 0x74, 0x61, 0x74, 0x65, + 0x53, 0x74, 0x65, 0x70, 0x12, 0x1b, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x75, 0x6e, + 0x64, 0x69, 0x6e, 0x67, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x73, + 0x67, 0x1a, 0x1b, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x75, 0x6e, 0x64, 0x69, 0x6e, + 0x67, 0x53, 0x74, 0x61, 0x74, 0x65, 0x53, 0x74, 0x65, 0x70, 0x52, 0x65, 0x73, 0x70, 0x12, 0x50, + 0x0a, 0x0f, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x6f, + 0x72, 0x12, 0x1c, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, + 0x6c, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x1a, + 0x1b, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x41, + 0x63, 0x63, 0x65, 0x70, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x28, 0x01, 0x30, 0x01, + 0x12, 0x46, 0x0a, 0x0c, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, + 0x12, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x43, 0x68, + 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x6c, + 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, + 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x30, 0x01, 0x12, 0x4d, 0x0a, 0x0e, 0x41, 0x62, 0x61, 0x6e, + 0x64, 0x6f, 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x12, 0x1c, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x62, 0x61, 0x6e, 0x64, 0x6f, 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, - 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3f, 0x0a, 0x0b, 0x53, 0x65, 0x6e, - 0x64, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x12, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, - 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x13, 0x2e, 0x6c, - 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x03, 0x88, 0x02, 0x01, 0x28, 0x01, 0x30, 0x01, 0x12, 0x3a, 0x0a, 0x0f, 0x53, 0x65, - 0x6e, 0x64, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x53, 0x79, 0x6e, 0x63, 0x12, 0x12, 0x2e, - 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x13, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x46, 0x0a, 0x0b, 0x53, 0x65, 0x6e, 0x64, 0x54, 0x6f, - 0x52, 0x6f, 0x75, 0x74, 0x65, 0x12, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, - 0x6e, 0x64, 0x54, 0x6f, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x13, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x03, 0x88, 0x02, 0x01, 0x28, 0x01, 0x30, 0x01, 0x12, 0x41, - 0x0a, 0x0f, 0x53, 0x65, 0x6e, 0x64, 0x54, 0x6f, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x53, 0x79, 0x6e, - 0x63, 0x12, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x54, 0x6f, - 0x52, 0x6f, 0x75, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x13, 0x2e, 0x6c, - 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x37, 0x0a, 0x0a, 0x41, 0x64, 0x64, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x12, - 0x0e, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x1a, - 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x64, 0x64, 0x49, 0x6e, 0x76, 0x6f, 0x69, - 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x45, 0x0a, 0x0c, 0x4c, 0x69, - 0x73, 0x74, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x73, 0x12, 0x19, 0x2e, 0x6c, 0x6e, 0x72, - 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, - 0x73, 0x74, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x33, 0x0a, 0x0d, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x49, 0x6e, 0x76, 0x6f, 0x69, - 0x63, 0x65, 0x12, 0x12, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x61, 0x79, 0x6d, 0x65, - 0x6e, 0x74, 0x48, 0x61, 0x73, 0x68, 0x1a, 0x0e, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x49, - 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x12, 0x41, 0x0a, 0x11, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, - 0x69, 0x62, 0x65, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x73, 0x12, 0x1a, 0x2e, 0x6c, 0x6e, - 0x72, 0x70, 0x63, 0x2e, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x53, 0x75, 0x62, 0x73, 0x63, - 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x1a, 0x0e, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, - 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x30, 0x01, 0x12, 0x32, 0x0a, 0x0c, 0x44, 0x65, 0x63, - 0x6f, 0x64, 0x65, 0x50, 0x61, 0x79, 0x52, 0x65, 0x71, 0x12, 0x13, 0x2e, 0x6c, 0x6e, 0x72, 0x70, - 0x63, 0x2e, 0x50, 0x61, 0x79, 0x52, 0x65, 0x71, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x1a, 0x0d, - 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x61, 0x79, 0x52, 0x65, 0x71, 0x12, 0x47, 0x0a, - 0x0c, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x1a, 0x2e, - 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, - 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x6c, 0x6e, 0x72, 0x70, - 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4a, 0x0a, 0x0d, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, - 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x1b, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, - 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x44, 0x65, 0x6c, - 0x65, 0x74, 0x65, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x56, 0x0a, 0x11, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x41, 0x6c, 0x6c, 0x50, - 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x1f, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, - 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x41, 0x6c, 0x6c, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, - 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, - 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x41, 0x6c, 0x6c, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, - 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x40, 0x0a, 0x0d, 0x44, 0x65, - 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x47, 0x72, 0x61, 0x70, 0x68, 0x12, 0x1a, 0x2e, 0x6c, 0x6e, - 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x47, 0x72, 0x61, 0x70, 0x68, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x13, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, - 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x47, 0x72, 0x61, 0x70, 0x68, 0x12, 0x47, 0x0a, 0x0e, - 0x47, 0x65, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x12, 0x19, - 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x4d, 0x65, 0x74, 0x72, 0x69, - 0x63, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, - 0x63, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x39, 0x0a, 0x0b, 0x47, 0x65, 0x74, 0x43, 0x68, 0x61, 0x6e, - 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x16, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, - 0x6e, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x12, 0x2e, 0x6c, - 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x45, 0x64, 0x67, 0x65, - 0x12, 0x36, 0x0a, 0x0b, 0x47, 0x65, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, - 0x16, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x0f, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, - 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x44, 0x0a, 0x0b, 0x51, 0x75, 0x65, 0x72, - 0x79, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x12, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, - 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, - 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3f, - 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x49, 0x6e, 0x66, 0x6f, - 0x12, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, - 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x12, 0x2e, 0x6c, 0x6e, - 0x72, 0x70, 0x63, 0x2e, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x49, 0x6e, 0x66, 0x6f, 0x12, - 0x35, 0x0a, 0x0a, 0x53, 0x74, 0x6f, 0x70, 0x44, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x12, 0x12, 0x2e, - 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x74, 0x6f, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x13, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x74, 0x6f, 0x70, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x57, 0x0a, 0x15, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, - 0x69, 0x62, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x47, 0x72, 0x61, 0x70, 0x68, 0x12, - 0x20, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x72, 0x61, 0x70, 0x68, 0x54, 0x6f, 0x70, - 0x6f, 0x6c, 0x6f, 0x67, 0x79, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, - 0x6e, 0x1a, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x72, 0x61, 0x70, 0x68, 0x54, - 0x6f, 0x70, 0x6f, 0x6c, 0x6f, 0x67, 0x79, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x30, 0x01, 0x12, - 0x41, 0x0a, 0x0a, 0x44, 0x65, 0x62, 0x75, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x18, 0x2e, - 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x44, 0x65, 0x62, 0x75, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, - 0x44, 0x65, 0x62, 0x75, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x3e, 0x0a, 0x09, 0x46, 0x65, 0x65, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x12, - 0x17, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x65, 0x65, 0x52, 0x65, 0x70, 0x6f, 0x72, - 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, - 0x2e, 0x46, 0x65, 0x65, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x4e, 0x0a, 0x13, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x43, 0x68, 0x61, 0x6e, - 0x6e, 0x65, 0x6c, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, - 0x63, 0x2e, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x6f, - 0x6c, 0x69, 0x63, 0x79, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x56, 0x0a, 0x11, 0x46, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, - 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x12, 0x1f, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, - 0x46, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, - 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, - 0x2e, 0x46, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x48, 0x69, 0x73, 0x74, 0x6f, - 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4e, 0x0a, 0x13, 0x45, 0x78, - 0x70, 0x6f, 0x72, 0x74, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x42, 0x61, 0x63, 0x6b, 0x75, - 0x70, 0x12, 0x21, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x45, 0x78, 0x70, 0x6f, 0x72, 0x74, - 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x14, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, - 0x6e, 0x6e, 0x65, 0x6c, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x12, 0x54, 0x0a, 0x17, 0x45, 0x78, - 0x70, 0x6f, 0x72, 0x74, 0x41, 0x6c, 0x6c, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x42, 0x61, - 0x63, 0x6b, 0x75, 0x70, 0x73, 0x12, 0x1e, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, - 0x61, 0x6e, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x45, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, - 0x61, 0x6e, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, - 0x12, 0x4e, 0x0a, 0x10, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x43, 0x68, 0x61, 0x6e, 0x42, 0x61, - 0x63, 0x6b, 0x75, 0x70, 0x12, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, - 0x6e, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x1a, - 0x1f, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x43, 0x68, - 0x61, 0x6e, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x12, 0x56, 0x0a, 0x15, 0x52, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x6e, - 0x65, 0x6c, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x73, 0x12, 0x1f, 0x2e, 0x6c, 0x6e, 0x72, 0x70, - 0x63, 0x2e, 0x52, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x42, 0x61, 0x63, - 0x6b, 0x75, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x6c, 0x6e, 0x72, - 0x70, 0x63, 0x2e, 0x52, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x58, 0x0a, 0x17, 0x53, 0x75, 0x62, 0x73, - 0x63, 0x72, 0x69, 0x62, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x42, 0x61, 0x63, 0x6b, - 0x75, 0x70, 0x73, 0x12, 0x20, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, - 0x6e, 0x65, 0x6c, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, - 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x1a, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, - 0x61, 0x6e, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, - 0x30, 0x01, 0x12, 0x47, 0x0a, 0x0c, 0x42, 0x61, 0x6b, 0x65, 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, - 0x6f, 0x6e, 0x12, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x42, 0x61, 0x6b, 0x65, 0x4d, - 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, - 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x42, 0x61, 0x6b, 0x65, 0x4d, 0x61, 0x63, 0x61, 0x72, - 0x6f, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x50, 0x0a, 0x0f, 0x4c, - 0x69, 0x73, 0x74, 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x49, 0x44, 0x73, 0x12, 0x1d, - 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x61, 0x63, 0x61, 0x72, - 0x6f, 0x6f, 0x6e, 0x49, 0x44, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, - 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, - 0x6f, 0x6e, 0x49, 0x44, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x53, 0x0a, - 0x10, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x49, - 0x44, 0x12, 0x1e, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, - 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x49, 0x44, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x1f, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, - 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x49, 0x44, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x50, 0x0a, 0x0f, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, - 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x1d, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, - 0x73, 0x74, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, - 0x74, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x53, 0x0a, 0x18, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x4d, 0x61, 0x63, - 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, - 0x12, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x4d, 0x61, - 0x63, 0x50, 0x65, 0x72, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x6c, - 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x4d, 0x61, 0x63, 0x50, 0x65, 0x72, - 0x6d, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x56, 0x0a, 0x15, 0x52, 0x65, 0x67, - 0x69, 0x73, 0x74, 0x65, 0x72, 0x52, 0x50, 0x43, 0x4d, 0x69, 0x64, 0x64, 0x6c, 0x65, 0x77, 0x61, - 0x72, 0x65, 0x12, 0x1c, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x50, 0x43, 0x4d, 0x69, - 0x64, 0x64, 0x6c, 0x65, 0x77, 0x61, 0x72, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x1a, 0x1b, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x50, 0x43, 0x4d, 0x69, 0x64, 0x64, - 0x6c, 0x65, 0x77, 0x61, 0x72, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x28, 0x01, 0x30, - 0x01, 0x12, 0x56, 0x0a, 0x11, 0x53, 0x65, 0x6e, 0x64, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x4d, - 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x1f, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, - 0x65, 0x6e, 0x64, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, - 0x53, 0x65, 0x6e, 0x64, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, - 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x58, 0x0a, 0x17, 0x53, 0x75, 0x62, - 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x4d, 0x65, 0x73, 0x73, - 0x61, 0x67, 0x65, 0x73, 0x12, 0x25, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x75, 0x62, - 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x4d, 0x65, 0x73, 0x73, - 0x61, 0x67, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x14, 0x2e, 0x6c, 0x6e, - 0x72, 0x70, 0x63, 0x2e, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, - 0x65, 0x30, 0x01, 0x12, 0x44, 0x0a, 0x0b, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x6c, 0x69, 0x61, 0x73, - 0x65, 0x73, 0x12, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x41, - 0x6c, 0x69, 0x61, 0x73, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, - 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x65, - 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5f, 0x0a, 0x14, 0x4c, 0x6f, 0x6f, - 0x6b, 0x75, 0x70, 0x48, 0x74, 0x6c, 0x63, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, - 0x6e, 0x12, 0x22, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, - 0x48, 0x74, 0x6c, 0x63, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x6f, - 0x6f, 0x6b, 0x75, 0x70, 0x48, 0x74, 0x6c, 0x63, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, - 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x27, 0x5a, 0x25, 0x67, 0x69, - 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x6e, 0x69, - 0x6e, 0x67, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x2f, 0x6c, 0x6e, 0x64, 0x2f, 0x6c, 0x6e, - 0x72, 0x70, 0x63, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, + 0x2e, 0x41, 0x62, 0x61, 0x6e, 0x64, 0x6f, 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3f, 0x0a, 0x0b, 0x53, 0x65, 0x6e, 0x64, 0x50, + 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x12, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, + 0x65, 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x13, 0x2e, 0x6c, 0x6e, 0x72, + 0x70, 0x63, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x03, 0x88, 0x02, 0x01, 0x28, 0x01, 0x30, 0x01, 0x12, 0x3a, 0x0a, 0x0f, 0x53, 0x65, 0x6e, 0x64, + 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x53, 0x79, 0x6e, 0x63, 0x12, 0x12, 0x2e, 0x6c, 0x6e, + 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x13, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x46, 0x0a, 0x0b, 0x53, 0x65, 0x6e, 0x64, 0x54, 0x6f, 0x52, 0x6f, + 0x75, 0x74, 0x65, 0x12, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x6e, 0x64, + 0x54, 0x6f, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x13, + 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x03, 0x88, 0x02, 0x01, 0x28, 0x01, 0x30, 0x01, 0x12, 0x41, 0x0a, 0x0f, + 0x53, 0x65, 0x6e, 0x64, 0x54, 0x6f, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x53, 0x79, 0x6e, 0x63, 0x12, + 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x54, 0x6f, 0x52, 0x6f, + 0x75, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x13, 0x2e, 0x6c, 0x6e, 0x72, + 0x70, 0x63, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x37, 0x0a, 0x0a, 0x41, 0x64, 0x64, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x12, 0x0e, 0x2e, + 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x1a, 0x19, 0x2e, + 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x64, 0x64, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x45, 0x0a, 0x0c, 0x4c, 0x69, 0x73, 0x74, + 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x73, 0x12, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, + 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, + 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x33, 0x0a, 0x0d, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, + 0x12, 0x12, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, + 0x48, 0x61, 0x73, 0x68, 0x1a, 0x0e, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x49, 0x6e, 0x76, + 0x6f, 0x69, 0x63, 0x65, 0x12, 0x41, 0x0a, 0x11, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, + 0x65, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x73, 0x12, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, + 0x63, 0x2e, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, + 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x1a, 0x0e, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x49, 0x6e, + 0x76, 0x6f, 0x69, 0x63, 0x65, 0x30, 0x01, 0x12, 0x32, 0x0a, 0x0c, 0x44, 0x65, 0x63, 0x6f, 0x64, + 0x65, 0x50, 0x61, 0x79, 0x52, 0x65, 0x71, 0x12, 0x13, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, + 0x50, 0x61, 0x79, 0x52, 0x65, 0x71, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x1a, 0x0d, 0x2e, 0x6c, + 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x61, 0x79, 0x52, 0x65, 0x71, 0x12, 0x47, 0x0a, 0x0c, 0x4c, + 0x69, 0x73, 0x74, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x1a, 0x2e, 0x6c, 0x6e, + 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x73, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, + 0x4c, 0x69, 0x73, 0x74, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4a, 0x0a, 0x0d, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x50, 0x61, + 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x1b, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x44, 0x65, + 0x6c, 0x65, 0x74, 0x65, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, + 0x65, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x56, 0x0a, 0x11, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x41, 0x6c, 0x6c, 0x50, 0x61, 0x79, + 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x1f, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x44, 0x65, + 0x6c, 0x65, 0x74, 0x65, 0x41, 0x6c, 0x6c, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x44, + 0x65, 0x6c, 0x65, 0x74, 0x65, 0x41, 0x6c, 0x6c, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x73, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x40, 0x0a, 0x0d, 0x44, 0x65, 0x73, 0x63, + 0x72, 0x69, 0x62, 0x65, 0x47, 0x72, 0x61, 0x70, 0x68, 0x12, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, + 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x47, 0x72, 0x61, 0x70, 0x68, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x13, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, + 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x47, 0x72, 0x61, 0x70, 0x68, 0x12, 0x47, 0x0a, 0x0e, 0x47, 0x65, + 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x12, 0x19, 0x2e, 0x6c, + 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, + 0x4e, 0x6f, 0x64, 0x65, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x39, 0x0a, 0x0b, 0x47, 0x65, 0x74, 0x43, 0x68, 0x61, 0x6e, 0x49, 0x6e, + 0x66, 0x6f, 0x12, 0x16, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x49, + 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x12, 0x2e, 0x6c, 0x6e, 0x72, + 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x45, 0x64, 0x67, 0x65, 0x12, 0x36, + 0x0a, 0x0b, 0x47, 0x65, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x16, 0x2e, + 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x0f, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4e, 0x6f, + 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x44, 0x0a, 0x0b, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, + 0x6f, 0x75, 0x74, 0x65, 0x73, 0x12, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x51, 0x75, + 0x65, 0x72, 0x79, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x6f, + 0x75, 0x74, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3f, 0x0a, 0x0e, + 0x47, 0x65, 0x74, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x19, + 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x49, 0x6e, + 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x12, 0x2e, 0x6c, 0x6e, 0x72, 0x70, + 0x63, 0x2e, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x35, 0x0a, + 0x0a, 0x53, 0x74, 0x6f, 0x70, 0x44, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x12, 0x12, 0x2e, 0x6c, 0x6e, + 0x72, 0x70, 0x63, 0x2e, 0x53, 0x74, 0x6f, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x13, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x74, 0x6f, 0x70, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x57, 0x0a, 0x15, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, + 0x65, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x47, 0x72, 0x61, 0x70, 0x68, 0x12, 0x20, 0x2e, + 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x72, 0x61, 0x70, 0x68, 0x54, 0x6f, 0x70, 0x6f, 0x6c, + 0x6f, 0x67, 0x79, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x1a, + 0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x72, 0x61, 0x70, 0x68, 0x54, 0x6f, 0x70, + 0x6f, 0x6c, 0x6f, 0x67, 0x79, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x30, 0x01, 0x12, 0x41, 0x0a, + 0x0a, 0x44, 0x65, 0x62, 0x75, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x18, 0x2e, 0x6c, 0x6e, + 0x72, 0x70, 0x63, 0x2e, 0x44, 0x65, 0x62, 0x75, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x44, 0x65, + 0x62, 0x75, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x3e, 0x0a, 0x09, 0x46, 0x65, 0x65, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x17, 0x2e, + 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x65, 0x65, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x46, + 0x65, 0x65, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x4e, 0x0a, 0x13, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, + 0x6c, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, + 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x6f, 0x6c, 0x69, + 0x63, 0x79, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x56, 0x0a, 0x11, 0x46, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x48, 0x69, + 0x73, 0x74, 0x6f, 0x72, 0x79, 0x12, 0x1f, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x6f, + 0x72, 0x77, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x46, + 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4e, 0x0a, 0x13, 0x45, 0x78, 0x70, 0x6f, + 0x72, 0x74, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x12, + 0x21, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x45, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x43, 0x68, + 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x14, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, + 0x65, 0x6c, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x12, 0x54, 0x0a, 0x17, 0x45, 0x78, 0x70, 0x6f, + 0x72, 0x74, 0x41, 0x6c, 0x6c, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x42, 0x61, 0x63, 0x6b, + 0x75, 0x70, 0x73, 0x12, 0x1e, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, + 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x45, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, + 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x12, 0x4e, + 0x0a, 0x10, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x43, 0x68, 0x61, 0x6e, 0x42, 0x61, 0x63, 0x6b, + 0x75, 0x70, 0x12, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x42, + 0x61, 0x63, 0x6b, 0x75, 0x70, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x1a, 0x1f, 0x2e, + 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x43, 0x68, 0x61, 0x6e, + 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x56, + 0x0a, 0x15, 0x52, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, + 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x73, 0x12, 0x1f, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, + 0x52, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x42, 0x61, 0x63, 0x6b, 0x75, + 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, + 0x2e, 0x52, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x58, 0x0a, 0x17, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, + 0x69, 0x62, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, + 0x73, 0x12, 0x20, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, + 0x6c, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, + 0x69, 0x6f, 0x6e, 0x1a, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, + 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x30, 0x01, + 0x12, 0x47, 0x0a, 0x0c, 0x42, 0x61, 0x6b, 0x65, 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, + 0x12, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x42, 0x61, 0x6b, 0x65, 0x4d, 0x61, 0x63, + 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x6c, + 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x42, 0x61, 0x6b, 0x65, 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, + 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x50, 0x0a, 0x0f, 0x4c, 0x69, 0x73, + 0x74, 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x49, 0x44, 0x73, 0x12, 0x1d, 0x2e, 0x6c, + 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, + 0x6e, 0x49, 0x44, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x6c, 0x6e, + 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, + 0x49, 0x44, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x53, 0x0a, 0x10, 0x44, + 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x49, 0x44, 0x12, + 0x1e, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4d, 0x61, + 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x49, 0x44, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x1f, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4d, 0x61, + 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x49, 0x44, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x50, 0x0a, 0x0f, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, + 0x6f, 0x6e, 0x73, 0x12, 0x1d, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, + 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x50, + 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x53, 0x0a, 0x18, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x4d, 0x61, 0x63, 0x61, 0x72, + 0x6f, 0x6f, 0x6e, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x1a, + 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x4d, 0x61, 0x63, 0x50, + 0x65, 0x72, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x6c, 0x6e, 0x72, + 0x70, 0x63, 0x2e, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x4d, 0x61, 0x63, 0x50, 0x65, 0x72, 0x6d, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x56, 0x0a, 0x15, 0x52, 0x65, 0x67, 0x69, 0x73, + 0x74, 0x65, 0x72, 0x52, 0x50, 0x43, 0x4d, 0x69, 0x64, 0x64, 0x6c, 0x65, 0x77, 0x61, 0x72, 0x65, + 0x12, 0x1c, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x50, 0x43, 0x4d, 0x69, 0x64, 0x64, + 0x6c, 0x65, 0x77, 0x61, 0x72, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x1a, 0x1b, + 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x50, 0x43, 0x4d, 0x69, 0x64, 0x64, 0x6c, 0x65, + 0x77, 0x61, 0x72, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x28, 0x01, 0x30, 0x01, 0x12, + 0x56, 0x0a, 0x11, 0x53, 0x65, 0x6e, 0x64, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x4d, 0x65, 0x73, + 0x73, 0x61, 0x67, 0x65, 0x12, 0x1f, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x6e, + 0x64, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, + 0x6e, 0x64, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x58, 0x0a, 0x17, 0x53, 0x75, 0x62, 0x73, 0x63, + 0x72, 0x69, 0x62, 0x65, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, + 0x65, 0x73, 0x12, 0x25, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x75, 0x62, 0x73, 0x63, + 0x72, 0x69, 0x62, 0x65, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, + 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x14, 0x2e, 0x6c, 0x6e, 0x72, 0x70, + 0x63, 0x2e, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x30, + 0x01, 0x12, 0x44, 0x0a, 0x0b, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x65, 0x73, + 0x12, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x6c, 0x69, + 0x61, 0x73, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x6c, 0x6e, + 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x65, 0x73, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5f, 0x0a, 0x14, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, + 0x70, 0x48, 0x74, 0x6c, 0x63, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x12, + 0x22, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x48, 0x74, + 0x6c, 0x63, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x6f, 0x6f, 0x6b, + 0x75, 0x70, 0x48, 0x74, 0x6c, 0x63, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x27, 0x5a, 0x25, 0x67, 0x69, 0x74, 0x68, + 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x6e, 0x69, 0x6e, 0x67, + 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x2f, 0x6c, 0x6e, 0x64, 0x2f, 0x6c, 0x6e, 0x72, 0x70, + 0x63, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -21282,7 +21303,7 @@ func file_lightning_proto_rawDescGZIP() []byte { } var file_lightning_proto_enumTypes = make([]protoimpl.EnumInfo, 21) -var file_lightning_proto_msgTypes = make([]protoimpl.MessageInfo, 226) +var file_lightning_proto_msgTypes = make([]protoimpl.MessageInfo, 227) var file_lightning_proto_goTypes = []interface{}{ (OutputScriptType)(0), // 0: lnrpc.OutputScriptType (CoinSelectionStrategy)(0), // 1: lnrpc.CoinSelectionStrategy @@ -21529,8 +21550,9 @@ var file_lightning_proto_goTypes = []interface{}{ nil, // 242: lnrpc.Invoice.FeaturesEntry nil, // 243: lnrpc.Invoice.AmpInvoiceStateEntry nil, // 244: lnrpc.InvoiceHTLC.CustomRecordsEntry - nil, // 245: lnrpc.PayReq.FeaturesEntry - nil, // 246: lnrpc.ListPermissionsResponse.MethodPermissionsEntry + nil, // 245: lnrpc.Payment.FirstHopCustomRecordsEntry + nil, // 246: lnrpc.PayReq.FeaturesEntry + nil, // 247: lnrpc.ListPermissionsResponse.MethodPermissionsEntry } var file_lightning_proto_depIdxs = []int32{ 2, // 0: lnrpc.Utxo.address_type:type_name -> lnrpc.AddressType @@ -21669,200 +21691,201 @@ var file_lightning_proto_depIdxs = []int32{ 18, // 133: lnrpc.Payment.status:type_name -> lnrpc.Payment.PaymentStatus 165, // 134: lnrpc.Payment.htlcs:type_name -> lnrpc.HTLCAttempt 9, // 135: lnrpc.Payment.failure_reason:type_name -> lnrpc.PaymentFailureReason - 19, // 136: lnrpc.HTLCAttempt.status:type_name -> lnrpc.HTLCAttempt.HTLCStatus - 126, // 137: lnrpc.HTLCAttempt.route:type_name -> lnrpc.Route - 209, // 138: lnrpc.HTLCAttempt.failure:type_name -> lnrpc.Failure - 164, // 139: lnrpc.ListPaymentsResponse.payments:type_name -> lnrpc.Payment - 38, // 140: lnrpc.AbandonChannelRequest.channel_point:type_name -> lnrpc.ChannelPoint - 150, // 141: lnrpc.PayReq.route_hints:type_name -> lnrpc.RouteHint - 245, // 142: lnrpc.PayReq.features:type_name -> lnrpc.PayReq.FeaturesEntry - 151, // 143: lnrpc.PayReq.blinded_paths:type_name -> lnrpc.BlindedPaymentPath - 180, // 144: lnrpc.FeeReportResponse.channel_fees:type_name -> lnrpc.ChannelFeeReport - 38, // 145: lnrpc.PolicyUpdateRequest.chan_point:type_name -> lnrpc.ChannelPoint - 182, // 146: lnrpc.PolicyUpdateRequest.inbound_fee:type_name -> lnrpc.InboundFee - 39, // 147: lnrpc.FailedUpdate.outpoint:type_name -> lnrpc.OutPoint - 11, // 148: lnrpc.FailedUpdate.reason:type_name -> lnrpc.UpdateFailure - 184, // 149: lnrpc.PolicyUpdateResponse.failed_updates:type_name -> lnrpc.FailedUpdate - 187, // 150: lnrpc.ForwardingHistoryResponse.forwarding_events:type_name -> lnrpc.ForwardingEvent - 38, // 151: lnrpc.ExportChannelBackupRequest.chan_point:type_name -> lnrpc.ChannelPoint - 38, // 152: lnrpc.ChannelBackup.chan_point:type_name -> lnrpc.ChannelPoint - 38, // 153: lnrpc.MultiChanBackup.chan_points:type_name -> lnrpc.ChannelPoint - 194, // 154: lnrpc.ChanBackupSnapshot.single_chan_backups:type_name -> lnrpc.ChannelBackups - 191, // 155: lnrpc.ChanBackupSnapshot.multi_chan_backup:type_name -> lnrpc.MultiChanBackup - 190, // 156: lnrpc.ChannelBackups.chan_backups:type_name -> lnrpc.ChannelBackup - 194, // 157: lnrpc.RestoreChanBackupRequest.chan_backups:type_name -> lnrpc.ChannelBackups - 199, // 158: lnrpc.BakeMacaroonRequest.permissions:type_name -> lnrpc.MacaroonPermission - 199, // 159: lnrpc.MacaroonPermissionList.permissions:type_name -> lnrpc.MacaroonPermission - 246, // 160: lnrpc.ListPermissionsResponse.method_permissions:type_name -> lnrpc.ListPermissionsResponse.MethodPermissionsEntry - 20, // 161: lnrpc.Failure.code:type_name -> lnrpc.Failure.FailureCode - 210, // 162: lnrpc.Failure.channel_update:type_name -> lnrpc.ChannelUpdate - 212, // 163: lnrpc.MacaroonId.ops:type_name -> lnrpc.Op - 199, // 164: lnrpc.CheckMacPermRequest.permissions:type_name -> lnrpc.MacaroonPermission - 216, // 165: lnrpc.RPCMiddlewareRequest.stream_auth:type_name -> lnrpc.StreamAuth - 217, // 166: lnrpc.RPCMiddlewareRequest.request:type_name -> lnrpc.RPCMessage - 217, // 167: lnrpc.RPCMiddlewareRequest.response:type_name -> lnrpc.RPCMessage - 219, // 168: lnrpc.RPCMiddlewareResponse.register:type_name -> lnrpc.MiddlewareRegistration - 220, // 169: lnrpc.RPCMiddlewareResponse.feedback:type_name -> lnrpc.InterceptFeedback - 178, // 170: lnrpc.Peer.FeaturesEntry.value:type_name -> lnrpc.Feature - 178, // 171: lnrpc.GetInfoResponse.FeaturesEntry.value:type_name -> lnrpc.Feature - 4, // 172: lnrpc.PendingChannelsResponse.PendingChannel.initiator:type_name -> lnrpc.Initiator - 3, // 173: lnrpc.PendingChannelsResponse.PendingChannel.commitment_type:type_name -> lnrpc.CommitmentType - 227, // 174: lnrpc.PendingChannelsResponse.PendingOpenChannel.channel:type_name -> lnrpc.PendingChannelsResponse.PendingChannel - 227, // 175: lnrpc.PendingChannelsResponse.WaitingCloseChannel.channel:type_name -> lnrpc.PendingChannelsResponse.PendingChannel - 230, // 176: lnrpc.PendingChannelsResponse.WaitingCloseChannel.commitments:type_name -> lnrpc.PendingChannelsResponse.Commitments - 227, // 177: lnrpc.PendingChannelsResponse.ClosedChannel.channel:type_name -> lnrpc.PendingChannelsResponse.PendingChannel - 227, // 178: lnrpc.PendingChannelsResponse.ForceClosedChannel.channel:type_name -> lnrpc.PendingChannelsResponse.PendingChannel - 108, // 179: lnrpc.PendingChannelsResponse.ForceClosedChannel.pending_htlcs:type_name -> lnrpc.PendingHTLC - 15, // 180: lnrpc.PendingChannelsResponse.ForceClosedChannel.anchor:type_name -> lnrpc.PendingChannelsResponse.ForceClosedChannel.AnchorState - 113, // 181: lnrpc.WalletBalanceResponse.AccountBalanceEntry.value:type_name -> lnrpc.WalletAccountBalance - 178, // 182: lnrpc.LightningNode.FeaturesEntry.value:type_name -> lnrpc.Feature - 137, // 183: lnrpc.NodeMetricsResponse.BetweennessCentralityEntry.value:type_name -> lnrpc.FloatMetric - 178, // 184: lnrpc.NodeUpdate.FeaturesEntry.value:type_name -> lnrpc.Feature - 178, // 185: lnrpc.Invoice.FeaturesEntry.value:type_name -> lnrpc.Feature - 154, // 186: lnrpc.Invoice.AmpInvoiceStateEntry.value:type_name -> lnrpc.AMPInvoiceState - 178, // 187: lnrpc.PayReq.FeaturesEntry.value:type_name -> lnrpc.Feature - 206, // 188: lnrpc.ListPermissionsResponse.MethodPermissionsEntry.value:type_name -> lnrpc.MacaroonPermissionList - 114, // 189: lnrpc.Lightning.WalletBalance:input_type -> lnrpc.WalletBalanceRequest - 117, // 190: lnrpc.Lightning.ChannelBalance:input_type -> lnrpc.ChannelBalanceRequest - 30, // 191: lnrpc.Lightning.GetTransactions:input_type -> lnrpc.GetTransactionsRequest - 42, // 192: lnrpc.Lightning.EstimateFee:input_type -> lnrpc.EstimateFeeRequest - 46, // 193: lnrpc.Lightning.SendCoins:input_type -> lnrpc.SendCoinsRequest - 48, // 194: lnrpc.Lightning.ListUnspent:input_type -> lnrpc.ListUnspentRequest - 30, // 195: lnrpc.Lightning.SubscribeTransactions:input_type -> lnrpc.GetTransactionsRequest - 44, // 196: lnrpc.Lightning.SendMany:input_type -> lnrpc.SendManyRequest - 50, // 197: lnrpc.Lightning.NewAddress:input_type -> lnrpc.NewAddressRequest - 52, // 198: lnrpc.Lightning.SignMessage:input_type -> lnrpc.SignMessageRequest - 54, // 199: lnrpc.Lightning.VerifyMessage:input_type -> lnrpc.VerifyMessageRequest - 56, // 200: lnrpc.Lightning.ConnectPeer:input_type -> lnrpc.ConnectPeerRequest - 58, // 201: lnrpc.Lightning.DisconnectPeer:input_type -> lnrpc.DisconnectPeerRequest - 74, // 202: lnrpc.Lightning.ListPeers:input_type -> lnrpc.ListPeersRequest - 76, // 203: lnrpc.Lightning.SubscribePeerEvents:input_type -> lnrpc.PeerEventSubscription - 78, // 204: lnrpc.Lightning.GetInfo:input_type -> lnrpc.GetInfoRequest - 80, // 205: lnrpc.Lightning.GetDebugInfo:input_type -> lnrpc.GetDebugInfoRequest - 82, // 206: lnrpc.Lightning.GetRecoveryInfo:input_type -> lnrpc.GetRecoveryInfoRequest - 109, // 207: lnrpc.Lightning.PendingChannels:input_type -> lnrpc.PendingChannelsRequest - 63, // 208: lnrpc.Lightning.ListChannels:input_type -> lnrpc.ListChannelsRequest - 111, // 209: lnrpc.Lightning.SubscribeChannelEvents:input_type -> lnrpc.ChannelEventSubscription - 70, // 210: lnrpc.Lightning.ClosedChannels:input_type -> lnrpc.ClosedChannelsRequest - 96, // 211: lnrpc.Lightning.OpenChannelSync:input_type -> lnrpc.OpenChannelRequest - 96, // 212: lnrpc.Lightning.OpenChannel:input_type -> lnrpc.OpenChannelRequest - 93, // 213: lnrpc.Lightning.BatchOpenChannel:input_type -> lnrpc.BatchOpenChannelRequest - 106, // 214: lnrpc.Lightning.FundingStateStep:input_type -> lnrpc.FundingTransitionMsg - 37, // 215: lnrpc.Lightning.ChannelAcceptor:input_type -> lnrpc.ChannelAcceptResponse - 88, // 216: lnrpc.Lightning.CloseChannel:input_type -> lnrpc.CloseChannelRequest - 172, // 217: lnrpc.Lightning.AbandonChannel:input_type -> lnrpc.AbandonChannelRequest - 33, // 218: lnrpc.Lightning.SendPayment:input_type -> lnrpc.SendRequest - 33, // 219: lnrpc.Lightning.SendPaymentSync:input_type -> lnrpc.SendRequest - 35, // 220: lnrpc.Lightning.SendToRoute:input_type -> lnrpc.SendToRouteRequest - 35, // 221: lnrpc.Lightning.SendToRouteSync:input_type -> lnrpc.SendToRouteRequest - 155, // 222: lnrpc.Lightning.AddInvoice:input_type -> lnrpc.Invoice - 161, // 223: lnrpc.Lightning.ListInvoices:input_type -> lnrpc.ListInvoiceRequest - 160, // 224: lnrpc.Lightning.LookupInvoice:input_type -> lnrpc.PaymentHash - 163, // 225: lnrpc.Lightning.SubscribeInvoices:input_type -> lnrpc.InvoiceSubscription - 176, // 226: lnrpc.Lightning.DecodePayReq:input_type -> lnrpc.PayReqString - 166, // 227: lnrpc.Lightning.ListPayments:input_type -> lnrpc.ListPaymentsRequest - 168, // 228: lnrpc.Lightning.DeletePayment:input_type -> lnrpc.DeletePaymentRequest - 169, // 229: lnrpc.Lightning.DeleteAllPayments:input_type -> lnrpc.DeleteAllPaymentsRequest - 133, // 230: lnrpc.Lightning.DescribeGraph:input_type -> lnrpc.ChannelGraphRequest - 135, // 231: lnrpc.Lightning.GetNodeMetrics:input_type -> lnrpc.NodeMetricsRequest - 138, // 232: lnrpc.Lightning.GetChanInfo:input_type -> lnrpc.ChanInfoRequest - 127, // 233: lnrpc.Lightning.GetNodeInfo:input_type -> lnrpc.NodeInfoRequest - 119, // 234: lnrpc.Lightning.QueryRoutes:input_type -> lnrpc.QueryRoutesRequest - 139, // 235: lnrpc.Lightning.GetNetworkInfo:input_type -> lnrpc.NetworkInfoRequest - 141, // 236: lnrpc.Lightning.StopDaemon:input_type -> lnrpc.StopRequest - 143, // 237: lnrpc.Lightning.SubscribeChannelGraph:input_type -> lnrpc.GraphTopologySubscription - 174, // 238: lnrpc.Lightning.DebugLevel:input_type -> lnrpc.DebugLevelRequest - 179, // 239: lnrpc.Lightning.FeeReport:input_type -> lnrpc.FeeReportRequest - 183, // 240: lnrpc.Lightning.UpdateChannelPolicy:input_type -> lnrpc.PolicyUpdateRequest - 186, // 241: lnrpc.Lightning.ForwardingHistory:input_type -> lnrpc.ForwardingHistoryRequest - 189, // 242: lnrpc.Lightning.ExportChannelBackup:input_type -> lnrpc.ExportChannelBackupRequest - 192, // 243: lnrpc.Lightning.ExportAllChannelBackups:input_type -> lnrpc.ChanBackupExportRequest - 193, // 244: lnrpc.Lightning.VerifyChanBackup:input_type -> lnrpc.ChanBackupSnapshot - 195, // 245: lnrpc.Lightning.RestoreChannelBackups:input_type -> lnrpc.RestoreChanBackupRequest - 197, // 246: lnrpc.Lightning.SubscribeChannelBackups:input_type -> lnrpc.ChannelBackupSubscription - 200, // 247: lnrpc.Lightning.BakeMacaroon:input_type -> lnrpc.BakeMacaroonRequest - 202, // 248: lnrpc.Lightning.ListMacaroonIDs:input_type -> lnrpc.ListMacaroonIDsRequest - 204, // 249: lnrpc.Lightning.DeleteMacaroonID:input_type -> lnrpc.DeleteMacaroonIDRequest - 207, // 250: lnrpc.Lightning.ListPermissions:input_type -> lnrpc.ListPermissionsRequest - 213, // 251: lnrpc.Lightning.CheckMacaroonPermissions:input_type -> lnrpc.CheckMacPermRequest - 218, // 252: lnrpc.Lightning.RegisterRPCMiddleware:input_type -> lnrpc.RPCMiddlewareResponse - 25, // 253: lnrpc.Lightning.SendCustomMessage:input_type -> lnrpc.SendCustomMessageRequest - 23, // 254: lnrpc.Lightning.SubscribeCustomMessages:input_type -> lnrpc.SubscribeCustomMessagesRequest - 66, // 255: lnrpc.Lightning.ListAliases:input_type -> lnrpc.ListAliasesRequest - 21, // 256: lnrpc.Lightning.LookupHtlcResolution:input_type -> lnrpc.LookupHtlcResolutionRequest - 115, // 257: lnrpc.Lightning.WalletBalance:output_type -> lnrpc.WalletBalanceResponse - 118, // 258: lnrpc.Lightning.ChannelBalance:output_type -> lnrpc.ChannelBalanceResponse - 31, // 259: lnrpc.Lightning.GetTransactions:output_type -> lnrpc.TransactionDetails - 43, // 260: lnrpc.Lightning.EstimateFee:output_type -> lnrpc.EstimateFeeResponse - 47, // 261: lnrpc.Lightning.SendCoins:output_type -> lnrpc.SendCoinsResponse - 49, // 262: lnrpc.Lightning.ListUnspent:output_type -> lnrpc.ListUnspentResponse - 29, // 263: lnrpc.Lightning.SubscribeTransactions:output_type -> lnrpc.Transaction - 45, // 264: lnrpc.Lightning.SendMany:output_type -> lnrpc.SendManyResponse - 51, // 265: lnrpc.Lightning.NewAddress:output_type -> lnrpc.NewAddressResponse - 53, // 266: lnrpc.Lightning.SignMessage:output_type -> lnrpc.SignMessageResponse - 55, // 267: lnrpc.Lightning.VerifyMessage:output_type -> lnrpc.VerifyMessageResponse - 57, // 268: lnrpc.Lightning.ConnectPeer:output_type -> lnrpc.ConnectPeerResponse - 59, // 269: lnrpc.Lightning.DisconnectPeer:output_type -> lnrpc.DisconnectPeerResponse - 75, // 270: lnrpc.Lightning.ListPeers:output_type -> lnrpc.ListPeersResponse - 77, // 271: lnrpc.Lightning.SubscribePeerEvents:output_type -> lnrpc.PeerEvent - 79, // 272: lnrpc.Lightning.GetInfo:output_type -> lnrpc.GetInfoResponse - 81, // 273: lnrpc.Lightning.GetDebugInfo:output_type -> lnrpc.GetDebugInfoResponse - 83, // 274: lnrpc.Lightning.GetRecoveryInfo:output_type -> lnrpc.GetRecoveryInfoResponse - 110, // 275: lnrpc.Lightning.PendingChannels:output_type -> lnrpc.PendingChannelsResponse - 64, // 276: lnrpc.Lightning.ListChannels:output_type -> lnrpc.ListChannelsResponse - 112, // 277: lnrpc.Lightning.SubscribeChannelEvents:output_type -> lnrpc.ChannelEventUpdate - 71, // 278: lnrpc.Lightning.ClosedChannels:output_type -> lnrpc.ClosedChannelsResponse - 38, // 279: lnrpc.Lightning.OpenChannelSync:output_type -> lnrpc.ChannelPoint - 97, // 280: lnrpc.Lightning.OpenChannel:output_type -> lnrpc.OpenStatusUpdate - 95, // 281: lnrpc.Lightning.BatchOpenChannel:output_type -> lnrpc.BatchOpenChannelResponse - 107, // 282: lnrpc.Lightning.FundingStateStep:output_type -> lnrpc.FundingStateStepResp - 36, // 283: lnrpc.Lightning.ChannelAcceptor:output_type -> lnrpc.ChannelAcceptRequest - 89, // 284: lnrpc.Lightning.CloseChannel:output_type -> lnrpc.CloseStatusUpdate - 173, // 285: lnrpc.Lightning.AbandonChannel:output_type -> lnrpc.AbandonChannelResponse - 34, // 286: lnrpc.Lightning.SendPayment:output_type -> lnrpc.SendResponse - 34, // 287: lnrpc.Lightning.SendPaymentSync:output_type -> lnrpc.SendResponse - 34, // 288: lnrpc.Lightning.SendToRoute:output_type -> lnrpc.SendResponse - 34, // 289: lnrpc.Lightning.SendToRouteSync:output_type -> lnrpc.SendResponse - 159, // 290: lnrpc.Lightning.AddInvoice:output_type -> lnrpc.AddInvoiceResponse - 162, // 291: lnrpc.Lightning.ListInvoices:output_type -> lnrpc.ListInvoiceResponse - 155, // 292: lnrpc.Lightning.LookupInvoice:output_type -> lnrpc.Invoice - 155, // 293: lnrpc.Lightning.SubscribeInvoices:output_type -> lnrpc.Invoice - 177, // 294: lnrpc.Lightning.DecodePayReq:output_type -> lnrpc.PayReq - 167, // 295: lnrpc.Lightning.ListPayments:output_type -> lnrpc.ListPaymentsResponse - 170, // 296: lnrpc.Lightning.DeletePayment:output_type -> lnrpc.DeletePaymentResponse - 171, // 297: lnrpc.Lightning.DeleteAllPayments:output_type -> lnrpc.DeleteAllPaymentsResponse - 134, // 298: lnrpc.Lightning.DescribeGraph:output_type -> lnrpc.ChannelGraph - 136, // 299: lnrpc.Lightning.GetNodeMetrics:output_type -> lnrpc.NodeMetricsResponse - 132, // 300: lnrpc.Lightning.GetChanInfo:output_type -> lnrpc.ChannelEdge - 128, // 301: lnrpc.Lightning.GetNodeInfo:output_type -> lnrpc.NodeInfo - 122, // 302: lnrpc.Lightning.QueryRoutes:output_type -> lnrpc.QueryRoutesResponse - 140, // 303: lnrpc.Lightning.GetNetworkInfo:output_type -> lnrpc.NetworkInfo - 142, // 304: lnrpc.Lightning.StopDaemon:output_type -> lnrpc.StopResponse - 144, // 305: lnrpc.Lightning.SubscribeChannelGraph:output_type -> lnrpc.GraphTopologyUpdate - 175, // 306: lnrpc.Lightning.DebugLevel:output_type -> lnrpc.DebugLevelResponse - 181, // 307: lnrpc.Lightning.FeeReport:output_type -> lnrpc.FeeReportResponse - 185, // 308: lnrpc.Lightning.UpdateChannelPolicy:output_type -> lnrpc.PolicyUpdateResponse - 188, // 309: lnrpc.Lightning.ForwardingHistory:output_type -> lnrpc.ForwardingHistoryResponse - 190, // 310: lnrpc.Lightning.ExportChannelBackup:output_type -> lnrpc.ChannelBackup - 193, // 311: lnrpc.Lightning.ExportAllChannelBackups:output_type -> lnrpc.ChanBackupSnapshot - 198, // 312: lnrpc.Lightning.VerifyChanBackup:output_type -> lnrpc.VerifyChanBackupResponse - 196, // 313: lnrpc.Lightning.RestoreChannelBackups:output_type -> lnrpc.RestoreBackupResponse - 193, // 314: lnrpc.Lightning.SubscribeChannelBackups:output_type -> lnrpc.ChanBackupSnapshot - 201, // 315: lnrpc.Lightning.BakeMacaroon:output_type -> lnrpc.BakeMacaroonResponse - 203, // 316: lnrpc.Lightning.ListMacaroonIDs:output_type -> lnrpc.ListMacaroonIDsResponse - 205, // 317: lnrpc.Lightning.DeleteMacaroonID:output_type -> lnrpc.DeleteMacaroonIDResponse - 208, // 318: lnrpc.Lightning.ListPermissions:output_type -> lnrpc.ListPermissionsResponse - 214, // 319: lnrpc.Lightning.CheckMacaroonPermissions:output_type -> lnrpc.CheckMacPermResponse - 215, // 320: lnrpc.Lightning.RegisterRPCMiddleware:output_type -> lnrpc.RPCMiddlewareRequest - 26, // 321: lnrpc.Lightning.SendCustomMessage:output_type -> lnrpc.SendCustomMessageResponse - 24, // 322: lnrpc.Lightning.SubscribeCustomMessages:output_type -> lnrpc.CustomMessage - 67, // 323: lnrpc.Lightning.ListAliases:output_type -> lnrpc.ListAliasesResponse - 22, // 324: lnrpc.Lightning.LookupHtlcResolution:output_type -> lnrpc.LookupHtlcResolutionResponse - 257, // [257:325] is the sub-list for method output_type - 189, // [189:257] is the sub-list for method input_type - 189, // [189:189] is the sub-list for extension type_name - 189, // [189:189] is the sub-list for extension extendee - 0, // [0:189] is the sub-list for field type_name + 245, // 136: lnrpc.Payment.first_hop_custom_records:type_name -> lnrpc.Payment.FirstHopCustomRecordsEntry + 19, // 137: lnrpc.HTLCAttempt.status:type_name -> lnrpc.HTLCAttempt.HTLCStatus + 126, // 138: lnrpc.HTLCAttempt.route:type_name -> lnrpc.Route + 209, // 139: lnrpc.HTLCAttempt.failure:type_name -> lnrpc.Failure + 164, // 140: lnrpc.ListPaymentsResponse.payments:type_name -> lnrpc.Payment + 38, // 141: lnrpc.AbandonChannelRequest.channel_point:type_name -> lnrpc.ChannelPoint + 150, // 142: lnrpc.PayReq.route_hints:type_name -> lnrpc.RouteHint + 246, // 143: lnrpc.PayReq.features:type_name -> lnrpc.PayReq.FeaturesEntry + 151, // 144: lnrpc.PayReq.blinded_paths:type_name -> lnrpc.BlindedPaymentPath + 180, // 145: lnrpc.FeeReportResponse.channel_fees:type_name -> lnrpc.ChannelFeeReport + 38, // 146: lnrpc.PolicyUpdateRequest.chan_point:type_name -> lnrpc.ChannelPoint + 182, // 147: lnrpc.PolicyUpdateRequest.inbound_fee:type_name -> lnrpc.InboundFee + 39, // 148: lnrpc.FailedUpdate.outpoint:type_name -> lnrpc.OutPoint + 11, // 149: lnrpc.FailedUpdate.reason:type_name -> lnrpc.UpdateFailure + 184, // 150: lnrpc.PolicyUpdateResponse.failed_updates:type_name -> lnrpc.FailedUpdate + 187, // 151: lnrpc.ForwardingHistoryResponse.forwarding_events:type_name -> lnrpc.ForwardingEvent + 38, // 152: lnrpc.ExportChannelBackupRequest.chan_point:type_name -> lnrpc.ChannelPoint + 38, // 153: lnrpc.ChannelBackup.chan_point:type_name -> lnrpc.ChannelPoint + 38, // 154: lnrpc.MultiChanBackup.chan_points:type_name -> lnrpc.ChannelPoint + 194, // 155: lnrpc.ChanBackupSnapshot.single_chan_backups:type_name -> lnrpc.ChannelBackups + 191, // 156: lnrpc.ChanBackupSnapshot.multi_chan_backup:type_name -> lnrpc.MultiChanBackup + 190, // 157: lnrpc.ChannelBackups.chan_backups:type_name -> lnrpc.ChannelBackup + 194, // 158: lnrpc.RestoreChanBackupRequest.chan_backups:type_name -> lnrpc.ChannelBackups + 199, // 159: lnrpc.BakeMacaroonRequest.permissions:type_name -> lnrpc.MacaroonPermission + 199, // 160: lnrpc.MacaroonPermissionList.permissions:type_name -> lnrpc.MacaroonPermission + 247, // 161: lnrpc.ListPermissionsResponse.method_permissions:type_name -> lnrpc.ListPermissionsResponse.MethodPermissionsEntry + 20, // 162: lnrpc.Failure.code:type_name -> lnrpc.Failure.FailureCode + 210, // 163: lnrpc.Failure.channel_update:type_name -> lnrpc.ChannelUpdate + 212, // 164: lnrpc.MacaroonId.ops:type_name -> lnrpc.Op + 199, // 165: lnrpc.CheckMacPermRequest.permissions:type_name -> lnrpc.MacaroonPermission + 216, // 166: lnrpc.RPCMiddlewareRequest.stream_auth:type_name -> lnrpc.StreamAuth + 217, // 167: lnrpc.RPCMiddlewareRequest.request:type_name -> lnrpc.RPCMessage + 217, // 168: lnrpc.RPCMiddlewareRequest.response:type_name -> lnrpc.RPCMessage + 219, // 169: lnrpc.RPCMiddlewareResponse.register:type_name -> lnrpc.MiddlewareRegistration + 220, // 170: lnrpc.RPCMiddlewareResponse.feedback:type_name -> lnrpc.InterceptFeedback + 178, // 171: lnrpc.Peer.FeaturesEntry.value:type_name -> lnrpc.Feature + 178, // 172: lnrpc.GetInfoResponse.FeaturesEntry.value:type_name -> lnrpc.Feature + 4, // 173: lnrpc.PendingChannelsResponse.PendingChannel.initiator:type_name -> lnrpc.Initiator + 3, // 174: lnrpc.PendingChannelsResponse.PendingChannel.commitment_type:type_name -> lnrpc.CommitmentType + 227, // 175: lnrpc.PendingChannelsResponse.PendingOpenChannel.channel:type_name -> lnrpc.PendingChannelsResponse.PendingChannel + 227, // 176: lnrpc.PendingChannelsResponse.WaitingCloseChannel.channel:type_name -> lnrpc.PendingChannelsResponse.PendingChannel + 230, // 177: lnrpc.PendingChannelsResponse.WaitingCloseChannel.commitments:type_name -> lnrpc.PendingChannelsResponse.Commitments + 227, // 178: lnrpc.PendingChannelsResponse.ClosedChannel.channel:type_name -> lnrpc.PendingChannelsResponse.PendingChannel + 227, // 179: lnrpc.PendingChannelsResponse.ForceClosedChannel.channel:type_name -> lnrpc.PendingChannelsResponse.PendingChannel + 108, // 180: lnrpc.PendingChannelsResponse.ForceClosedChannel.pending_htlcs:type_name -> lnrpc.PendingHTLC + 15, // 181: lnrpc.PendingChannelsResponse.ForceClosedChannel.anchor:type_name -> lnrpc.PendingChannelsResponse.ForceClosedChannel.AnchorState + 113, // 182: lnrpc.WalletBalanceResponse.AccountBalanceEntry.value:type_name -> lnrpc.WalletAccountBalance + 178, // 183: lnrpc.LightningNode.FeaturesEntry.value:type_name -> lnrpc.Feature + 137, // 184: lnrpc.NodeMetricsResponse.BetweennessCentralityEntry.value:type_name -> lnrpc.FloatMetric + 178, // 185: lnrpc.NodeUpdate.FeaturesEntry.value:type_name -> lnrpc.Feature + 178, // 186: lnrpc.Invoice.FeaturesEntry.value:type_name -> lnrpc.Feature + 154, // 187: lnrpc.Invoice.AmpInvoiceStateEntry.value:type_name -> lnrpc.AMPInvoiceState + 178, // 188: lnrpc.PayReq.FeaturesEntry.value:type_name -> lnrpc.Feature + 206, // 189: lnrpc.ListPermissionsResponse.MethodPermissionsEntry.value:type_name -> lnrpc.MacaroonPermissionList + 114, // 190: lnrpc.Lightning.WalletBalance:input_type -> lnrpc.WalletBalanceRequest + 117, // 191: lnrpc.Lightning.ChannelBalance:input_type -> lnrpc.ChannelBalanceRequest + 30, // 192: lnrpc.Lightning.GetTransactions:input_type -> lnrpc.GetTransactionsRequest + 42, // 193: lnrpc.Lightning.EstimateFee:input_type -> lnrpc.EstimateFeeRequest + 46, // 194: lnrpc.Lightning.SendCoins:input_type -> lnrpc.SendCoinsRequest + 48, // 195: lnrpc.Lightning.ListUnspent:input_type -> lnrpc.ListUnspentRequest + 30, // 196: lnrpc.Lightning.SubscribeTransactions:input_type -> lnrpc.GetTransactionsRequest + 44, // 197: lnrpc.Lightning.SendMany:input_type -> lnrpc.SendManyRequest + 50, // 198: lnrpc.Lightning.NewAddress:input_type -> lnrpc.NewAddressRequest + 52, // 199: lnrpc.Lightning.SignMessage:input_type -> lnrpc.SignMessageRequest + 54, // 200: lnrpc.Lightning.VerifyMessage:input_type -> lnrpc.VerifyMessageRequest + 56, // 201: lnrpc.Lightning.ConnectPeer:input_type -> lnrpc.ConnectPeerRequest + 58, // 202: lnrpc.Lightning.DisconnectPeer:input_type -> lnrpc.DisconnectPeerRequest + 74, // 203: lnrpc.Lightning.ListPeers:input_type -> lnrpc.ListPeersRequest + 76, // 204: lnrpc.Lightning.SubscribePeerEvents:input_type -> lnrpc.PeerEventSubscription + 78, // 205: lnrpc.Lightning.GetInfo:input_type -> lnrpc.GetInfoRequest + 80, // 206: lnrpc.Lightning.GetDebugInfo:input_type -> lnrpc.GetDebugInfoRequest + 82, // 207: lnrpc.Lightning.GetRecoveryInfo:input_type -> lnrpc.GetRecoveryInfoRequest + 109, // 208: lnrpc.Lightning.PendingChannels:input_type -> lnrpc.PendingChannelsRequest + 63, // 209: lnrpc.Lightning.ListChannels:input_type -> lnrpc.ListChannelsRequest + 111, // 210: lnrpc.Lightning.SubscribeChannelEvents:input_type -> lnrpc.ChannelEventSubscription + 70, // 211: lnrpc.Lightning.ClosedChannels:input_type -> lnrpc.ClosedChannelsRequest + 96, // 212: lnrpc.Lightning.OpenChannelSync:input_type -> lnrpc.OpenChannelRequest + 96, // 213: lnrpc.Lightning.OpenChannel:input_type -> lnrpc.OpenChannelRequest + 93, // 214: lnrpc.Lightning.BatchOpenChannel:input_type -> lnrpc.BatchOpenChannelRequest + 106, // 215: lnrpc.Lightning.FundingStateStep:input_type -> lnrpc.FundingTransitionMsg + 37, // 216: lnrpc.Lightning.ChannelAcceptor:input_type -> lnrpc.ChannelAcceptResponse + 88, // 217: lnrpc.Lightning.CloseChannel:input_type -> lnrpc.CloseChannelRequest + 172, // 218: lnrpc.Lightning.AbandonChannel:input_type -> lnrpc.AbandonChannelRequest + 33, // 219: lnrpc.Lightning.SendPayment:input_type -> lnrpc.SendRequest + 33, // 220: lnrpc.Lightning.SendPaymentSync:input_type -> lnrpc.SendRequest + 35, // 221: lnrpc.Lightning.SendToRoute:input_type -> lnrpc.SendToRouteRequest + 35, // 222: lnrpc.Lightning.SendToRouteSync:input_type -> lnrpc.SendToRouteRequest + 155, // 223: lnrpc.Lightning.AddInvoice:input_type -> lnrpc.Invoice + 161, // 224: lnrpc.Lightning.ListInvoices:input_type -> lnrpc.ListInvoiceRequest + 160, // 225: lnrpc.Lightning.LookupInvoice:input_type -> lnrpc.PaymentHash + 163, // 226: lnrpc.Lightning.SubscribeInvoices:input_type -> lnrpc.InvoiceSubscription + 176, // 227: lnrpc.Lightning.DecodePayReq:input_type -> lnrpc.PayReqString + 166, // 228: lnrpc.Lightning.ListPayments:input_type -> lnrpc.ListPaymentsRequest + 168, // 229: lnrpc.Lightning.DeletePayment:input_type -> lnrpc.DeletePaymentRequest + 169, // 230: lnrpc.Lightning.DeleteAllPayments:input_type -> lnrpc.DeleteAllPaymentsRequest + 133, // 231: lnrpc.Lightning.DescribeGraph:input_type -> lnrpc.ChannelGraphRequest + 135, // 232: lnrpc.Lightning.GetNodeMetrics:input_type -> lnrpc.NodeMetricsRequest + 138, // 233: lnrpc.Lightning.GetChanInfo:input_type -> lnrpc.ChanInfoRequest + 127, // 234: lnrpc.Lightning.GetNodeInfo:input_type -> lnrpc.NodeInfoRequest + 119, // 235: lnrpc.Lightning.QueryRoutes:input_type -> lnrpc.QueryRoutesRequest + 139, // 236: lnrpc.Lightning.GetNetworkInfo:input_type -> lnrpc.NetworkInfoRequest + 141, // 237: lnrpc.Lightning.StopDaemon:input_type -> lnrpc.StopRequest + 143, // 238: lnrpc.Lightning.SubscribeChannelGraph:input_type -> lnrpc.GraphTopologySubscription + 174, // 239: lnrpc.Lightning.DebugLevel:input_type -> lnrpc.DebugLevelRequest + 179, // 240: lnrpc.Lightning.FeeReport:input_type -> lnrpc.FeeReportRequest + 183, // 241: lnrpc.Lightning.UpdateChannelPolicy:input_type -> lnrpc.PolicyUpdateRequest + 186, // 242: lnrpc.Lightning.ForwardingHistory:input_type -> lnrpc.ForwardingHistoryRequest + 189, // 243: lnrpc.Lightning.ExportChannelBackup:input_type -> lnrpc.ExportChannelBackupRequest + 192, // 244: lnrpc.Lightning.ExportAllChannelBackups:input_type -> lnrpc.ChanBackupExportRequest + 193, // 245: lnrpc.Lightning.VerifyChanBackup:input_type -> lnrpc.ChanBackupSnapshot + 195, // 246: lnrpc.Lightning.RestoreChannelBackups:input_type -> lnrpc.RestoreChanBackupRequest + 197, // 247: lnrpc.Lightning.SubscribeChannelBackups:input_type -> lnrpc.ChannelBackupSubscription + 200, // 248: lnrpc.Lightning.BakeMacaroon:input_type -> lnrpc.BakeMacaroonRequest + 202, // 249: lnrpc.Lightning.ListMacaroonIDs:input_type -> lnrpc.ListMacaroonIDsRequest + 204, // 250: lnrpc.Lightning.DeleteMacaroonID:input_type -> lnrpc.DeleteMacaroonIDRequest + 207, // 251: lnrpc.Lightning.ListPermissions:input_type -> lnrpc.ListPermissionsRequest + 213, // 252: lnrpc.Lightning.CheckMacaroonPermissions:input_type -> lnrpc.CheckMacPermRequest + 218, // 253: lnrpc.Lightning.RegisterRPCMiddleware:input_type -> lnrpc.RPCMiddlewareResponse + 25, // 254: lnrpc.Lightning.SendCustomMessage:input_type -> lnrpc.SendCustomMessageRequest + 23, // 255: lnrpc.Lightning.SubscribeCustomMessages:input_type -> lnrpc.SubscribeCustomMessagesRequest + 66, // 256: lnrpc.Lightning.ListAliases:input_type -> lnrpc.ListAliasesRequest + 21, // 257: lnrpc.Lightning.LookupHtlcResolution:input_type -> lnrpc.LookupHtlcResolutionRequest + 115, // 258: lnrpc.Lightning.WalletBalance:output_type -> lnrpc.WalletBalanceResponse + 118, // 259: lnrpc.Lightning.ChannelBalance:output_type -> lnrpc.ChannelBalanceResponse + 31, // 260: lnrpc.Lightning.GetTransactions:output_type -> lnrpc.TransactionDetails + 43, // 261: lnrpc.Lightning.EstimateFee:output_type -> lnrpc.EstimateFeeResponse + 47, // 262: lnrpc.Lightning.SendCoins:output_type -> lnrpc.SendCoinsResponse + 49, // 263: lnrpc.Lightning.ListUnspent:output_type -> lnrpc.ListUnspentResponse + 29, // 264: lnrpc.Lightning.SubscribeTransactions:output_type -> lnrpc.Transaction + 45, // 265: lnrpc.Lightning.SendMany:output_type -> lnrpc.SendManyResponse + 51, // 266: lnrpc.Lightning.NewAddress:output_type -> lnrpc.NewAddressResponse + 53, // 267: lnrpc.Lightning.SignMessage:output_type -> lnrpc.SignMessageResponse + 55, // 268: lnrpc.Lightning.VerifyMessage:output_type -> lnrpc.VerifyMessageResponse + 57, // 269: lnrpc.Lightning.ConnectPeer:output_type -> lnrpc.ConnectPeerResponse + 59, // 270: lnrpc.Lightning.DisconnectPeer:output_type -> lnrpc.DisconnectPeerResponse + 75, // 271: lnrpc.Lightning.ListPeers:output_type -> lnrpc.ListPeersResponse + 77, // 272: lnrpc.Lightning.SubscribePeerEvents:output_type -> lnrpc.PeerEvent + 79, // 273: lnrpc.Lightning.GetInfo:output_type -> lnrpc.GetInfoResponse + 81, // 274: lnrpc.Lightning.GetDebugInfo:output_type -> lnrpc.GetDebugInfoResponse + 83, // 275: lnrpc.Lightning.GetRecoveryInfo:output_type -> lnrpc.GetRecoveryInfoResponse + 110, // 276: lnrpc.Lightning.PendingChannels:output_type -> lnrpc.PendingChannelsResponse + 64, // 277: lnrpc.Lightning.ListChannels:output_type -> lnrpc.ListChannelsResponse + 112, // 278: lnrpc.Lightning.SubscribeChannelEvents:output_type -> lnrpc.ChannelEventUpdate + 71, // 279: lnrpc.Lightning.ClosedChannels:output_type -> lnrpc.ClosedChannelsResponse + 38, // 280: lnrpc.Lightning.OpenChannelSync:output_type -> lnrpc.ChannelPoint + 97, // 281: lnrpc.Lightning.OpenChannel:output_type -> lnrpc.OpenStatusUpdate + 95, // 282: lnrpc.Lightning.BatchOpenChannel:output_type -> lnrpc.BatchOpenChannelResponse + 107, // 283: lnrpc.Lightning.FundingStateStep:output_type -> lnrpc.FundingStateStepResp + 36, // 284: lnrpc.Lightning.ChannelAcceptor:output_type -> lnrpc.ChannelAcceptRequest + 89, // 285: lnrpc.Lightning.CloseChannel:output_type -> lnrpc.CloseStatusUpdate + 173, // 286: lnrpc.Lightning.AbandonChannel:output_type -> lnrpc.AbandonChannelResponse + 34, // 287: lnrpc.Lightning.SendPayment:output_type -> lnrpc.SendResponse + 34, // 288: lnrpc.Lightning.SendPaymentSync:output_type -> lnrpc.SendResponse + 34, // 289: lnrpc.Lightning.SendToRoute:output_type -> lnrpc.SendResponse + 34, // 290: lnrpc.Lightning.SendToRouteSync:output_type -> lnrpc.SendResponse + 159, // 291: lnrpc.Lightning.AddInvoice:output_type -> lnrpc.AddInvoiceResponse + 162, // 292: lnrpc.Lightning.ListInvoices:output_type -> lnrpc.ListInvoiceResponse + 155, // 293: lnrpc.Lightning.LookupInvoice:output_type -> lnrpc.Invoice + 155, // 294: lnrpc.Lightning.SubscribeInvoices:output_type -> lnrpc.Invoice + 177, // 295: lnrpc.Lightning.DecodePayReq:output_type -> lnrpc.PayReq + 167, // 296: lnrpc.Lightning.ListPayments:output_type -> lnrpc.ListPaymentsResponse + 170, // 297: lnrpc.Lightning.DeletePayment:output_type -> lnrpc.DeletePaymentResponse + 171, // 298: lnrpc.Lightning.DeleteAllPayments:output_type -> lnrpc.DeleteAllPaymentsResponse + 134, // 299: lnrpc.Lightning.DescribeGraph:output_type -> lnrpc.ChannelGraph + 136, // 300: lnrpc.Lightning.GetNodeMetrics:output_type -> lnrpc.NodeMetricsResponse + 132, // 301: lnrpc.Lightning.GetChanInfo:output_type -> lnrpc.ChannelEdge + 128, // 302: lnrpc.Lightning.GetNodeInfo:output_type -> lnrpc.NodeInfo + 122, // 303: lnrpc.Lightning.QueryRoutes:output_type -> lnrpc.QueryRoutesResponse + 140, // 304: lnrpc.Lightning.GetNetworkInfo:output_type -> lnrpc.NetworkInfo + 142, // 305: lnrpc.Lightning.StopDaemon:output_type -> lnrpc.StopResponse + 144, // 306: lnrpc.Lightning.SubscribeChannelGraph:output_type -> lnrpc.GraphTopologyUpdate + 175, // 307: lnrpc.Lightning.DebugLevel:output_type -> lnrpc.DebugLevelResponse + 181, // 308: lnrpc.Lightning.FeeReport:output_type -> lnrpc.FeeReportResponse + 185, // 309: lnrpc.Lightning.UpdateChannelPolicy:output_type -> lnrpc.PolicyUpdateResponse + 188, // 310: lnrpc.Lightning.ForwardingHistory:output_type -> lnrpc.ForwardingHistoryResponse + 190, // 311: lnrpc.Lightning.ExportChannelBackup:output_type -> lnrpc.ChannelBackup + 193, // 312: lnrpc.Lightning.ExportAllChannelBackups:output_type -> lnrpc.ChanBackupSnapshot + 198, // 313: lnrpc.Lightning.VerifyChanBackup:output_type -> lnrpc.VerifyChanBackupResponse + 196, // 314: lnrpc.Lightning.RestoreChannelBackups:output_type -> lnrpc.RestoreBackupResponse + 193, // 315: lnrpc.Lightning.SubscribeChannelBackups:output_type -> lnrpc.ChanBackupSnapshot + 201, // 316: lnrpc.Lightning.BakeMacaroon:output_type -> lnrpc.BakeMacaroonResponse + 203, // 317: lnrpc.Lightning.ListMacaroonIDs:output_type -> lnrpc.ListMacaroonIDsResponse + 205, // 318: lnrpc.Lightning.DeleteMacaroonID:output_type -> lnrpc.DeleteMacaroonIDResponse + 208, // 319: lnrpc.Lightning.ListPermissions:output_type -> lnrpc.ListPermissionsResponse + 214, // 320: lnrpc.Lightning.CheckMacaroonPermissions:output_type -> lnrpc.CheckMacPermResponse + 215, // 321: lnrpc.Lightning.RegisterRPCMiddleware:output_type -> lnrpc.RPCMiddlewareRequest + 26, // 322: lnrpc.Lightning.SendCustomMessage:output_type -> lnrpc.SendCustomMessageResponse + 24, // 323: lnrpc.Lightning.SubscribeCustomMessages:output_type -> lnrpc.CustomMessage + 67, // 324: lnrpc.Lightning.ListAliases:output_type -> lnrpc.ListAliasesResponse + 22, // 325: lnrpc.Lightning.LookupHtlcResolution:output_type -> lnrpc.LookupHtlcResolutionResponse + 258, // [258:326] is the sub-list for method output_type + 190, // [190:258] is the sub-list for method input_type + 190, // [190:190] is the sub-list for extension type_name + 190, // [190:190] is the sub-list for extension extendee + 0, // [0:190] is the sub-list for field type_name } func init() { file_lightning_proto_init() } @@ -24406,7 +24429,7 @@ func file_lightning_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_lightning_proto_rawDesc, NumEnums: 21, - NumMessages: 226, + NumMessages: 227, NumExtensions: 0, NumServices: 1, }, diff --git a/lnrpc/lightning.proto b/lnrpc/lightning.proto index 201bb0df2b..0bdee236c0 100644 --- a/lnrpc/lightning.proto +++ b/lnrpc/lightning.proto @@ -4162,6 +4162,12 @@ message Payment { uint64 payment_index = 15; PaymentFailureReason failure_reason = 16; + + /* + The custom TLV records that were sent to the first hop as part of the HTLC + wire message for this payment. + */ + map first_hop_custom_records = 17; } message HTLCAttempt { diff --git a/lnrpc/lightning.swagger.json b/lnrpc/lightning.swagger.json index bcd9e438b0..0db9ace9b1 100644 --- a/lnrpc/lightning.swagger.json +++ b/lnrpc/lightning.swagger.json @@ -6417,6 +6417,14 @@ }, "failure_reason": { "$ref": "#/definitions/lnrpcPaymentFailureReason" + }, + "first_hop_custom_records": { + "type": "object", + "additionalProperties": { + "type": "string", + "format": "byte" + }, + "description": "The custom TLV records that were sent to the first hop as part of the HTLC\nwire message for this payment." } } }, diff --git a/lnrpc/routerrpc/router.swagger.json b/lnrpc/routerrpc/router.swagger.json index e50061604c..3469fd34c6 100644 --- a/lnrpc/routerrpc/router.swagger.json +++ b/lnrpc/routerrpc/router.swagger.json @@ -1095,6 +1095,14 @@ }, "failure_reason": { "$ref": "#/definitions/lnrpcPaymentFailureReason" + }, + "first_hop_custom_records": { + "type": "object", + "additionalProperties": { + "type": "string", + "format": "byte" + }, + "description": "The custom TLV records that were sent to the first hop as part of the HTLC\nwire message for this payment." } } }, diff --git a/lnrpc/routerrpc/router_backend.go b/lnrpc/routerrpc/router_backend.go index 9b41d9d8c1..4eab3bb1a4 100644 --- a/lnrpc/routerrpc/router_backend.go +++ b/lnrpc/routerrpc/router_backend.go @@ -1682,21 +1682,22 @@ func (r *RouterBackend) MarshallPayment(payment *channeldb.MPPayment) ( return &lnrpc.Payment{ // TODO: set this to setID for AMP-payments? - PaymentHash: hex.EncodeToString(paymentID[:]), - Value: satValue, - ValueMsat: msatValue, - ValueSat: satValue, - CreationDate: payment.Info.CreationTime.Unix(), - CreationTimeNs: creationTimeNS, - Fee: int64(fee.ToSatoshis()), - FeeSat: int64(fee.ToSatoshis()), - FeeMsat: int64(fee), - PaymentPreimage: hex.EncodeToString(preimage[:]), - PaymentRequest: string(payment.Info.PaymentRequest), - Status: status, - Htlcs: htlcs, - PaymentIndex: payment.SequenceNum, - FailureReason: failureReason, + PaymentHash: hex.EncodeToString(paymentID[:]), + Value: satValue, + ValueMsat: msatValue, + ValueSat: satValue, + CreationDate: payment.Info.CreationTime.Unix(), + CreationTimeNs: creationTimeNS, + Fee: int64(fee.ToSatoshis()), + FeeSat: int64(fee.ToSatoshis()), + FeeMsat: int64(fee), + PaymentPreimage: hex.EncodeToString(preimage[:]), + PaymentRequest: string(payment.Info.PaymentRequest), + Status: status, + Htlcs: htlcs, + PaymentIndex: payment.SequenceNum, + FailureReason: failureReason, + FirstHopCustomRecords: payment.Info.FirstHopCustomRecords, }, nil } From c70832a823f726e6cfb586de11e7c355c4d8a942 Mon Sep 17 00:00:00 2001 From: George Tsagkarelis Date: Wed, 15 May 2024 17:25:53 +0200 Subject: [PATCH 070/218] lnwallet: expose commitment blob from channel --- lnwallet/channel.go | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/lnwallet/channel.go b/lnwallet/channel.go index 7af99f527b..49d8df566c 100644 --- a/lnwallet/channel.go +++ b/lnwallet/channel.go @@ -9119,3 +9119,19 @@ func (lc *LightningChannel) MultiSigKeys() (keychain.KeyDescriptor, return lc.channelState.LocalChanCfg.MultiSigKey, lc.channelState.RemoteChanCfg.MultiSigKey } + +// LocalCommitmentBlob returns the custom blob of the local commitment. +func (lc *LightningChannel) LocalCommitmentBlob() fn.Option[tlv.Blob] { + lc.RLock() + defer lc.RUnlock() + + chanState := lc.channelState + localBalance := chanState.LocalCommitment.CustomBlob + + return fn.MapOption(func(b tlv.Blob) tlv.Blob { + newBlob := make([]byte, len(b)) + copy(newBlob, b) + + return newBlob + })(localBalance) +} From 69430ce04269a39fff5fe2c47c129239396ff404 Mon Sep 17 00:00:00 2001 From: George Tsagkarelis Date: Thu, 2 May 2024 16:52:47 +0200 Subject: [PATCH 071/218] htlcswitch: expose custom channel blob from link --- htlcswitch/interfaces.go | 11 +++++++++++ htlcswitch/link.go | 26 ++++++++++++++++++++++++++ htlcswitch/mock.go | 9 +++++++++ 3 files changed, 46 insertions(+) diff --git a/htlcswitch/interfaces.go b/htlcswitch/interfaces.go index f5b3bbe98c..4e6384b978 100644 --- a/htlcswitch/interfaces.go +++ b/htlcswitch/interfaces.go @@ -13,6 +13,7 @@ import ( "github.com/lightningnetwork/lnd/lnwallet/chainfee" "github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/record" + "github.com/lightningnetwork/lnd/tlv" ) // InvoiceDatabase is an interface which represents the persistent subsystem @@ -278,6 +279,16 @@ type ChannelLink interface { // have buffered messages. AttachMailBox(MailBox) + // FundingCustomBlob returns the custom funding blob of the channel that + // this link is associated with. The funding blob represents static + // information about the channel that was created at channel funding + // time. + FundingCustomBlob() fn.Option[tlv.Blob] + + // CommitmentCustomBlob returns the custom blob of the current local + // commitment of the channel that this link is associated with. + CommitmentCustomBlob() fn.Option[tlv.Blob] + // Start/Stop are used to initiate the start/stop of the channel link // functioning. Start() error diff --git a/htlcswitch/link.go b/htlcswitch/link.go index 57d5319354..a8e35afe68 100644 --- a/htlcswitch/link.go +++ b/htlcswitch/link.go @@ -31,6 +31,7 @@ import ( "github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/queue" "github.com/lightningnetwork/lnd/ticker" + "github.com/lightningnetwork/lnd/tlv" ) func init() { @@ -4105,3 +4106,28 @@ func (l *channelLink) failf(linkErr LinkFailureError, format string, l.failed = true l.cfg.OnChannelFailure(l.ChanID(), l.ShortChanID(), linkErr) } + +// FundingCustomBlob returns the custom funding blob of the channel that this +// link is associated with. The funding blob represents static information about +// the channel that was created at channel funding time. +func (l *channelLink) FundingCustomBlob() fn.Option[tlv.Blob] { + if l.channel == nil { + return fn.None[tlv.Blob]() + } + + if l.channel.State() == nil { + return fn.None[tlv.Blob]() + } + + return l.channel.State().CustomBlob +} + +// CommitmentCustomBlob returns the custom blob of the current local commitment +// of the channel that this link is associated with. +func (l *channelLink) CommitmentCustomBlob() fn.Option[tlv.Blob] { + if l.channel == nil { + return fn.None[tlv.Blob]() + } + + return l.channel.LocalCommitmentBlob() +} diff --git a/htlcswitch/mock.go b/htlcswitch/mock.go index c328bc533e..6d27a5be99 100644 --- a/htlcswitch/mock.go +++ b/htlcswitch/mock.go @@ -35,6 +35,7 @@ import ( "github.com/lightningnetwork/lnd/lnwallet/chainfee" "github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/ticker" + "github.com/lightningnetwork/lnd/tlv" ) func isAlias(scid lnwire.ShortChannelID) bool { @@ -950,6 +951,14 @@ func (f *mockChannelLink) OnCommitOnce(LinkDirection, func()) { // TODO(proofofkeags): Implement } +func (f *mockChannelLink) FundingCustomBlob() fn.Option[tlv.Blob] { + return fn.None[tlv.Blob]() +} + +func (f *mockChannelLink) CommitmentCustomBlob() fn.Option[tlv.Blob] { + return fn.None[tlv.Blob]() +} + var _ ChannelLink = (*mockChannelLink)(nil) func newDB() (*channeldb.DB, func(), error) { From f927563049210520d0f50aeba7f0fc9c4e9cdda1 Mon Sep 17 00:00:00 2001 From: George Tsagkarelis Date: Fri, 19 Apr 2024 13:49:45 +0200 Subject: [PATCH 072/218] routing: add TlvTrafficShaper to bandwidth hints --- routing/bandwidth.go | 169 +++++++++++++++++++-- routing/bandwidth_test.go | 36 +++++ routing/integrated_routing_context_test.go | 9 ++ routing/integrated_routing_test.go | 2 +- routing/mock_test.go | 28 +++- routing/payment_lifecycle.go | 1 + routing/payment_lifecycle_test.go | 26 +++- routing/payment_session.go | 13 +- routing/payment_session_source.go | 8 +- routing/payment_session_test.go | 3 + routing/router.go | 42 ++++- routing/router_test.go | 18 ++- 12 files changed, 313 insertions(+), 42 deletions(-) diff --git a/routing/bandwidth.go b/routing/bandwidth.go index 0868255685..a193c654ad 100644 --- a/routing/bandwidth.go +++ b/routing/bandwidth.go @@ -1,10 +1,14 @@ package routing import ( + "fmt" + "github.com/lightningnetwork/lnd/channeldb" + "github.com/lightningnetwork/lnd/fn" "github.com/lightningnetwork/lnd/htlcswitch" "github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/routing/route" + "github.com/lightningnetwork/lnd/tlv" ) // bandwidthHints provides hints about the currently available balance in our @@ -19,6 +23,42 @@ type bandwidthHints interface { // returned. availableChanBandwidth(channelID uint64, amount lnwire.MilliSatoshi) (lnwire.MilliSatoshi, bool) + + // firstHopCustomBlob returns the custom blob for the first hop of the + // payment, if available. + firstHopCustomBlob() fn.Option[tlv.Blob] +} + +// TlvTrafficShaper is an interface that allows the sender to determine if a +// payment should be carried by a channel based on the TLV records that may be +// present in the `update_add_htlc` message or the channel commitment itself. +type TlvTrafficShaper interface { + AuxHtlcModifier + + // ShouldHandleTraffic is called in order to check if the channel + // identified by the provided channel ID may have external mechanisms + // that would allow it to carry out the payment. + ShouldHandleTraffic(cid lnwire.ShortChannelID, + fundingBlob fn.Option[tlv.Blob]) (bool, error) + + // PaymentBandwidth returns the available bandwidth for a custom channel + // decided by the given channel aux blob and HTLC blob. A return value + // of 0 means there is no bandwidth available. To find out if a channel + // is a custom channel that should be handled by the traffic shaper, the + // HandleTraffic method should be called first. + PaymentBandwidth(htlcBlob, commitmentBlob fn.Option[tlv.Blob], + linkBandwidth lnwire.MilliSatoshi) (lnwire.MilliSatoshi, error) +} + +// AuxHtlcModifier is an interface that allows the sender to modify the outgoing +// HTLC of a payment by changing the amount or the wire message tlv records. +type AuxHtlcModifier interface { + // ProduceHtlcExtraData is a function that, based on the previous extra + // data blob of an HTLC, may produce a different blob or modify the + // amount of bitcoin this htlc should carry. + ProduceHtlcExtraData(totalAmount lnwire.MilliSatoshi, + htlcCustomRecords lnwire.CustomRecords) (lnwire.MilliSatoshi, + lnwire.CustomRecords, error) } // getLinkQuery is the function signature used to lookup a link. @@ -29,8 +69,10 @@ type getLinkQuery func(lnwire.ShortChannelID) ( // uses the link lookup provided to query the link for our latest local channel // balances. type bandwidthManager struct { - getLink getLinkQuery - localChans map[lnwire.ShortChannelID]struct{} + getLink getLinkQuery + localChans map[lnwire.ShortChannelID]struct{} + firstHopBlob fn.Option[tlv.Blob] + trafficShaper fn.Option[TlvTrafficShaper] } // newBandwidthManager creates a bandwidth manager for the source node provided @@ -40,11 +82,14 @@ type bandwidthManager struct { // allows us to reduce the number of extraneous attempts as we can skip channels // that are inactive, or just don't have enough bandwidth to carry the payment. func newBandwidthManager(graph Graph, sourceNode route.Vertex, - linkQuery getLinkQuery) (*bandwidthManager, error) { + linkQuery getLinkQuery, firstHopBlob fn.Option[tlv.Blob], + trafficShaper fn.Option[TlvTrafficShaper]) (*bandwidthManager, error) { manager := &bandwidthManager{ - getLink: linkQuery, - localChans: make(map[lnwire.ShortChannelID]struct{}), + getLink: linkQuery, + localChans: make(map[lnwire.ShortChannelID]struct{}), + firstHopBlob: firstHopBlob, + trafficShaper: trafficShaper, } // First, we'll collect the set of outbound edges from the target @@ -89,17 +134,111 @@ func (b *bandwidthManager) getBandwidth(cid lnwire.ShortChannelID, return 0 } - // If our link isn't currently in a state where it can add another + // bandwidthResult is an inline type that we'll use to pass the + // bandwidth result from the external traffic shaper to the main logic + // below. + type bandwidthResult struct { + // bandwidth is the available bandwidth for the channel as + // reported by the external traffic shaper. If the external + // traffic shaper is not handling the channel, this value will + // be fn.None + bandwidth fn.Option[lnwire.MilliSatoshi] + + // htlcAmount is the amount we're going to use to check if we + // can add another HTLC to the channel. If the external traffic + // shaper is handling the channel, we'll use 0 to just sanity + // check the number of HTLCs on the channel, since we don't know + // the actual HTLC amount that will be sent. + htlcAmount fn.Option[lnwire.MilliSatoshi] + } + + var ( + // We will pass the link bandwidth to the external traffic + // shaper. This is the current best estimate for the available + // bandwidth for the link. + linkBandwidth = link.Bandwidth() + + bandwidthErr = func(err error) fn.Result[bandwidthResult] { + return fn.Err[bandwidthResult](err) + } + ) + + result, err := fn.MapOptionZ( + b.trafficShaper, + func(ts TlvTrafficShaper) fn.Result[bandwidthResult] { + fundingBlob := link.FundingCustomBlob() + shouldHandle, err := ts.ShouldHandleTraffic( + cid, fundingBlob, + ) + if err != nil { + return bandwidthErr(fmt.Errorf("traffic "+ + "shaper failed to decide whether to "+ + "handle traffic: %w", err)) + } + + log.Debugf("ShortChannelID=%v: external traffic "+ + "shaper is handling traffic: %v", cid, + shouldHandle) + + // If this channel isn't handled by the external traffic + // shaper, we'll return early. + if !shouldHandle { + return fn.Ok(bandwidthResult{}) + } + + // Ask for a specific bandwidth to be used for the + // channel. + commitmentBlob := link.CommitmentCustomBlob() + auxBandwidth, err := ts.PaymentBandwidth( + b.firstHopBlob, commitmentBlob, linkBandwidth, + ) + if err != nil { + return bandwidthErr(fmt.Errorf("failed to get "+ + "bandwidth from external traffic "+ + "shaper: %w", err)) + } + + log.Debugf("ShortChannelID=%v: external traffic "+ + "shaper reported available bandwidth: %v", cid, + auxBandwidth) + + // We don't know the actual HTLC amount that will be + // sent using the custom channel. But we'll still want + // to make sure we can add another HTLC, using the + // MayAddOutgoingHtlc method below. Passing 0 into that + // method will use the minimum HTLC value for the + // channel, which is okay to just check we don't exceed + // the max number of HTLCs on the channel. A proper + // balance check is done elsewhere. + return fn.Ok(bandwidthResult{ + bandwidth: fn.Some(auxBandwidth), + htlcAmount: fn.Some[lnwire.MilliSatoshi](0), + }) + }, + ).Unpack() + if err != nil { + log.Errorf("ShortChannelID=%v: failed to get bandwidth from "+ + "external traffic shaper: %v", cid, err) + + return 0 + } + + htlcAmount := result.htlcAmount.UnwrapOr(amount) + + // If our link isn't currently in a state where it can add another // outgoing htlc, treat the link as unusable. - if err := link.MayAddOutgoingHtlc(amount); err != nil { - log.Warnf("ShortChannelID=%v: cannot add outgoing htlc: %v", - cid, err) + if err := link.MayAddOutgoingHtlc(htlcAmount); err != nil { + log.Warnf("ShortChannelID=%v: cannot add outgoing "+ + "htlc with amount %v: %v", cid, htlcAmount, err) return 0 } - // Otherwise, we'll return the current best estimate for the available - // bandwidth for the link. - return link.Bandwidth() + // If the external traffic shaper determined the bandwidth, we'll return + // that value, even if it is zero (which would mean no bandwidth is + // available on that channel). + reportedBandwidth := result.bandwidth.UnwrapOr(linkBandwidth) + + return reportedBandwidth } // availableChanBandwidth returns the total available bandwidth for a channel @@ -116,3 +255,9 @@ func (b *bandwidthManager) availableChanBandwidth(channelID uint64, return b.getBandwidth(shortID, amount), true } + +// firstHopCustomBlob returns the custom blob for the first hop of the payment, +// if available. +func (b *bandwidthManager) firstHopCustomBlob() fn.Option[tlv.Blob] { + return b.firstHopBlob +} diff --git a/routing/bandwidth_test.go b/routing/bandwidth_test.go index ef12d69737..4876f09c70 100644 --- a/routing/bandwidth_test.go +++ b/routing/bandwidth_test.go @@ -5,8 +5,10 @@ import ( "github.com/btcsuite/btcd/btcutil" "github.com/go-errors/errors" + "github.com/lightningnetwork/lnd/fn" "github.com/lightningnetwork/lnd/htlcswitch" "github.com/lightningnetwork/lnd/lnwire" + "github.com/lightningnetwork/lnd/tlv" "github.com/stretchr/testify/require" ) @@ -115,6 +117,8 @@ func TestBandwidthManager(t *testing.T) { m, err := newBandwidthManager( g, sourceNode.pubkey, testCase.linkQuery, + fn.None[[]byte](), + fn.Some[TlvTrafficShaper](&mockTrafficShaper{}), ) require.NoError(t, err) @@ -126,3 +130,35 @@ func TestBandwidthManager(t *testing.T) { }) } } + +type mockTrafficShaper struct{} + +// ShouldHandleTraffic is called in order to check if the channel identified +// by the provided channel ID may have external mechanisms that would +// allow it to carry out the payment. +func (*mockTrafficShaper) ShouldHandleTraffic(_ lnwire.ShortChannelID, + _ fn.Option[tlv.Blob]) (bool, error) { + + return true, nil +} + +// PaymentBandwidth returns the available bandwidth for a custom channel +// decided by the given channel aux blob and HTLC blob. A return value +// of 0 means there is no bandwidth available. To find out if a channel +// is a custom channel that should be handled by the traffic shaper, the +// HandleTraffic method should be called first. +func (*mockTrafficShaper) PaymentBandwidth(_, _ fn.Option[tlv.Blob], + linkBandwidth lnwire.MilliSatoshi) (lnwire.MilliSatoshi, error) { + + return linkBandwidth, nil +} + +// ProduceHtlcExtraData is a function that, based on the previous extra +// data blob of an HTLC, may produce a different blob or modify the +// amount of bitcoin this htlc should carry. +func (*mockTrafficShaper) ProduceHtlcExtraData(totalAmount lnwire.MilliSatoshi, + _ lnwire.CustomRecords) (lnwire.MilliSatoshi, lnwire.CustomRecords, + error) { + + return totalAmount, nil, nil +} diff --git a/routing/integrated_routing_context_test.go b/routing/integrated_routing_context_test.go index ee6fed295a..785fa1a50a 100644 --- a/routing/integrated_routing_context_test.go +++ b/routing/integrated_routing_context_test.go @@ -8,9 +8,11 @@ import ( "time" "github.com/lightningnetwork/lnd/channeldb" + "github.com/lightningnetwork/lnd/fn" "github.com/lightningnetwork/lnd/kvdb" "github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/routing/route" + "github.com/lightningnetwork/lnd/tlv" "github.com/lightningnetwork/lnd/zpay32" "github.com/stretchr/testify/require" ) @@ -35,6 +37,10 @@ func (m *mockBandwidthHints) availableChanBandwidth(channelID uint64, return balance, ok } +func (m *mockBandwidthHints) firstHopCustomBlob() fn.Option[tlv.Blob] { + return fn.None[tlv.Blob]() +} + // integratedRoutingContext defines the context in which integrated routing // tests run. type integratedRoutingContext struct { @@ -227,6 +233,9 @@ func (c *integratedRoutingContext) testPayment(maxParts uint32, // Find a route. route, err := session.RequestRoute( amtRemaining, lnwire.MaxMilliSatoshi, inFlightHtlcs, 0, + lnwire.CustomRecords{ + lnwire.MinCustomRecordsTlvType: []byte{1, 2, 3}, + }, ) if err != nil { return attempts, err diff --git a/routing/integrated_routing_test.go b/routing/integrated_routing_test.go index 0e873d6646..4a2447b487 100644 --- a/routing/integrated_routing_test.go +++ b/routing/integrated_routing_test.go @@ -296,7 +296,7 @@ func testMppSend(t *testing.T, testCase *mppSendTestCase) { case err == nil && testCase.expectedFailure: t.Fatal("expected payment to fail") case err != nil && !testCase.expectedFailure: - t.Fatal("expected payment to succeed") + t.Fatalf("expected payment to succeed, got %v", err) } if len(attempts) != testCase.expectedAttempts { diff --git a/routing/mock_test.go b/routing/mock_test.go index 306c182107..99d56c68bd 100644 --- a/routing/mock_test.go +++ b/routing/mock_test.go @@ -9,12 +9,14 @@ import ( "github.com/go-errors/errors" "github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/channeldb/models" + "github.com/lightningnetwork/lnd/fn" "github.com/lightningnetwork/lnd/htlcswitch" "github.com/lightningnetwork/lnd/lntypes" "github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/record" "github.com/lightningnetwork/lnd/routing/route" "github.com/lightningnetwork/lnd/routing/shards" + "github.com/lightningnetwork/lnd/tlv" "github.com/stretchr/testify/mock" ) @@ -104,7 +106,8 @@ type mockPaymentSessionSourceOld struct { var _ PaymentSessionSource = (*mockPaymentSessionSourceOld)(nil) func (m *mockPaymentSessionSourceOld) NewPaymentSession( - _ *LightningPayment) (PaymentSession, error) { + _ *LightningPayment, _ fn.Option[tlv.Blob], + _ fn.Option[TlvTrafficShaper]) (PaymentSession, error) { return &mockPaymentSessionOld{ routes: m.routes, @@ -166,7 +169,8 @@ type mockPaymentSessionOld struct { var _ PaymentSession = (*mockPaymentSessionOld)(nil) func (m *mockPaymentSessionOld) RequestRoute(_, _ lnwire.MilliSatoshi, - _, height uint32) (*route.Route, error) { + _, height uint32, _ lnwire.CustomRecords) (*route.Route, + error) { if m.release != nil { m.release <- struct{}{} @@ -630,9 +634,10 @@ type mockPaymentSessionSource struct { var _ PaymentSessionSource = (*mockPaymentSessionSource)(nil) func (m *mockPaymentSessionSource) NewPaymentSession( - payment *LightningPayment) (PaymentSession, error) { + payment *LightningPayment, firstHopBlob fn.Option[tlv.Blob], + tlvShaper fn.Option[TlvTrafficShaper]) (PaymentSession, error) { - args := m.Called(payment) + args := m.Called(payment, firstHopBlob, tlvShaper) return args.Get(0).(PaymentSession), args.Error(1) } @@ -690,9 +695,12 @@ type mockPaymentSession struct { var _ PaymentSession = (*mockPaymentSession)(nil) func (m *mockPaymentSession) RequestRoute(maxAmt, feeLimit lnwire.MilliSatoshi, - activeShards, height uint32) (*route.Route, error) { + activeShards, height uint32, + firstHopCustomRecords lnwire.CustomRecords) (*route.Route, error) { - args := m.Called(maxAmt, feeLimit, activeShards, height) + args := m.Called( + maxAmt, feeLimit, activeShards, height, firstHopCustomRecords, + ) // Type assertion on nil will fail, so we check and return here. if args.Get(0) == nil { @@ -897,6 +905,14 @@ func (m *mockLink) MayAddOutgoingHtlc(_ lnwire.MilliSatoshi) error { return m.mayAddOutgoingErr } +func (m *mockLink) FundingCustomBlob() fn.Option[tlv.Blob] { + return fn.None[tlv.Blob]() +} + +func (m *mockLink) CommitmentCustomBlob() fn.Option[tlv.Blob] { + return fn.None[tlv.Blob]() +} + type mockShardTracker struct { mock.Mock } diff --git a/routing/payment_lifecycle.go b/routing/payment_lifecycle.go index 2c7fd3b21d..96ff796080 100644 --- a/routing/payment_lifecycle.go +++ b/routing/payment_lifecycle.go @@ -366,6 +366,7 @@ func (p *paymentLifecycle) requestRoute( rt, err := p.paySession.RequestRoute( ps.RemainingAmt, remainingFees, uint32(ps.NumAttemptsInFlight), uint32(p.currentHeight), + p.firstHopCustomRecords, ) // Exit early if there's no error. diff --git a/routing/payment_lifecycle_test.go b/routing/payment_lifecycle_test.go index 4df27523f2..315c1bad58 100644 --- a/routing/payment_lifecycle_test.go +++ b/routing/payment_lifecycle_test.go @@ -9,6 +9,7 @@ import ( "github.com/btcsuite/btcd/btcec/v2" "github.com/go-errors/errors" "github.com/lightningnetwork/lnd/channeldb" + "github.com/lightningnetwork/lnd/fn" "github.com/lightningnetwork/lnd/htlcswitch" "github.com/lightningnetwork/lnd/lnmock" "github.com/lightningnetwork/lnd/lntest/wait" @@ -28,7 +29,11 @@ func createTestPaymentLifecycle() *paymentLifecycle { paymentHash := lntypes.Hash{1, 2, 3} quitChan := make(chan struct{}) rt := &ChannelRouter{ - cfg: &Config{}, + cfg: &Config{ + TrafficShaper: fn.Some[TlvTrafficShaper]( + &mockTrafficShaper{}, + ), + }, quit: quitChan, } @@ -78,6 +83,9 @@ func newTestPaymentLifecycle(t *testing.T) (*paymentLifecycle, *mockers) { Payer: mockPayer, Clock: mockClock, MissionControl: mockMissionControl, + TrafficShaper: fn.Some[TlvTrafficShaper]( + &mockTrafficShaper{}, + ), }, quit: quitChan, } @@ -372,6 +380,7 @@ func TestRequestRouteSucceed(t *testing.T) { // Mock the paySession's `RequestRoute` method to return no error. paySession.On("RequestRoute", mock.Anything, mock.Anything, mock.Anything, mock.Anything, + mock.Anything, ).Return(dummyRoute, nil) result, err := p.requestRoute(ps) @@ -408,6 +417,7 @@ func TestRequestRouteHandleCriticalErr(t *testing.T) { // Mock the paySession's `RequestRoute` method to return an error. paySession.On("RequestRoute", mock.Anything, mock.Anything, mock.Anything, mock.Anything, + mock.Anything, ).Return(nil, errDummy) result, err := p.requestRoute(ps) @@ -442,6 +452,7 @@ func TestRequestRouteHandleNoRouteErr(t *testing.T) { // type. m.paySession.On("RequestRoute", mock.Anything, mock.Anything, mock.Anything, mock.Anything, + mock.Anything, ).Return(nil, errNoTlvPayload) // The payment should be failed with reason no route. @@ -489,6 +500,7 @@ func TestRequestRouteFailPaymentError(t *testing.T) { // Mock the paySession's `RequestRoute` method to return an error. paySession.On("RequestRoute", mock.Anything, mock.Anything, mock.Anything, mock.Anything, + mock.Anything, ).Return(nil, errNoTlvPayload) result, err := p.requestRoute(ps) @@ -865,7 +877,7 @@ func TestResumePaymentFailOnRequestRouteErr(t *testing.T) { // 4. mock requestRoute to return an error. m.paySession.On("RequestRoute", paymentAmt, p.feeLimit, uint32(ps.NumAttemptsInFlight), - uint32(p.currentHeight), + uint32(p.currentHeight), mock.Anything, ).Return(nil, errDummy).Once() // Send the payment and assert it failed. @@ -911,7 +923,7 @@ func TestResumePaymentFailOnRegisterAttemptErr(t *testing.T) { // 4. mock requestRoute to return an route. m.paySession.On("RequestRoute", paymentAmt, p.feeLimit, uint32(ps.NumAttemptsInFlight), - uint32(p.currentHeight), + uint32(p.currentHeight), mock.Anything, ).Return(rt, nil).Once() // 5. mock shardTracker used in `createNewPaymentAttempt` to return an @@ -971,7 +983,7 @@ func TestResumePaymentFailOnSendAttemptErr(t *testing.T) { // 4. mock requestRoute to return an route. m.paySession.On("RequestRoute", paymentAmt, p.feeLimit, uint32(ps.NumAttemptsInFlight), - uint32(p.currentHeight), + uint32(p.currentHeight), mock.Anything, ).Return(rt, nil).Once() // 5. mock `registerAttempt` to return an attempt. @@ -1063,7 +1075,7 @@ func TestResumePaymentSuccess(t *testing.T) { // 1.4. mock requestRoute to return an route. m.paySession.On("RequestRoute", paymentAmt, p.feeLimit, uint32(ps.NumAttemptsInFlight), - uint32(p.currentHeight), + uint32(p.currentHeight), mock.Anything, ).Return(rt, nil).Once() // 1.5. mock `registerAttempt` to return an attempt. @@ -1164,7 +1176,7 @@ func TestResumePaymentSuccessWithTwoAttempts(t *testing.T) { // 1.4. mock requestRoute to return an route. m.paySession.On("RequestRoute", paymentAmt, p.feeLimit, uint32(ps.NumAttemptsInFlight), - uint32(p.currentHeight), + uint32(p.currentHeight), mock.Anything, ).Return(rt, nil).Once() // Create two attempt IDs here. @@ -1226,7 +1238,7 @@ func TestResumePaymentSuccessWithTwoAttempts(t *testing.T) { // 2.4. mock requestRoute to return an route. m.paySession.On("RequestRoute", paymentAmt/2, p.feeLimit, uint32(ps.NumAttemptsInFlight), - uint32(p.currentHeight), + uint32(p.currentHeight), mock.Anything, ).Return(rt, nil).Once() // 2.5. mock `registerAttempt` to return an attempt. diff --git a/routing/payment_session.go b/routing/payment_session.go index 00b4ab70ed..b3c131cc3d 100644 --- a/routing/payment_session.go +++ b/routing/payment_session.go @@ -139,7 +139,9 @@ type PaymentSession interface { // A noRouteError is returned if a non-critical error is encountered // during path finding. RequestRoute(maxAmt, feeLimit lnwire.MilliSatoshi, - activeShards, height uint32) (*route.Route, error) + activeShards, height uint32, + firstHopCustomRecords lnwire.CustomRecords) (*route.Route, + error) // UpdateAdditionalEdge takes an additional channel edge policy // (private channels) and applies the update from the message. Returns @@ -243,7 +245,8 @@ func newPaymentSession(p *LightningPayment, selfNode route.Vertex, // NOTE: This function is safe for concurrent access. // NOTE: Part of the PaymentSession interface. func (p *paymentSession) RequestRoute(maxAmt, feeLimit lnwire.MilliSatoshi, - activeShards, height uint32) (*route.Route, error) { + activeShards, height uint32, + firstHopCustomRecords lnwire.CustomRecords) (*route.Route, error) { if p.empty { return nil, errEmptyPaySession @@ -284,9 +287,9 @@ func (p *paymentSession) RequestRoute(maxAmt, feeLimit lnwire.MilliSatoshi, // client-side MTU that we'll attempt to respect at all times. maxShardActive := p.payment.MaxShardAmt != nil if maxShardActive && maxAmt > *p.payment.MaxShardAmt { - p.log.Debug("Clamping payment attempt from %v to %v due to "+ - "max shard size of %v", maxAmt, - *p.payment.MaxShardAmt, maxAmt) + p.log.Debugf("Clamping payment attempt from %v to %v due to "+ + "max shard size of %v", maxAmt, *p.payment.MaxShardAmt, + maxAmt) maxAmt = *p.payment.MaxShardAmt } diff --git a/routing/payment_session_source.go b/routing/payment_session_source.go index 46e7a42aa1..c89d6a8e52 100644 --- a/routing/payment_session_source.go +++ b/routing/payment_session_source.go @@ -4,8 +4,10 @@ import ( "github.com/btcsuite/btcd/btcec/v2" "github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/channeldb/models" + "github.com/lightningnetwork/lnd/fn" "github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/routing/route" + "github.com/lightningnetwork/lnd/tlv" "github.com/lightningnetwork/lnd/zpay32" ) @@ -49,12 +51,14 @@ type SessionSource struct { // view from Mission Control. An optional set of routing hints can be provided // in order to populate additional edges to explore when finding a path to the // payment's destination. -func (m *SessionSource) NewPaymentSession(p *LightningPayment) ( - PaymentSession, error) { +func (m *SessionSource) NewPaymentSession(p *LightningPayment, + firstHopBlob fn.Option[tlv.Blob], + trafficShaper fn.Option[TlvTrafficShaper]) (PaymentSession, error) { getBandwidthHints := func(graph Graph) (bandwidthHints, error) { return newBandwidthManager( graph, m.SourceNode.PubKeyBytes, m.GetLink, + firstHopBlob, trafficShaper, ) } diff --git a/routing/payment_session_test.go b/routing/payment_session_test.go index f6873aa752..34f8356820 100644 --- a/routing/payment_session_test.go +++ b/routing/payment_session_test.go @@ -235,6 +235,9 @@ func TestRequestRoute(t *testing.T) { route, err := session.RequestRoute( payment.Amount, payment.FeeLimit, 0, height, + lnwire.CustomRecords{ + lnwire.MinCustomRecordsTlvType + 123: []byte{1, 2, 3}, + }, ) if err != nil { t.Fatal(err) diff --git a/routing/router.go b/routing/router.go index de5f45fd89..e5769151bf 100644 --- a/routing/router.go +++ b/routing/router.go @@ -29,6 +29,7 @@ import ( "github.com/lightningnetwork/lnd/record" "github.com/lightningnetwork/lnd/routing/route" "github.com/lightningnetwork/lnd/routing/shards" + "github.com/lightningnetwork/lnd/tlv" "github.com/lightningnetwork/lnd/zpay32" ) @@ -154,7 +155,10 @@ type PaymentSessionSource interface { // routes to the given target. An optional set of routing hints can be // provided in order to populate additional edges to explore when // finding a path to the payment's destination. - NewPaymentSession(p *LightningPayment) (PaymentSession, error) + NewPaymentSession(p *LightningPayment, + firstHopBlob fn.Option[tlv.Blob], + trafficShaper fn.Option[TlvTrafficShaper]) (PaymentSession, + error) // NewPaymentSessionEmpty creates a new paymentSession instance that is // empty, and will be exhausted immediately. Used for failure reporting @@ -290,6 +294,10 @@ type Config struct { // // TODO(yy): remove it once the root cause of stuck payments is found. ClosedSCIDs map[lnwire.ShortChannelID]struct{} + + // TrafficShaper is an optional traffic shaper that can be used to + // control the outgoing channel of a payment. + TrafficShaper fn.Option[TlvTrafficShaper] } // EdgeLocator is a struct used to identify a specific edge. @@ -517,6 +525,7 @@ func (r *ChannelRouter) FindRoute(req *RouteRequest) (*route.Route, float64, // eliminate certain routes early on in the path finding process. bandwidthHints, err := newBandwidthManager( r.cfg.RoutingGraph, r.cfg.SelfNode, r.cfg.GetLink, + fn.None[tlv.Blob](), r.cfg.TrafficShaper, ) if err != nil { return nil, 0, err @@ -1009,10 +1018,29 @@ func spewPayment(payment *LightningPayment) lnutils.LogClosure { func (r *ChannelRouter) PreparePayment(payment *LightningPayment) ( PaymentSession, shards.ShardTracker, error) { + // Assemble any custom data we want to send to the first hop only. + var firstHopData fn.Option[tlv.Blob] + if len(payment.FirstHopCustomRecords) > 0 { + if err := payment.FirstHopCustomRecords.Validate(); err != nil { + return nil, nil, fmt.Errorf("invalid first hop custom "+ + "records: %w", err) + } + + firstHopBlob, err := payment.FirstHopCustomRecords.Serialize() + if err != nil { + return nil, nil, fmt.Errorf("unable to serialize "+ + "first hop custom records: %w", err) + } + + firstHopData = fn.Some(firstHopBlob) + } + // Before starting the HTLC routing attempt, we'll create a fresh // payment session which will report our errors back to mission // control. - paySession, err := r.cfg.SessionSource.NewPaymentSession(payment) + paySession, err := r.cfg.SessionSource.NewPaymentSession( + payment, firstHopData, r.cfg.TrafficShaper, + ) if err != nil { return nil, nil, err } @@ -1277,6 +1305,11 @@ func (r *ChannelRouter) sendPayment(ctx context.Context, return [32]byte{}, nil, err } + // Validate the custom records before we attempt to send the payment. + if err := firstHopCustomRecords.Validate(); err != nil { + return [32]byte{}, nil, err + } + // Now set up a paymentLifecycle struct with these params, such that we // can resume the payment from the current state. p := newPaymentLifecycle( @@ -1327,7 +1360,7 @@ func (e ErrNoChannel) Error() string { // outgoing channel, use the outgoingChan parameter. func (r *ChannelRouter) BuildRoute(amt fn.Option[lnwire.MilliSatoshi], hops []route.Vertex, outgoingChan *uint64, finalCltvDelta int32, - payAddr *[32]byte, _ fn.Option[[]byte]) (*route.Route, + payAddr *[32]byte, firstHopBlob fn.Option[[]byte]) (*route.Route, error) { log.Tracef("BuildRoute called: hopsCount=%v, amt=%v", len(hops), amt) @@ -1342,7 +1375,8 @@ func (r *ChannelRouter) BuildRoute(amt fn.Option[lnwire.MilliSatoshi], // We'll attempt to obtain a set of bandwidth hints that helps us select // the best outgoing channel to use in case no outgoing channel is set. bandwidthHints, err := newBandwidthManager( - r.cfg.RoutingGraph, r.cfg.SelfNode, r.cfg.GetLink, + r.cfg.RoutingGraph, r.cfg.SelfNode, r.cfg.GetLink, firstHopBlob, + r.cfg.TrafficShaper, ) if err != nil { return nil, err diff --git a/routing/router_test.go b/routing/router_test.go index 676fde4cc9..7726091fcd 100644 --- a/routing/router_test.go +++ b/routing/router_test.go @@ -164,6 +164,9 @@ func createTestCtxFromGraphInstanceAssumeValid(t *testing.T, Clock: clock.NewTestClock(time.Unix(1, 0)), ApplyChannelUpdate: graphBuilder.ApplyChannelUpdate, ClosedSCIDs: mockClosedSCIDs, + TrafficShaper: fn.Some[TlvTrafficShaper]( + &mockTrafficShaper{}, + ), }) require.NoError(t, router.Start(), "unable to start router") @@ -2189,7 +2192,8 @@ func TestSendToRouteSkipTempErrSuccess(t *testing.T) { NextPaymentID: func() (uint64, error) { return 0, nil }, - ClosedSCIDs: mockClosedSCIDs, + ClosedSCIDs: mockClosedSCIDs, + TrafficShaper: fn.Some[TlvTrafficShaper](&mockTrafficShaper{}), }} // Register mockers with the expected method calls. @@ -2273,7 +2277,8 @@ func TestSendToRouteSkipTempErrNonMPP(t *testing.T) { NextPaymentID: func() (uint64, error) { return 0, nil }, - ClosedSCIDs: mockClosedSCIDs, + ClosedSCIDs: mockClosedSCIDs, + TrafficShaper: fn.Some[TlvTrafficShaper](&mockTrafficShaper{}), }} // Expect an error to be returned. @@ -2328,7 +2333,8 @@ func TestSendToRouteSkipTempErrTempFailure(t *testing.T) { NextPaymentID: func() (uint64, error) { return 0, nil }, - ClosedSCIDs: mockClosedSCIDs, + ClosedSCIDs: mockClosedSCIDs, + TrafficShaper: fn.Some[TlvTrafficShaper](&mockTrafficShaper{}), }} // Create the error to be returned. @@ -2411,7 +2417,8 @@ func TestSendToRouteSkipTempErrPermanentFailure(t *testing.T) { NextPaymentID: func() (uint64, error) { return 0, nil }, - ClosedSCIDs: mockClosedSCIDs, + ClosedSCIDs: mockClosedSCIDs, + TrafficShaper: fn.Some[TlvTrafficShaper](&mockTrafficShaper{}), }} // Create the error to be returned. @@ -2498,7 +2505,8 @@ func TestSendToRouteTempFailure(t *testing.T) { NextPaymentID: func() (uint64, error) { return 0, nil }, - ClosedSCIDs: mockClosedSCIDs, + ClosedSCIDs: mockClosedSCIDs, + TrafficShaper: fn.Some[TlvTrafficShaper](&mockTrafficShaper{}), }} // Create the error to be returned. From ec2b2c6e1e1739d46b6022a09d0697e3d2954f07 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Thu, 11 Jul 2024 19:48:47 -0700 Subject: [PATCH 073/218] routing: skip amtInRange for custom HTLCs We might be trying to send an invoice amount that's greater than the size of the channel, but once you factor in the custom channel logic, an actual HTLC can be sent over the channel to pay that larger payment. As a result, we'll skip over this check if a have a custom HTLC. --- routing/unified_edges.go | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/routing/unified_edges.go b/routing/unified_edges.go index a0300eea4b..6c44372e99 100644 --- a/routing/unified_edges.go +++ b/routing/unified_edges.go @@ -247,8 +247,13 @@ func (u *edgeUnifier) getEdgeLocal(netAmtReceived lnwire.MilliSatoshi, // local channel. amt := netAmtReceived + lnwire.MilliSatoshi(inboundFee) - // Check valid amount range for the channel. - if !edge.amtInRange(amt) { + // Check valid amount range for the channel. We skip this test + // for payments with custom HTLC data, as the amount sent on + // the BTC layer may differ from the amount that is actually + // forwarded in custom channels. + if bandwidthHints.firstHopCustomBlob().IsNone() && + !edge.amtInRange(amt) { + log.Debugf("Amount %v not in range for edge %v", netAmtReceived, edge.policy.ChannelID) From 6ac750955fe7e0c3037fb188d3c0efed4be97d89 Mon Sep 17 00:00:00 2001 From: George Tsagkarelis Date: Fri, 19 Apr 2024 14:00:34 +0200 Subject: [PATCH 074/218] lnd: use impl cfg TlvTrafficShaper --- config_builder.go | 5 +++++ server.go | 1 + 2 files changed, 6 insertions(+) diff --git a/config_builder.go b/config_builder.go index 7c399297e7..21b1ccee0b 100644 --- a/config_builder.go +++ b/config_builder.go @@ -44,6 +44,7 @@ import ( "github.com/lightningnetwork/lnd/lnwallet/rpcwallet" "github.com/lightningnetwork/lnd/macaroons" "github.com/lightningnetwork/lnd/msgmux" + "github.com/lightningnetwork/lnd/routing" "github.com/lightningnetwork/lnd/rpcperms" "github.com/lightningnetwork/lnd/signal" "github.com/lightningnetwork/lnd/sqldb" @@ -159,6 +160,10 @@ type AuxComponents struct { // channels to fetch+store various data. AuxLeafStore fn.Option[lnwallet.AuxLeafStore] + // TrafficShaper is an optional traffic shaper that can be used to + // control the outgoing channel of a payment. + TrafficShaper fn.Option[routing.TlvTrafficShaper] + // MsgRouter is an optional message router that if set will be used in // place of a new blank default message router. MsgRouter fn.Option[msgmux.Router] diff --git a/server.go b/server.go index 257bde4bfc..d7d0d1a6e0 100644 --- a/server.go +++ b/server.go @@ -1021,6 +1021,7 @@ func newServer(cfg *Config, listenAddrs []net.Addr, Clock: clock.NewDefaultClock(), ApplyChannelUpdate: s.graphBuilder.ApplyChannelUpdate, ClosedSCIDs: s.fetchClosedChannelSCIDs(), + TrafficShaper: implCfg.TrafficShaper, }) if err != nil { return nil, fmt.Errorf("can't create router: %w", err) From 5489b06628278ce3cb26069ff4508e1bf2d9c55f Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Fri, 30 Aug 2024 14:44:09 +0200 Subject: [PATCH 075/218] channeldb+routing: persist first hop custom data for route --- channeldb/payments.go | 39 ++++++++++++++++++++++++++++++++++++++ channeldb/payments_test.go | 25 +++++++++++++++++++----- routing/route/route.go | 20 +++++++++++++++++++ 3 files changed, 79 insertions(+), 5 deletions(-) diff --git a/channeldb/payments.go b/channeldb/payments.go index a0db8f22ad..0ccb75774c 100644 --- a/channeldb/payments.go +++ b/channeldb/payments.go @@ -1096,6 +1096,25 @@ func serializeHTLCAttemptInfo(w io.Writer, a *HTLCAttemptInfo) error { return err } + // Merge the fixed/known records together with the custom records to + // serialize them as a single blob. We can't do this in SerializeRoute + // because we're in the middle of the byte stream there. We can only do + // TLV serialization at the end of the stream, since EOF is allowed for + // a stream if no more data is expected. + producers := []tlv.RecordProducer{ + &a.Route.FirstHopAmount, + } + tlvData, err := lnwire.MergeAndEncode( + producers, nil, a.Route.FirstHopWireCustomRecords, + ) + if err != nil { + return err + } + + if _, err := w.Write(tlvData); err != nil { + return err + } + return nil } @@ -1133,6 +1152,22 @@ func deserializeHTLCAttemptInfo(r io.Reader) (*HTLCAttemptInfo, error) { a.Hash = &hash + // Read any remaining data (if any) and parse it into the known records + // and custom records. + extraData, err := io.ReadAll(r) + if err != nil { + return nil, err + } + + customRecords, _, _, err := lnwire.ParseAndExtractCustomRecords( + extraData, &a.Route.FirstHopAmount, + ) + if err != nil { + return nil, err + } + + a.Route.FirstHopWireCustomRecords = customRecords + return a, nil } @@ -1398,6 +1433,8 @@ func SerializeRoute(w io.Writer, r route.Route) error { } } + // Any new/extra TLV data is encoded in serializeHTLCAttemptInfo! + return nil } @@ -1431,5 +1468,7 @@ func DeserializeRoute(r io.Reader) (route.Route, error) { } rt.Hops = hops + // Any new/extra TLV data is decoded in deserializeHTLCAttemptInfo! + return rt, nil } diff --git a/channeldb/payments_test.go b/channeldb/payments_test.go index 844b818e84..0c3753e662 100644 --- a/channeldb/payments_test.go +++ b/channeldb/payments_test.go @@ -16,6 +16,7 @@ import ( "github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/record" "github.com/lightningnetwork/lnd/routing/route" + "github.com/lightningnetwork/lnd/tlv" "github.com/stretchr/testify/require" ) @@ -149,20 +150,34 @@ func TestSentPaymentSerialization(t *testing.T) { require.NoError(t, err, "deserialize") require.Equal(t, c, newCreationInfo) + b.Reset() require.NoError(t, serializeHTLCAttemptInfo(&b, s), "serialize") newWireInfo, err := deserializeHTLCAttemptInfo(&b) require.NoError(t, err, "deserialize") - newWireInfo.AttemptID = s.AttemptID - // First we verify all the records match up properly, as they aren't - // able to be properly compared using reflect.DeepEqual. - err = assertRouteEqual(&s.Route, &newWireInfo.Route) - require.NoError(t, err) + // First we verify all the records match up properly. + require.Equal(t, s.Route, newWireInfo.Route) + + // We now add the new fields and custom records to the route and + // serialize it again. + b.Reset() + s.Route.FirstHopAmount = tlv.NewRecordT[tlv.TlvType0]( + tlv.NewBigSizeT(lnwire.MilliSatoshi(1234)), + ) + s.Route.FirstHopWireCustomRecords = lnwire.CustomRecords{ + lnwire.MinCustomRecordsTlvType + 3: []byte{4, 5, 6}, + } + require.NoError(t, serializeHTLCAttemptInfo(&b, s), "serialize") + + newWireInfo, err = deserializeHTLCAttemptInfo(&b) + require.NoError(t, err, "deserialize") + require.Equal(t, s.Route, newWireInfo.Route) // Clear routes to allow DeepEqual to compare the remaining fields. newWireInfo.Route = route.Route{} s.Route = route.Route{} + newWireInfo.AttemptID = s.AttemptID // Call session key method to set our cached session key so we can use // DeepEqual, and assert that our key equals the original key. diff --git a/routing/route/route.go b/routing/route/route.go index 0516cc7a22..9aa28759bc 100644 --- a/routing/route/route.go +++ b/routing/route/route.go @@ -488,6 +488,26 @@ type Route struct { // Hops contains details concerning the specific forwarding details at // each hop. Hops []*Hop + + // FirstHopAmount is the amount that should actually be sent to the + // first hop in the route. This is only different from TotalAmount above + // for custom channels where the on-chain amount doesn't necessarily + // reflect all the value of an outgoing payment. + FirstHopAmount tlv.RecordT[ + tlv.TlvType0, tlv.BigSizeT[lnwire.MilliSatoshi], + ] + + // FirstHopWireCustomRecords is a set of custom records that should be + // included in the wire message sent to the first hop. This is only set + // on custom channels and is used to include additional information + // about the actual value of the payment. + // + // NOTE: Since these records already represent TLV records, and we + // enforce them to be in the custom range (e.g. >= 65536), we don't use + // another parent record type here. Instead, when serializing the Route + // we merge the TLV records together with the custom records and encode + // everything as a single TLV stream. + FirstHopWireCustomRecords lnwire.CustomRecords } // Copy returns a deep copy of the Route. From 444ef199a65770beb2f6ac842d9655ec5c34365b Mon Sep 17 00:00:00 2001 From: George Tsagkarelis Date: Thu, 2 May 2024 18:48:28 +0200 Subject: [PATCH 076/218] routing: use first hop records on path finding --- routing/pathfind.go | 27 ++++++++++- routing/payment_lifecycle.go | 88 ++++++++++++++++++++++++++++++++++-- routing/payment_session.go | 21 +++++---- routing/router.go | 7 +++ 4 files changed, 128 insertions(+), 15 deletions(-) diff --git a/routing/pathfind.go b/routing/pathfind.go index 01325c2238..6f394ea581 100644 --- a/routing/pathfind.go +++ b/routing/pathfind.go @@ -466,6 +466,10 @@ type RestrictParams struct { // BlindedPaymentPathSet is necessary to determine the hop size of the // last/exit hop. BlindedPaymentPathSet *BlindedPaymentPathSet + + // FirstHopCustomRecords includes any records that should be included in + // the update_add_htlc message towards our peer. + FirstHopCustomRecords lnwire.CustomRecords } // PathFindingConfig defines global parameters that control the trade-off in @@ -524,7 +528,16 @@ func getOutgoingBalance(node route.Vertex, outgoingChans map[uint64]struct{}, max = bandwidth } - total += bandwidth + var overflow bool + total, overflow = overflowSafeAdd(total, bandwidth) + if overflow { + // If the current total and the bandwidth would + // overflow the maximum value, we set the total to the + // maximum value. Which is more milli-satoshis than are + // in existence anyway, so the actual value is + // irrelevant. + total = lnwire.MilliSatoshi(math.MaxUint64) + } return nil } @@ -1446,3 +1459,15 @@ func lastHopPayloadSize(r *RestrictParams, finalHtlcExpiry int32, // The final hop does not have a short chanID set. return finalHop.PayloadSize(0) } + +// overflowSafeAdd adds two MilliSatoshi values and returns the result. If an +// overflow could occur, zero is returned instead and the boolean is set to +// true. +func overflowSafeAdd(x, y lnwire.MilliSatoshi) (lnwire.MilliSatoshi, bool) { + if y > math.MaxUint64-x { + // Overflow would occur, return 0 and set overflow flag. + return 0, true + } + + return x + y, false +} diff --git a/routing/payment_lifecycle.go b/routing/payment_lifecycle.go index 96ff796080..43e646e192 100644 --- a/routing/payment_lifecycle.go +++ b/routing/payment_lifecycle.go @@ -11,11 +11,13 @@ import ( sphinx "github.com/lightningnetwork/lightning-onion" "github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/channeldb/models" + "github.com/lightningnetwork/lnd/fn" "github.com/lightningnetwork/lnd/htlcswitch" "github.com/lightningnetwork/lnd/lntypes" "github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/routing/route" "github.com/lightningnetwork/lnd/routing/shards" + "github.com/lightningnetwork/lnd/tlv" ) // ErrPaymentLifecycleExiting is used when waiting for htlc attempt result, but @@ -275,6 +277,13 @@ lifecycle: log.Tracef("Found route: %s", spew.Sdump(rt.Hops)) + // Allow the traffic shaper to add custom records to the + // outgoing HTLC and also adjust the amount if needed. + err = p.amendFirstHopData(rt) + if err != nil { + return exitWithErr(err) + } + // We found a route to try, create a new HTLC attempt to try. attempt, err := p.registerAttempt(rt, ps.RemainingAmt) if err != nil { @@ -668,8 +677,10 @@ func (p *paymentLifecycle) createNewPaymentAttempt(rt *route.Route, func (p *paymentLifecycle) sendAttempt( attempt *channeldb.HTLCAttempt) (*attemptResult, error) { - log.Debugf("Sending HTLC attempt(id=%v, amt=%v) for payment %v", - attempt.AttemptID, attempt.Route.TotalAmount, p.identifier) + log.Debugf("Sending HTLC attempt(id=%v, total_amt=%v, first_hop_amt=%d"+ + ") for payment %v", attempt.AttemptID, + attempt.Route.TotalAmount, attempt.Route.FirstHopAmount.Val, + p.identifier) rt := attempt.Route @@ -680,10 +691,10 @@ func (p *paymentLifecycle) sendAttempt( // this packet will be used to route the payment through the network, // starting with the first-hop. htlcAdd := &lnwire.UpdateAddHTLC{ - Amount: rt.TotalAmount, + Amount: rt.FirstHopAmount.Val.Int(), Expiry: rt.TotalTimeLock, PaymentHash: *attempt.Hash, - CustomRecords: p.firstHopCustomRecords, + CustomRecords: rt.FirstHopWireCustomRecords, } // Generate the raw encoded sphinx packet to be included along @@ -722,6 +733,75 @@ func (p *paymentLifecycle) sendAttempt( }, nil } +// amendFirstHopData is a function that calls the traffic shaper to allow it to +// add custom records to the outgoing HTLC and also adjust the amount if +// needed. +func (p *paymentLifecycle) amendFirstHopData(rt *route.Route) error { + // The first hop amount on the route is the full route amount if not + // overwritten by the traffic shaper. So we set the initial value now + // and potentially overwrite it later. + rt.FirstHopAmount = tlv.NewRecordT[tlv.TlvType0]( + tlv.NewBigSizeT(rt.TotalAmount), + ) + + // By default, we set the first hop custom records to the initial + // value requested by the RPC. The traffic shaper may overwrite this + // value. + rt.FirstHopWireCustomRecords = p.firstHopCustomRecords + + // extraDataRequest is a helper struct to pass the custom records and + // amount back from the traffic shaper. + type extraDataRequest struct { + customRecords fn.Option[lnwire.CustomRecords] + + amount fn.Option[lnwire.MilliSatoshi] + } + + // If a hook exists that may affect our outgoing message, we call it now + // and apply its side effects to the UpdateAddHTLC message. + result, err := fn.MapOptionZ( + p.router.cfg.TrafficShaper, + func(ts TlvTrafficShaper) fn.Result[extraDataRequest] { + newAmt, newRecords, err := ts.ProduceHtlcExtraData( + rt.TotalAmount, p.firstHopCustomRecords, + ) + if err != nil { + return fn.Err[extraDataRequest](err) + } + + // Make sure we only received valid records. + if err := newRecords.Validate(); err != nil { + return fn.Err[extraDataRequest](err) + } + + log.Debugf("TLV traffic shaper returned custom "+ + "records %v and amount %d msat for HTLC", + spew.Sdump(newRecords), newAmt) + + return fn.Ok(extraDataRequest{ + customRecords: fn.Some(newRecords), + amount: fn.Some(newAmt), + }) + }, + ).Unpack() + if err != nil { + return fmt.Errorf("traffic shaper failed to produce extra "+ + "data: %w", err) + } + + // Apply the side effects to the UpdateAddHTLC message. + result.customRecords.WhenSome(func(records lnwire.CustomRecords) { + rt.FirstHopWireCustomRecords = records + }) + result.amount.WhenSome(func(amount lnwire.MilliSatoshi) { + rt.FirstHopAmount = tlv.NewRecordT[tlv.TlvType0]( + tlv.NewBigSizeT(amount), + ) + }) + + return nil +} + // failAttemptAndPayment fails both the payment and its attempt via the // router's control tower, which marks the payment as failed in db. func (p *paymentLifecycle) failPaymentAndAttempt( diff --git a/routing/payment_session.go b/routing/payment_session.go index b3c131cc3d..5e6a468fe1 100644 --- a/routing/payment_session.go +++ b/routing/payment_session.go @@ -268,16 +268,17 @@ func (p *paymentSession) RequestRoute(maxAmt, feeLimit lnwire.MilliSatoshi, // to our destination, respecting the recommendations from // MissionControl. restrictions := &RestrictParams{ - ProbabilitySource: p.missionControl.GetProbability, - FeeLimit: feeLimit, - OutgoingChannelIDs: p.payment.OutgoingChannelIDs, - LastHop: p.payment.LastHop, - CltvLimit: cltvLimit, - DestCustomRecords: p.payment.DestCustomRecords, - DestFeatures: p.payment.DestFeatures, - PaymentAddr: p.payment.PaymentAddr, - Amp: p.payment.amp, - Metadata: p.payment.Metadata, + ProbabilitySource: p.missionControl.GetProbability, + FeeLimit: feeLimit, + OutgoingChannelIDs: p.payment.OutgoingChannelIDs, + LastHop: p.payment.LastHop, + CltvLimit: cltvLimit, + DestCustomRecords: p.payment.DestCustomRecords, + DestFeatures: p.payment.DestFeatures, + PaymentAddr: p.payment.PaymentAddr, + Amp: p.payment.amp, + Metadata: p.payment.Metadata, + FirstHopCustomRecords: firstHopCustomRecords, } finalHtlcExpiry := int32(height) + int32(finalCltvDelta) diff --git a/routing/router.go b/routing/router.go index e5769151bf..db3fbc8573 100644 --- a/routing/router.go +++ b/routing/router.go @@ -1188,6 +1188,13 @@ func (r *ChannelRouter) sendToRoute(htlcHash lntypes.Hash, rt *route.Route, firstHopCustomRecords, ) + // Allow the traffic shaper to add custom records to the outgoing HTLC + // and also adjust the amount if needed. + err = p.amendFirstHopData(rt) + if err != nil { + return nil, err + } + // We found a route to try, create a new HTLC attempt to try. // // NOTE: we use zero `remainingAmt` here to simulate the same effect of From 1822b2f79a8cbb5a3ef0bb5edd3e0354857876eb Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Fri, 30 Aug 2024 15:10:13 +0200 Subject: [PATCH 077/218] lnrpc: add first hop custom data to route --- lnrpc/lightning.pb.go | 3134 ++++++++++++++------------- lnrpc/lightning.proto | 14 + lnrpc/lightning.swagger.json | 10 + lnrpc/routerrpc/router.swagger.json | 10 + lnrpc/routerrpc/router_backend.go | 27 +- 5 files changed, 1636 insertions(+), 1559 deletions(-) diff --git a/lnrpc/lightning.pb.go b/lnrpc/lightning.pb.go index 624a13be43..8ff18e4b11 100644 --- a/lnrpc/lightning.pb.go +++ b/lnrpc/lightning.pb.go @@ -10388,6 +10388,14 @@ type Route struct { TotalFeesMsat int64 `protobuf:"varint,5,opt,name=total_fees_msat,json=totalFeesMsat,proto3" json:"total_fees_msat,omitempty"` // The total amount in millisatoshis. TotalAmtMsat int64 `protobuf:"varint,6,opt,name=total_amt_msat,json=totalAmtMsat,proto3" json:"total_amt_msat,omitempty"` + // The actual on-chain amount that was sent out to the first hop. This value is + // only different from the total_amt_msat field if this is a custom channel + // payment and the value transported in the HTLC is different from the BTC + // amount in the HTLC. If this value is zero, then this is an old payment that + // didn't have this value yet and can be ignored. + FirstHopAmountMsat int64 `protobuf:"varint,7,opt,name=first_hop_amount_msat,json=firstHopAmountMsat,proto3" json:"first_hop_amount_msat,omitempty"` + // Custom channel data that might be populated in custom channels. + CustomChannelData []byte `protobuf:"bytes,8,opt,name=custom_channel_data,json=customChannelData,proto3" json:"custom_channel_data,omitempty"` } func (x *Route) Reset() { @@ -10466,6 +10474,20 @@ func (x *Route) GetTotalAmtMsat() int64 { return 0 } +func (x *Route) GetFirstHopAmountMsat() int64 { + if x != nil { + return x.FirstHopAmountMsat + } + return 0 +} + +func (x *Route) GetCustomChannelData() []byte { + if x != nil { + return x.CustomChannelData + } + return nil +} + type NodeInfoRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -19708,7 +19730,7 @@ var file_lightning_proto_rawDesc = []byte{ 0x73, 0x65, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x73, 0x65, 0x74, 0x49, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x63, 0x68, 0x69, 0x6c, 0x64, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x63, 0x68, 0x69, 0x6c, 0x64, 0x49, - 0x6e, 0x64, 0x65, 0x78, 0x22, 0xe1, 0x01, 0x0a, 0x05, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x12, 0x26, + 0x6e, 0x64, 0x65, 0x78, 0x22, 0xc4, 0x02, 0x0a, 0x05, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x12, 0x26, 0x0a, 0x0f, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x6c, 0x6f, 0x63, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0d, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x54, 0x69, 0x6d, 0x65, 0x4c, 0x6f, 0x63, 0x6b, 0x12, 0x21, 0x0a, 0x0a, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, @@ -19722,1572 +19744,1578 @@ var file_lightning_proto_rawDesc = []byte{ 0x01, 0x28, 0x03, 0x52, 0x0d, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x46, 0x65, 0x65, 0x73, 0x4d, 0x73, 0x61, 0x74, 0x12, 0x24, 0x0a, 0x0e, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x61, 0x6d, 0x74, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0c, 0x74, 0x6f, 0x74, 0x61, - 0x6c, 0x41, 0x6d, 0x74, 0x4d, 0x73, 0x61, 0x74, 0x22, 0x55, 0x0a, 0x0f, 0x4e, 0x6f, 0x64, 0x65, - 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x17, 0x0a, 0x07, 0x70, - 0x75, 0x62, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x70, 0x75, - 0x62, 0x4b, 0x65, 0x79, 0x12, 0x29, 0x0a, 0x10, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x5f, - 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, - 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x22, - 0xae, 0x01, 0x0a, 0x08, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x28, 0x0a, 0x04, - 0x6e, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x6c, 0x6e, 0x72, - 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x67, 0x68, 0x74, 0x6e, 0x69, 0x6e, 0x67, 0x4e, 0x6f, 0x64, 0x65, - 0x52, 0x04, 0x6e, 0x6f, 0x64, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x6e, 0x75, 0x6d, 0x5f, 0x63, 0x68, - 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x6e, 0x75, - 0x6d, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x12, 0x25, 0x0a, 0x0e, 0x74, 0x6f, 0x74, - 0x61, 0x6c, 0x5f, 0x63, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x03, 0x52, 0x0d, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x43, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, - 0x12, 0x2e, 0x0a, 0x08, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x18, 0x04, 0x20, 0x03, - 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, - 0x65, 0x6c, 0x45, 0x64, 0x67, 0x65, 0x52, 0x08, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, - 0x22, 0xc6, 0x03, 0x0a, 0x0d, 0x4c, 0x69, 0x67, 0x68, 0x74, 0x6e, 0x69, 0x6e, 0x67, 0x4e, 0x6f, - 0x64, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, - 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x6c, 0x61, 0x73, 0x74, 0x55, 0x70, 0x64, - 0x61, 0x74, 0x65, 0x12, 0x17, 0x0a, 0x07, 0x70, 0x75, 0x62, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x70, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, - 0x61, 0x6c, 0x69, 0x61, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x61, 0x6c, 0x69, - 0x61, 0x73, 0x12, 0x30, 0x0a, 0x09, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x18, - 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4e, 0x6f, - 0x64, 0x65, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x09, 0x61, 0x64, 0x64, 0x72, 0x65, - 0x73, 0x73, 0x65, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x18, 0x05, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x05, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x12, 0x3e, 0x0a, 0x08, 0x66, 0x65, - 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x6c, - 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x67, 0x68, 0x74, 0x6e, 0x69, 0x6e, 0x67, 0x4e, 0x6f, - 0x64, 0x65, 0x2e, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, - 0x52, 0x08, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x12, 0x4e, 0x0a, 0x0e, 0x63, 0x75, - 0x73, 0x74, 0x6f, 0x6d, 0x5f, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x18, 0x07, 0x20, 0x03, - 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x67, 0x68, 0x74, - 0x6e, 0x69, 0x6e, 0x67, 0x4e, 0x6f, 0x64, 0x65, 0x2e, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x52, - 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0d, 0x63, 0x75, 0x73, - 0x74, 0x6f, 0x6d, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x1a, 0x4b, 0x0a, 0x0d, 0x46, 0x65, - 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, - 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x24, 0x0a, - 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x6c, - 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x52, 0x05, 0x76, 0x61, - 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x40, 0x0a, 0x12, 0x43, 0x75, 0x73, 0x74, 0x6f, - 0x6d, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, - 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, - 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, - 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x3b, 0x0a, 0x0b, 0x4e, 0x6f, 0x64, - 0x65, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x6e, 0x65, 0x74, 0x77, - 0x6f, 0x72, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6e, 0x65, 0x74, 0x77, 0x6f, - 0x72, 0x6b, 0x12, 0x12, 0x0a, 0x04, 0x61, 0x64, 0x64, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x04, 0x61, 0x64, 0x64, 0x72, 0x22, 0x89, 0x04, 0x0a, 0x0d, 0x52, 0x6f, 0x75, 0x74, 0x69, - 0x6e, 0x67, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x26, 0x0a, 0x0f, 0x74, 0x69, 0x6d, 0x65, - 0x5f, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x0d, 0x52, 0x0d, 0x74, 0x69, 0x6d, 0x65, 0x4c, 0x6f, 0x63, 0x6b, 0x44, 0x65, 0x6c, 0x74, 0x61, - 0x12, 0x19, 0x0a, 0x08, 0x6d, 0x69, 0x6e, 0x5f, 0x68, 0x74, 0x6c, 0x63, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x03, 0x52, 0x07, 0x6d, 0x69, 0x6e, 0x48, 0x74, 0x6c, 0x63, 0x12, 0x22, 0x0a, 0x0d, 0x66, - 0x65, 0x65, 0x5f, 0x62, 0x61, 0x73, 0x65, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x03, 0x52, 0x0b, 0x66, 0x65, 0x65, 0x42, 0x61, 0x73, 0x65, 0x4d, 0x73, 0x61, 0x74, 0x12, - 0x2d, 0x0a, 0x13, 0x66, 0x65, 0x65, 0x5f, 0x72, 0x61, 0x74, 0x65, 0x5f, 0x6d, 0x69, 0x6c, 0x6c, - 0x69, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x10, 0x66, 0x65, - 0x65, 0x52, 0x61, 0x74, 0x65, 0x4d, 0x69, 0x6c, 0x6c, 0x69, 0x4d, 0x73, 0x61, 0x74, 0x12, 0x1a, - 0x0a, 0x08, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, - 0x52, 0x08, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x22, 0x0a, 0x0d, 0x6d, 0x61, - 0x78, 0x5f, 0x68, 0x74, 0x6c, 0x63, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, - 0x04, 0x52, 0x0b, 0x6d, 0x61, 0x78, 0x48, 0x74, 0x6c, 0x63, 0x4d, 0x73, 0x61, 0x74, 0x12, 0x1f, - 0x0a, 0x0b, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x18, 0x07, 0x20, - 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x6c, 0x61, 0x73, 0x74, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, - 0x4e, 0x0a, 0x0e, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5f, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, - 0x73, 0x18, 0x08, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, - 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x43, 0x75, + 0x6c, 0x41, 0x6d, 0x74, 0x4d, 0x73, 0x61, 0x74, 0x12, 0x31, 0x0a, 0x15, 0x66, 0x69, 0x72, 0x73, + 0x74, 0x5f, 0x68, 0x6f, 0x70, 0x5f, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x6d, 0x73, 0x61, + 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x03, 0x52, 0x12, 0x66, 0x69, 0x72, 0x73, 0x74, 0x48, 0x6f, + 0x70, 0x41, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x4d, 0x73, 0x61, 0x74, 0x12, 0x2e, 0x0a, 0x13, 0x63, + 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x5f, 0x64, 0x61, + 0x74, 0x61, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x11, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, + 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x44, 0x61, 0x74, 0x61, 0x22, 0x55, 0x0a, 0x0f, 0x4e, + 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x17, + 0x0a, 0x07, 0x70, 0x75, 0x62, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x06, 0x70, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x12, 0x29, 0x0a, 0x10, 0x69, 0x6e, 0x63, 0x6c, 0x75, + 0x64, 0x65, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x08, 0x52, 0x0f, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, + 0x6c, 0x73, 0x22, 0xae, 0x01, 0x0a, 0x08, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, + 0x28, 0x0a, 0x04, 0x6e, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, + 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x67, 0x68, 0x74, 0x6e, 0x69, 0x6e, 0x67, 0x4e, + 0x6f, 0x64, 0x65, 0x52, 0x04, 0x6e, 0x6f, 0x64, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x6e, 0x75, 0x6d, + 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, + 0x0b, 0x6e, 0x75, 0x6d, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x12, 0x25, 0x0a, 0x0e, + 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x63, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x03, 0x52, 0x0d, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x43, 0x61, 0x70, 0x61, 0x63, + 0x69, 0x74, 0x79, 0x12, 0x2e, 0x0a, 0x08, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x18, + 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, + 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x45, 0x64, 0x67, 0x65, 0x52, 0x08, 0x63, 0x68, 0x61, 0x6e, 0x6e, + 0x65, 0x6c, 0x73, 0x22, 0xc6, 0x03, 0x0a, 0x0d, 0x4c, 0x69, 0x67, 0x68, 0x74, 0x6e, 0x69, 0x6e, + 0x67, 0x4e, 0x6f, 0x64, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x75, 0x70, + 0x64, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x6c, 0x61, 0x73, 0x74, + 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x17, 0x0a, 0x07, 0x70, 0x75, 0x62, 0x5f, 0x6b, 0x65, + 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x70, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x12, + 0x14, 0x0a, 0x05, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, + 0x61, 0x6c, 0x69, 0x61, 0x73, 0x12, 0x30, 0x0a, 0x09, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, + 0x65, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, + 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x09, 0x61, 0x64, + 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x6f, 0x6c, 0x6f, 0x72, + 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x12, 0x3e, 0x0a, + 0x08, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x22, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x67, 0x68, 0x74, 0x6e, 0x69, 0x6e, + 0x67, 0x4e, 0x6f, 0x64, 0x65, 0x2e, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x45, 0x6e, + 0x74, 0x72, 0x79, 0x52, 0x08, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x12, 0x4e, 0x0a, + 0x0e, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5f, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x18, + 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, + 0x67, 0x68, 0x74, 0x6e, 0x69, 0x6e, 0x67, 0x4e, 0x6f, 0x64, 0x65, 0x2e, 0x43, 0x75, 0x73, 0x74, + 0x6f, 0x6d, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0d, + 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x1a, 0x4b, 0x0a, + 0x0d, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, + 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x6b, 0x65, 0x79, + 0x12, 0x24, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x0e, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x52, + 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x40, 0x0a, 0x12, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, - 0x52, 0x0d, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x12, - 0x31, 0x0a, 0x15, 0x69, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x5f, 0x66, 0x65, 0x65, 0x5f, 0x62, - 0x61, 0x73, 0x65, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x09, 0x20, 0x01, 0x28, 0x05, 0x52, 0x12, - 0x69, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x46, 0x65, 0x65, 0x42, 0x61, 0x73, 0x65, 0x4d, 0x73, - 0x61, 0x74, 0x12, 0x3c, 0x0a, 0x1b, 0x69, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x5f, 0x66, 0x65, - 0x65, 0x5f, 0x72, 0x61, 0x74, 0x65, 0x5f, 0x6d, 0x69, 0x6c, 0x6c, 0x69, 0x5f, 0x6d, 0x73, 0x61, - 0x74, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x05, 0x52, 0x17, 0x69, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, - 0x46, 0x65, 0x65, 0x52, 0x61, 0x74, 0x65, 0x4d, 0x69, 0x6c, 0x6c, 0x69, 0x4d, 0x73, 0x61, 0x74, + 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x03, 0x6b, + 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0c, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x3b, 0x0a, 0x0b, + 0x4e, 0x6f, 0x64, 0x65, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x6e, + 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6e, 0x65, + 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x12, 0x12, 0x0a, 0x04, 0x61, 0x64, 0x64, 0x72, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x04, 0x61, 0x64, 0x64, 0x72, 0x22, 0x89, 0x04, 0x0a, 0x0d, 0x52, 0x6f, + 0x75, 0x74, 0x69, 0x6e, 0x67, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x26, 0x0a, 0x0f, 0x74, + 0x69, 0x6d, 0x65, 0x5f, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0d, 0x74, 0x69, 0x6d, 0x65, 0x4c, 0x6f, 0x63, 0x6b, 0x44, 0x65, + 0x6c, 0x74, 0x61, 0x12, 0x19, 0x0a, 0x08, 0x6d, 0x69, 0x6e, 0x5f, 0x68, 0x74, 0x6c, 0x63, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x6d, 0x69, 0x6e, 0x48, 0x74, 0x6c, 0x63, 0x12, 0x22, + 0x0a, 0x0d, 0x66, 0x65, 0x65, 0x5f, 0x62, 0x61, 0x73, 0x65, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b, 0x66, 0x65, 0x65, 0x42, 0x61, 0x73, 0x65, 0x4d, 0x73, + 0x61, 0x74, 0x12, 0x2d, 0x0a, 0x13, 0x66, 0x65, 0x65, 0x5f, 0x72, 0x61, 0x74, 0x65, 0x5f, 0x6d, + 0x69, 0x6c, 0x6c, 0x69, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, + 0x10, 0x66, 0x65, 0x65, 0x52, 0x61, 0x74, 0x65, 0x4d, 0x69, 0x6c, 0x6c, 0x69, 0x4d, 0x73, 0x61, + 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x05, 0x20, + 0x01, 0x28, 0x08, 0x52, 0x08, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x22, 0x0a, + 0x0d, 0x6d, 0x61, 0x78, 0x5f, 0x68, 0x74, 0x6c, 0x63, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x06, + 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x6d, 0x61, 0x78, 0x48, 0x74, 0x6c, 0x63, 0x4d, 0x73, 0x61, + 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, + 0x18, 0x07, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x6c, 0x61, 0x73, 0x74, 0x55, 0x70, 0x64, 0x61, + 0x74, 0x65, 0x12, 0x4e, 0x0a, 0x0e, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5f, 0x72, 0x65, 0x63, + 0x6f, 0x72, 0x64, 0x73, 0x18, 0x08, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x6c, 0x6e, 0x72, + 0x70, 0x63, 0x2e, 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, + 0x2e, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x45, 0x6e, + 0x74, 0x72, 0x79, 0x52, 0x0d, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x52, 0x65, 0x63, 0x6f, 0x72, + 0x64, 0x73, 0x12, 0x31, 0x0a, 0x15, 0x69, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x5f, 0x66, 0x65, + 0x65, 0x5f, 0x62, 0x61, 0x73, 0x65, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x09, 0x20, 0x01, 0x28, + 0x05, 0x52, 0x12, 0x69, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x46, 0x65, 0x65, 0x42, 0x61, 0x73, + 0x65, 0x4d, 0x73, 0x61, 0x74, 0x12, 0x3c, 0x0a, 0x1b, 0x69, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, + 0x5f, 0x66, 0x65, 0x65, 0x5f, 0x72, 0x61, 0x74, 0x65, 0x5f, 0x6d, 0x69, 0x6c, 0x6c, 0x69, 0x5f, + 0x6d, 0x73, 0x61, 0x74, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x05, 0x52, 0x17, 0x69, 0x6e, 0x62, 0x6f, + 0x75, 0x6e, 0x64, 0x46, 0x65, 0x65, 0x52, 0x61, 0x74, 0x65, 0x4d, 0x69, 0x6c, 0x6c, 0x69, 0x4d, + 0x73, 0x61, 0x74, 0x1a, 0x40, 0x0a, 0x12, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x52, 0x65, 0x63, + 0x6f, 0x72, 0x64, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xcc, 0x03, 0x0a, 0x0b, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, + 0x6c, 0x45, 0x64, 0x67, 0x65, 0x12, 0x21, 0x0a, 0x0a, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, + 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x42, 0x02, 0x30, 0x01, 0x52, 0x09, 0x63, + 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x68, 0x61, 0x6e, + 0x5f, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x68, + 0x61, 0x6e, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x23, 0x0a, 0x0b, 0x6c, 0x61, 0x73, 0x74, 0x5f, + 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x42, 0x02, 0x18, 0x01, + 0x52, 0x0a, 0x6c, 0x61, 0x73, 0x74, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x1b, 0x0a, 0x09, + 0x6e, 0x6f, 0x64, 0x65, 0x31, 0x5f, 0x70, 0x75, 0x62, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x08, 0x6e, 0x6f, 0x64, 0x65, 0x31, 0x50, 0x75, 0x62, 0x12, 0x1b, 0x0a, 0x09, 0x6e, 0x6f, 0x64, + 0x65, 0x32, 0x5f, 0x70, 0x75, 0x62, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6e, 0x6f, + 0x64, 0x65, 0x32, 0x50, 0x75, 0x62, 0x12, 0x1a, 0x0a, 0x08, 0x63, 0x61, 0x70, 0x61, 0x63, 0x69, + 0x74, 0x79, 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x63, 0x61, 0x70, 0x61, 0x63, 0x69, + 0x74, 0x79, 0x12, 0x37, 0x0a, 0x0c, 0x6e, 0x6f, 0x64, 0x65, 0x31, 0x5f, 0x70, 0x6f, 0x6c, 0x69, + 0x63, 0x79, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, + 0x2e, 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x0b, + 0x6e, 0x6f, 0x64, 0x65, 0x31, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x37, 0x0a, 0x0c, 0x6e, + 0x6f, 0x64, 0x65, 0x32, 0x5f, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x18, 0x08, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x14, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, + 0x67, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x0b, 0x6e, 0x6f, 0x64, 0x65, 0x32, 0x50, 0x6f, + 0x6c, 0x69, 0x63, 0x79, 0x12, 0x4c, 0x0a, 0x0e, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5f, 0x72, + 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x18, 0x09, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x6c, + 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x45, 0x64, 0x67, 0x65, + 0x2e, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x45, 0x6e, + 0x74, 0x72, 0x79, 0x52, 0x0d, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x52, 0x65, 0x63, 0x6f, 0x72, + 0x64, 0x73, 0x1a, 0x40, 0x0a, 0x12, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x52, 0x65, 0x63, 0x6f, + 0x72, 0x64, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, + 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x3a, 0x02, 0x38, 0x01, 0x22, 0x46, 0x0a, 0x13, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x47, + 0x72, 0x61, 0x70, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2f, 0x0a, 0x13, 0x69, + 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x5f, 0x75, 0x6e, 0x61, 0x6e, 0x6e, 0x6f, 0x75, 0x6e, 0x63, + 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x12, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, + 0x65, 0x55, 0x6e, 0x61, 0x6e, 0x6e, 0x6f, 0x75, 0x6e, 0x63, 0x65, 0x64, 0x22, 0x64, 0x0a, 0x0c, + 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x47, 0x72, 0x61, 0x70, 0x68, 0x12, 0x2a, 0x0a, 0x05, + 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x6c, 0x6e, + 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x67, 0x68, 0x74, 0x6e, 0x69, 0x6e, 0x67, 0x4e, 0x6f, 0x64, + 0x65, 0x52, 0x05, 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x12, 0x28, 0x0a, 0x05, 0x65, 0x64, 0x67, 0x65, + 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, + 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x45, 0x64, 0x67, 0x65, 0x52, 0x05, 0x65, 0x64, 0x67, + 0x65, 0x73, 0x22, 0x41, 0x0a, 0x12, 0x4e, 0x6f, 0x64, 0x65, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, + 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2b, 0x0a, 0x05, 0x74, 0x79, 0x70, 0x65, + 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0e, 0x32, 0x15, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, + 0x4e, 0x6f, 0x64, 0x65, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x54, 0x79, 0x70, 0x65, 0x52, 0x05, + 0x74, 0x79, 0x70, 0x65, 0x73, 0x22, 0xe1, 0x01, 0x0a, 0x13, 0x4e, 0x6f, 0x64, 0x65, 0x4d, 0x65, + 0x74, 0x72, 0x69, 0x63, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x6c, 0x0a, + 0x16, 0x62, 0x65, 0x74, 0x77, 0x65, 0x65, 0x6e, 0x6e, 0x65, 0x73, 0x73, 0x5f, 0x63, 0x65, 0x6e, + 0x74, 0x72, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x35, 0x2e, + 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, + 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x42, 0x65, 0x74, 0x77, 0x65, 0x65, + 0x6e, 0x6e, 0x65, 0x73, 0x73, 0x43, 0x65, 0x6e, 0x74, 0x72, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x45, + 0x6e, 0x74, 0x72, 0x79, 0x52, 0x15, 0x62, 0x65, 0x74, 0x77, 0x65, 0x65, 0x6e, 0x6e, 0x65, 0x73, + 0x73, 0x43, 0x65, 0x6e, 0x74, 0x72, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x1a, 0x5c, 0x0a, 0x1a, 0x42, + 0x65, 0x74, 0x77, 0x65, 0x65, 0x6e, 0x6e, 0x65, 0x73, 0x73, 0x43, 0x65, 0x6e, 0x74, 0x72, 0x61, + 0x6c, 0x69, 0x74, 0x79, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x28, 0x0a, 0x05, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x6c, 0x6e, 0x72, + 0x70, 0x63, 0x2e, 0x46, 0x6c, 0x6f, 0x61, 0x74, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x52, 0x05, + 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x4e, 0x0a, 0x0b, 0x46, 0x6c, 0x6f, + 0x61, 0x74, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x01, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x29, + 0x0a, 0x10, 0x6e, 0x6f, 0x72, 0x6d, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x76, 0x61, 0x6c, + 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x01, 0x52, 0x0f, 0x6e, 0x6f, 0x72, 0x6d, 0x61, 0x6c, + 0x69, 0x7a, 0x65, 0x64, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x4d, 0x0a, 0x0f, 0x43, 0x68, 0x61, + 0x6e, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x07, + 0x63, 0x68, 0x61, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x42, 0x02, 0x30, + 0x01, 0x52, 0x06, 0x63, 0x68, 0x61, 0x6e, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x68, 0x61, + 0x6e, 0x5f, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, + 0x68, 0x61, 0x6e, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x22, 0x14, 0x0a, 0x12, 0x4e, 0x65, 0x74, 0x77, + 0x6f, 0x72, 0x6b, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0xd5, + 0x03, 0x0a, 0x0b, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x25, + 0x0a, 0x0e, 0x67, 0x72, 0x61, 0x70, 0x68, 0x5f, 0x64, 0x69, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0d, 0x67, 0x72, 0x61, 0x70, 0x68, 0x44, 0x69, 0x61, + 0x6d, 0x65, 0x74, 0x65, 0x72, 0x12, 0x24, 0x0a, 0x0e, 0x61, 0x76, 0x67, 0x5f, 0x6f, 0x75, 0x74, + 0x5f, 0x64, 0x65, 0x67, 0x72, 0x65, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x01, 0x52, 0x0c, 0x61, + 0x76, 0x67, 0x4f, 0x75, 0x74, 0x44, 0x65, 0x67, 0x72, 0x65, 0x65, 0x12, 0x24, 0x0a, 0x0e, 0x6d, + 0x61, 0x78, 0x5f, 0x6f, 0x75, 0x74, 0x5f, 0x64, 0x65, 0x67, 0x72, 0x65, 0x65, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x0d, 0x52, 0x0c, 0x6d, 0x61, 0x78, 0x4f, 0x75, 0x74, 0x44, 0x65, 0x67, 0x72, 0x65, + 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x6e, 0x75, 0x6d, 0x5f, 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x6e, 0x75, 0x6d, 0x4e, 0x6f, 0x64, 0x65, 0x73, 0x12, 0x21, + 0x0a, 0x0c, 0x6e, 0x75, 0x6d, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x18, 0x05, + 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x6e, 0x75, 0x6d, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, + 0x73, 0x12, 0x34, 0x0a, 0x16, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x6e, 0x65, 0x74, 0x77, 0x6f, + 0x72, 0x6b, 0x5f, 0x63, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x18, 0x06, 0x20, 0x01, 0x28, + 0x03, 0x52, 0x14, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x43, + 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x12, 0x28, 0x0a, 0x10, 0x61, 0x76, 0x67, 0x5f, 0x63, + 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, + 0x01, 0x52, 0x0e, 0x61, 0x76, 0x67, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x53, 0x69, 0x7a, + 0x65, 0x12, 0x28, 0x0a, 0x10, 0x6d, 0x69, 0x6e, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, + 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0e, 0x6d, 0x69, 0x6e, + 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x28, 0x0a, 0x10, 0x6d, + 0x61, 0x78, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, + 0x09, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0e, 0x6d, 0x61, 0x78, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, + 0x6c, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x35, 0x0a, 0x17, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x6e, 0x5f, + 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x5f, 0x73, 0x61, 0x74, + 0x18, 0x0a, 0x20, 0x01, 0x28, 0x03, 0x52, 0x14, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x6e, 0x43, 0x68, + 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x53, 0x69, 0x7a, 0x65, 0x53, 0x61, 0x74, 0x12, 0x28, 0x0a, 0x10, + 0x6e, 0x75, 0x6d, 0x5f, 0x7a, 0x6f, 0x6d, 0x62, 0x69, 0x65, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x73, + 0x18, 0x0b, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0e, 0x6e, 0x75, 0x6d, 0x5a, 0x6f, 0x6d, 0x62, 0x69, + 0x65, 0x43, 0x68, 0x61, 0x6e, 0x73, 0x22, 0x0d, 0x0a, 0x0b, 0x53, 0x74, 0x6f, 0x70, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x0e, 0x0a, 0x0c, 0x53, 0x74, 0x6f, 0x70, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x0a, 0x19, 0x47, 0x72, 0x61, 0x70, 0x68, 0x54, 0x6f, + 0x70, 0x6f, 0x6c, 0x6f, 0x67, 0x79, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, + 0x6f, 0x6e, 0x22, 0xcd, 0x01, 0x0a, 0x13, 0x47, 0x72, 0x61, 0x70, 0x68, 0x54, 0x6f, 0x70, 0x6f, + 0x6c, 0x6f, 0x67, 0x79, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x34, 0x0a, 0x0c, 0x6e, 0x6f, + 0x64, 0x65, 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x11, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x55, 0x70, 0x64, + 0x61, 0x74, 0x65, 0x52, 0x0b, 0x6e, 0x6f, 0x64, 0x65, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, + 0x12, 0x41, 0x0a, 0x0f, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x5f, 0x75, 0x70, 0x64, 0x61, + 0x74, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x6c, 0x6e, 0x72, 0x70, + 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x45, 0x64, 0x67, 0x65, 0x55, 0x70, 0x64, + 0x61, 0x74, 0x65, 0x52, 0x0e, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x55, 0x70, 0x64, 0x61, + 0x74, 0x65, 0x73, 0x12, 0x3d, 0x0a, 0x0c, 0x63, 0x6c, 0x6f, 0x73, 0x65, 0x64, 0x5f, 0x63, 0x68, + 0x61, 0x6e, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, + 0x63, 0x2e, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x64, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x55, + 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x0b, 0x63, 0x6c, 0x6f, 0x73, 0x65, 0x64, 0x43, 0x68, 0x61, + 0x6e, 0x73, 0x22, 0xef, 0x02, 0x0a, 0x0a, 0x4e, 0x6f, 0x64, 0x65, 0x55, 0x70, 0x64, 0x61, 0x74, + 0x65, 0x12, 0x20, 0x0a, 0x09, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x18, 0x01, + 0x20, 0x03, 0x28, 0x09, 0x42, 0x02, 0x18, 0x01, 0x52, 0x09, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, + 0x73, 0x65, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x5f, + 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x69, 0x64, 0x65, 0x6e, 0x74, + 0x69, 0x74, 0x79, 0x4b, 0x65, 0x79, 0x12, 0x2b, 0x0a, 0x0f, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, + 0x5f, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x42, + 0x02, 0x18, 0x01, 0x52, 0x0e, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x46, 0x65, 0x61, 0x74, 0x75, + 0x72, 0x65, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x05, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x6f, 0x6c, + 0x6f, 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x12, + 0x39, 0x0a, 0x0e, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, + 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, + 0x4e, 0x6f, 0x64, 0x65, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x0d, 0x6e, 0x6f, 0x64, + 0x65, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x12, 0x3b, 0x0a, 0x08, 0x66, 0x65, + 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x6c, + 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x2e, + 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x08, 0x66, + 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x1a, 0x4b, 0x0a, 0x0d, 0x46, 0x65, 0x61, 0x74, 0x75, + 0x72, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x24, 0x0a, 0x05, 0x76, 0x61, + 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x6c, 0x6e, 0x72, 0x70, + 0x63, 0x2e, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x3a, 0x02, 0x38, 0x01, 0x22, 0x91, 0x02, 0x0a, 0x11, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, + 0x45, 0x64, 0x67, 0x65, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x1b, 0x0a, 0x07, 0x63, 0x68, + 0x61, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x42, 0x02, 0x30, 0x01, 0x52, + 0x06, 0x63, 0x68, 0x61, 0x6e, 0x49, 0x64, 0x12, 0x32, 0x0a, 0x0a, 0x63, 0x68, 0x61, 0x6e, 0x5f, + 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x6c, 0x6e, + 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x50, 0x6f, 0x69, 0x6e, 0x74, + 0x52, 0x09, 0x63, 0x68, 0x61, 0x6e, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x63, + 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x63, + 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x12, 0x3b, 0x0a, 0x0e, 0x72, 0x6f, 0x75, 0x74, 0x69, + 0x6e, 0x67, 0x5f, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x14, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x50, + 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x0d, 0x72, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x50, 0x6f, + 0x6c, 0x69, 0x63, 0x79, 0x12, 0x29, 0x0a, 0x10, 0x61, 0x64, 0x76, 0x65, 0x72, 0x74, 0x69, 0x73, + 0x69, 0x6e, 0x67, 0x5f, 0x6e, 0x6f, 0x64, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, + 0x61, 0x64, 0x76, 0x65, 0x72, 0x74, 0x69, 0x73, 0x69, 0x6e, 0x67, 0x4e, 0x6f, 0x64, 0x65, 0x12, + 0x27, 0x0a, 0x0f, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6e, 0x67, 0x5f, 0x6e, 0x6f, + 0x64, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, + 0x74, 0x69, 0x6e, 0x67, 0x4e, 0x6f, 0x64, 0x65, 0x22, 0xa7, 0x01, 0x0a, 0x13, 0x43, 0x6c, 0x6f, + 0x73, 0x65, 0x64, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, + 0x12, 0x1b, 0x0a, 0x07, 0x63, 0x68, 0x61, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x04, 0x42, 0x02, 0x30, 0x01, 0x52, 0x06, 0x63, 0x68, 0x61, 0x6e, 0x49, 0x64, 0x12, 0x1a, 0x0a, + 0x08, 0x63, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, + 0x08, 0x63, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x6c, 0x6f, + 0x73, 0x65, 0x64, 0x5f, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, + 0x52, 0x0c, 0x63, 0x6c, 0x6f, 0x73, 0x65, 0x64, 0x48, 0x65, 0x69, 0x67, 0x68, 0x74, 0x12, 0x32, + 0x0a, 0x0a, 0x63, 0x68, 0x61, 0x6e, 0x5f, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, + 0x65, 0x6c, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x09, 0x63, 0x68, 0x61, 0x6e, 0x50, 0x6f, 0x69, + 0x6e, 0x74, 0x22, 0xcf, 0x01, 0x0a, 0x07, 0x48, 0x6f, 0x70, 0x48, 0x69, 0x6e, 0x74, 0x12, 0x17, + 0x0a, 0x07, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x06, 0x6e, 0x6f, 0x64, 0x65, 0x49, 0x64, 0x12, 0x1b, 0x0a, 0x07, 0x63, 0x68, 0x61, 0x6e, 0x5f, + 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x42, 0x02, 0x30, 0x01, 0x52, 0x06, 0x63, 0x68, + 0x61, 0x6e, 0x49, 0x64, 0x12, 0x22, 0x0a, 0x0d, 0x66, 0x65, 0x65, 0x5f, 0x62, 0x61, 0x73, 0x65, + 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x66, 0x65, 0x65, + 0x42, 0x61, 0x73, 0x65, 0x4d, 0x73, 0x61, 0x74, 0x12, 0x3e, 0x0a, 0x1b, 0x66, 0x65, 0x65, 0x5f, + 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x72, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x5f, 0x6d, 0x69, 0x6c, + 0x6c, 0x69, 0x6f, 0x6e, 0x74, 0x68, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x19, 0x66, + 0x65, 0x65, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x72, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x4d, 0x69, + 0x6c, 0x6c, 0x69, 0x6f, 0x6e, 0x74, 0x68, 0x73, 0x12, 0x2a, 0x0a, 0x11, 0x63, 0x6c, 0x74, 0x76, + 0x5f, 0x65, 0x78, 0x70, 0x69, 0x72, 0x79, 0x5f, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x18, 0x05, 0x20, + 0x01, 0x28, 0x0d, 0x52, 0x0f, 0x63, 0x6c, 0x74, 0x76, 0x45, 0x78, 0x70, 0x69, 0x72, 0x79, 0x44, + 0x65, 0x6c, 0x74, 0x61, 0x22, 0x1e, 0x0a, 0x05, 0x53, 0x65, 0x74, 0x49, 0x44, 0x12, 0x15, 0x0a, + 0x06, 0x73, 0x65, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x73, + 0x65, 0x74, 0x49, 0x64, 0x22, 0x38, 0x0a, 0x09, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x48, 0x69, 0x6e, + 0x74, 0x12, 0x2b, 0x0a, 0x09, 0x68, 0x6f, 0x70, 0x5f, 0x68, 0x69, 0x6e, 0x74, 0x73, 0x18, 0x01, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x48, 0x6f, 0x70, + 0x48, 0x69, 0x6e, 0x74, 0x52, 0x08, 0x68, 0x6f, 0x70, 0x48, 0x69, 0x6e, 0x74, 0x73, 0x22, 0xc4, + 0x02, 0x0a, 0x12, 0x42, 0x6c, 0x69, 0x6e, 0x64, 0x65, 0x64, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, + 0x74, 0x50, 0x61, 0x74, 0x68, 0x12, 0x35, 0x0a, 0x0c, 0x62, 0x6c, 0x69, 0x6e, 0x64, 0x65, 0x64, + 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x6c, 0x6e, + 0x72, 0x70, 0x63, 0x2e, 0x42, 0x6c, 0x69, 0x6e, 0x64, 0x65, 0x64, 0x50, 0x61, 0x74, 0x68, 0x52, + 0x0b, 0x62, 0x6c, 0x69, 0x6e, 0x64, 0x65, 0x64, 0x50, 0x61, 0x74, 0x68, 0x12, 0x22, 0x0a, 0x0d, + 0x62, 0x61, 0x73, 0x65, 0x5f, 0x66, 0x65, 0x65, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x04, 0x52, 0x0b, 0x62, 0x61, 0x73, 0x65, 0x46, 0x65, 0x65, 0x4d, 0x73, 0x61, 0x74, + 0x12, 0x32, 0x0a, 0x15, 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x72, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, + 0x5f, 0x66, 0x65, 0x65, 0x5f, 0x72, 0x61, 0x74, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, + 0x13, 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x72, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x46, 0x65, 0x65, + 0x52, 0x61, 0x74, 0x65, 0x12, 0x28, 0x0a, 0x10, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x63, 0x6c, + 0x74, 0x76, 0x5f, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0e, + 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x43, 0x6c, 0x74, 0x76, 0x44, 0x65, 0x6c, 0x74, 0x61, 0x12, 0x22, + 0x0a, 0x0d, 0x68, 0x74, 0x6c, 0x63, 0x5f, 0x6d, 0x69, 0x6e, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, + 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x68, 0x74, 0x6c, 0x63, 0x4d, 0x69, 0x6e, 0x4d, 0x73, + 0x61, 0x74, 0x12, 0x22, 0x0a, 0x0d, 0x68, 0x74, 0x6c, 0x63, 0x5f, 0x6d, 0x61, 0x78, 0x5f, 0x6d, + 0x73, 0x61, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x68, 0x74, 0x6c, 0x63, 0x4d, + 0x61, 0x78, 0x4d, 0x73, 0x61, 0x74, 0x12, 0x2d, 0x0a, 0x08, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, + 0x65, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0e, 0x32, 0x11, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, + 0x2e, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x42, 0x69, 0x74, 0x52, 0x08, 0x66, 0x65, 0x61, + 0x74, 0x75, 0x72, 0x65, 0x73, 0x22, 0x97, 0x01, 0x0a, 0x0b, 0x42, 0x6c, 0x69, 0x6e, 0x64, 0x65, + 0x64, 0x50, 0x61, 0x74, 0x68, 0x12, 0x2b, 0x0a, 0x11, 0x69, 0x6e, 0x74, 0x72, 0x6f, 0x64, 0x75, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6e, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, + 0x52, 0x10, 0x69, 0x6e, 0x74, 0x72, 0x6f, 0x64, 0x75, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x4e, 0x6f, + 0x64, 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x62, 0x6c, 0x69, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x70, + 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0d, 0x62, 0x6c, 0x69, 0x6e, + 0x64, 0x69, 0x6e, 0x67, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x34, 0x0a, 0x0c, 0x62, 0x6c, 0x69, + 0x6e, 0x64, 0x65, 0x64, 0x5f, 0x68, 0x6f, 0x70, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x11, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x42, 0x6c, 0x69, 0x6e, 0x64, 0x65, 0x64, 0x48, + 0x6f, 0x70, 0x52, 0x0b, 0x62, 0x6c, 0x69, 0x6e, 0x64, 0x65, 0x64, 0x48, 0x6f, 0x70, 0x73, 0x22, + 0x56, 0x0a, 0x0a, 0x42, 0x6c, 0x69, 0x6e, 0x64, 0x65, 0x64, 0x48, 0x6f, 0x70, 0x12, 0x21, 0x0a, + 0x0c, 0x62, 0x6c, 0x69, 0x6e, 0x64, 0x65, 0x64, 0x5f, 0x6e, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x62, 0x6c, 0x69, 0x6e, 0x64, 0x65, 0x64, 0x4e, 0x6f, 0x64, 0x65, + 0x12, 0x25, 0x0a, 0x0e, 0x65, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x5f, 0x64, 0x61, + 0x74, 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0d, 0x65, 0x6e, 0x63, 0x72, 0x79, 0x70, + 0x74, 0x65, 0x64, 0x44, 0x61, 0x74, 0x61, 0x22, 0xa8, 0x01, 0x0a, 0x0f, 0x41, 0x4d, 0x50, 0x49, + 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x2d, 0x0a, 0x05, 0x73, + 0x74, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x17, 0x2e, 0x6c, 0x6e, 0x72, + 0x70, 0x63, 0x2e, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x48, 0x54, 0x4c, 0x43, 0x53, 0x74, + 0x61, 0x74, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x73, 0x65, + 0x74, 0x74, 0x6c, 0x65, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, + 0x52, 0x0b, 0x73, 0x65, 0x74, 0x74, 0x6c, 0x65, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x1f, 0x0a, + 0x0b, 0x73, 0x65, 0x74, 0x74, 0x6c, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x03, 0x52, 0x0a, 0x73, 0x65, 0x74, 0x74, 0x6c, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x22, + 0x0a, 0x0d, 0x61, 0x6d, 0x74, 0x5f, 0x70, 0x61, 0x69, 0x64, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, + 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b, 0x61, 0x6d, 0x74, 0x50, 0x61, 0x69, 0x64, 0x4d, 0x73, + 0x61, 0x74, 0x22, 0xac, 0x0a, 0x0a, 0x07, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x12, 0x12, + 0x0a, 0x04, 0x6d, 0x65, 0x6d, 0x6f, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6d, 0x65, + 0x6d, 0x6f, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x5f, 0x70, 0x72, 0x65, 0x69, 0x6d, 0x61, 0x67, 0x65, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x72, 0x50, 0x72, 0x65, 0x69, 0x6d, 0x61, 0x67, + 0x65, 0x12, 0x15, 0x0a, 0x06, 0x72, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x0c, 0x52, 0x05, 0x72, 0x48, 0x61, 0x73, 0x68, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x1d, + 0x0a, 0x0a, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x17, 0x20, 0x01, + 0x28, 0x03, 0x52, 0x09, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x4d, 0x73, 0x61, 0x74, 0x12, 0x1c, 0x0a, + 0x07, 0x73, 0x65, 0x74, 0x74, 0x6c, 0x65, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x42, 0x02, + 0x18, 0x01, 0x52, 0x07, 0x73, 0x65, 0x74, 0x74, 0x6c, 0x65, 0x64, 0x12, 0x23, 0x0a, 0x0d, 0x63, + 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x64, 0x61, 0x74, 0x65, 0x18, 0x07, 0x20, 0x01, + 0x28, 0x03, 0x52, 0x0c, 0x63, 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x61, 0x74, 0x65, + 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x65, 0x74, 0x74, 0x6c, 0x65, 0x5f, 0x64, 0x61, 0x74, 0x65, 0x18, + 0x08, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x73, 0x65, 0x74, 0x74, 0x6c, 0x65, 0x44, 0x61, 0x74, + 0x65, 0x12, 0x27, 0x0a, 0x0f, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x72, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x70, 0x61, 0x79, 0x6d, + 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x29, 0x0a, 0x10, 0x64, 0x65, + 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x0a, + 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0f, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, + 0x6e, 0x48, 0x61, 0x73, 0x68, 0x12, 0x16, 0x0a, 0x06, 0x65, 0x78, 0x70, 0x69, 0x72, 0x79, 0x18, + 0x0b, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x65, 0x78, 0x70, 0x69, 0x72, 0x79, 0x12, 0x23, 0x0a, + 0x0d, 0x66, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x18, 0x0c, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x66, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x41, 0x64, + 0x64, 0x72, 0x12, 0x1f, 0x0a, 0x0b, 0x63, 0x6c, 0x74, 0x76, 0x5f, 0x65, 0x78, 0x70, 0x69, 0x72, + 0x79, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x63, 0x6c, 0x74, 0x76, 0x45, 0x78, 0x70, + 0x69, 0x72, 0x79, 0x12, 0x31, 0x0a, 0x0b, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x5f, 0x68, 0x69, 0x6e, + 0x74, 0x73, 0x18, 0x0e, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, + 0x2e, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x48, 0x69, 0x6e, 0x74, 0x52, 0x0a, 0x72, 0x6f, 0x75, 0x74, + 0x65, 0x48, 0x69, 0x6e, 0x74, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, + 0x65, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, + 0x12, 0x1b, 0x0a, 0x09, 0x61, 0x64, 0x64, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x10, 0x20, + 0x01, 0x28, 0x04, 0x52, 0x08, 0x61, 0x64, 0x64, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x21, 0x0a, + 0x0c, 0x73, 0x65, 0x74, 0x74, 0x6c, 0x65, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x11, 0x20, + 0x01, 0x28, 0x04, 0x52, 0x0b, 0x73, 0x65, 0x74, 0x74, 0x6c, 0x65, 0x49, 0x6e, 0x64, 0x65, 0x78, + 0x12, 0x1d, 0x0a, 0x08, 0x61, 0x6d, 0x74, 0x5f, 0x70, 0x61, 0x69, 0x64, 0x18, 0x12, 0x20, 0x01, + 0x28, 0x03, 0x42, 0x02, 0x18, 0x01, 0x52, 0x07, 0x61, 0x6d, 0x74, 0x50, 0x61, 0x69, 0x64, 0x12, + 0x20, 0x0a, 0x0c, 0x61, 0x6d, 0x74, 0x5f, 0x70, 0x61, 0x69, 0x64, 0x5f, 0x73, 0x61, 0x74, 0x18, + 0x13, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x61, 0x6d, 0x74, 0x50, 0x61, 0x69, 0x64, 0x53, 0x61, + 0x74, 0x12, 0x22, 0x0a, 0x0d, 0x61, 0x6d, 0x74, 0x5f, 0x70, 0x61, 0x69, 0x64, 0x5f, 0x6d, 0x73, + 0x61, 0x74, 0x18, 0x14, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b, 0x61, 0x6d, 0x74, 0x50, 0x61, 0x69, + 0x64, 0x4d, 0x73, 0x61, 0x74, 0x12, 0x31, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x15, + 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1b, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x49, 0x6e, 0x76, + 0x6f, 0x69, 0x63, 0x65, 0x2e, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x53, 0x74, 0x61, 0x74, + 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x28, 0x0a, 0x05, 0x68, 0x74, 0x6c, 0x63, + 0x73, 0x18, 0x16, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, + 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x48, 0x54, 0x4c, 0x43, 0x52, 0x05, 0x68, 0x74, 0x6c, + 0x63, 0x73, 0x12, 0x38, 0x0a, 0x08, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x18, 0x18, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x49, 0x6e, 0x76, + 0x6f, 0x69, 0x63, 0x65, 0x2e, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x45, 0x6e, 0x74, + 0x72, 0x79, 0x52, 0x08, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x12, 0x1d, 0x0a, 0x0a, + 0x69, 0x73, 0x5f, 0x6b, 0x65, 0x79, 0x73, 0x65, 0x6e, 0x64, 0x18, 0x19, 0x20, 0x01, 0x28, 0x08, + 0x52, 0x09, 0x69, 0x73, 0x4b, 0x65, 0x79, 0x73, 0x65, 0x6e, 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x70, + 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x18, 0x1a, 0x20, 0x01, 0x28, + 0x0c, 0x52, 0x0b, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x41, 0x64, 0x64, 0x72, 0x12, 0x15, + 0x0a, 0x06, 0x69, 0x73, 0x5f, 0x61, 0x6d, 0x70, 0x18, 0x1b, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, + 0x69, 0x73, 0x41, 0x6d, 0x70, 0x12, 0x4f, 0x0a, 0x11, 0x61, 0x6d, 0x70, 0x5f, 0x69, 0x6e, 0x76, + 0x6f, 0x69, 0x63, 0x65, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x1c, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x23, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, + 0x2e, 0x41, 0x6d, 0x70, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, + 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0f, 0x61, 0x6d, 0x70, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, + 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x69, 0x73, 0x5f, 0x62, 0x6c, 0x69, + 0x6e, 0x64, 0x65, 0x64, 0x18, 0x1d, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x69, 0x73, 0x42, 0x6c, + 0x69, 0x6e, 0x64, 0x65, 0x64, 0x12, 0x48, 0x0a, 0x13, 0x62, 0x6c, 0x69, 0x6e, 0x64, 0x65, 0x64, + 0x5f, 0x70, 0x61, 0x74, 0x68, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x1e, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x42, 0x6c, 0x69, 0x6e, 0x64, + 0x65, 0x64, 0x50, 0x61, 0x74, 0x68, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x11, 0x62, 0x6c, + 0x69, 0x6e, 0x64, 0x65, 0x64, 0x50, 0x61, 0x74, 0x68, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x1a, + 0x4b, 0x0a, 0x0d, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, + 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x6b, + 0x65, 0x79, 0x12, 0x24, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x0e, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, + 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x5a, 0x0a, 0x14, + 0x41, 0x6d, 0x70, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x45, + 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x2c, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x4d, + 0x50, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x05, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x41, 0x0a, 0x0c, 0x49, 0x6e, 0x76, 0x6f, + 0x69, 0x63, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x08, 0x0a, 0x04, 0x4f, 0x50, 0x45, 0x4e, + 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x53, 0x45, 0x54, 0x54, 0x4c, 0x45, 0x44, 0x10, 0x01, 0x12, + 0x0c, 0x0a, 0x08, 0x43, 0x41, 0x4e, 0x43, 0x45, 0x4c, 0x45, 0x44, 0x10, 0x02, 0x12, 0x0c, 0x0a, + 0x08, 0x41, 0x43, 0x43, 0x45, 0x50, 0x54, 0x45, 0x44, 0x10, 0x03, 0x4a, 0x04, 0x08, 0x02, 0x10, + 0x03, 0x22, 0xef, 0x01, 0x0a, 0x11, 0x42, 0x6c, 0x69, 0x6e, 0x64, 0x65, 0x64, 0x50, 0x61, 0x74, + 0x68, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x2e, 0x0a, 0x11, 0x6d, 0x69, 0x6e, 0x5f, 0x6e, + 0x75, 0x6d, 0x5f, 0x72, 0x65, 0x61, 0x6c, 0x5f, 0x68, 0x6f, 0x70, 0x73, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0d, 0x48, 0x00, 0x52, 0x0e, 0x6d, 0x69, 0x6e, 0x4e, 0x75, 0x6d, 0x52, 0x65, 0x61, 0x6c, + 0x48, 0x6f, 0x70, 0x73, 0x88, 0x01, 0x01, 0x12, 0x1e, 0x0a, 0x08, 0x6e, 0x75, 0x6d, 0x5f, 0x68, + 0x6f, 0x70, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x48, 0x01, 0x52, 0x07, 0x6e, 0x75, 0x6d, + 0x48, 0x6f, 0x70, 0x73, 0x88, 0x01, 0x01, 0x12, 0x27, 0x0a, 0x0d, 0x6d, 0x61, 0x78, 0x5f, 0x6e, + 0x75, 0x6d, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x48, 0x02, + 0x52, 0x0b, 0x6d, 0x61, 0x78, 0x4e, 0x75, 0x6d, 0x50, 0x61, 0x74, 0x68, 0x73, 0x88, 0x01, 0x01, + 0x12, 0x2c, 0x0a, 0x12, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x6f, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, + 0x6e, 0x5f, 0x6c, 0x69, 0x73, 0x74, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x10, 0x6e, 0x6f, + 0x64, 0x65, 0x4f, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x4c, 0x69, 0x73, 0x74, 0x42, 0x14, + 0x0a, 0x12, 0x5f, 0x6d, 0x69, 0x6e, 0x5f, 0x6e, 0x75, 0x6d, 0x5f, 0x72, 0x65, 0x61, 0x6c, 0x5f, + 0x68, 0x6f, 0x70, 0x73, 0x42, 0x0b, 0x0a, 0x09, 0x5f, 0x6e, 0x75, 0x6d, 0x5f, 0x68, 0x6f, 0x70, + 0x73, 0x42, 0x10, 0x0a, 0x0e, 0x5f, 0x6d, 0x61, 0x78, 0x5f, 0x6e, 0x75, 0x6d, 0x5f, 0x70, 0x61, + 0x74, 0x68, 0x73, 0x22, 0xfc, 0x03, 0x0a, 0x0b, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x48, + 0x54, 0x4c, 0x43, 0x12, 0x1b, 0x0a, 0x07, 0x63, 0x68, 0x61, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x04, 0x42, 0x02, 0x30, 0x01, 0x52, 0x06, 0x63, 0x68, 0x61, 0x6e, 0x49, 0x64, + 0x12, 0x1d, 0x0a, 0x0a, 0x68, 0x74, 0x6c, 0x63, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x68, 0x74, 0x6c, 0x63, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, + 0x19, 0x0a, 0x08, 0x61, 0x6d, 0x74, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x04, 0x52, 0x07, 0x61, 0x6d, 0x74, 0x4d, 0x73, 0x61, 0x74, 0x12, 0x23, 0x0a, 0x0d, 0x61, 0x63, + 0x63, 0x65, 0x70, 0x74, 0x5f, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x05, 0x52, 0x0c, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x48, 0x65, 0x69, 0x67, 0x68, 0x74, 0x12, + 0x1f, 0x0a, 0x0b, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x05, + 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x54, 0x69, 0x6d, 0x65, + 0x12, 0x21, 0x0a, 0x0c, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, + 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x54, + 0x69, 0x6d, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x65, 0x78, 0x70, 0x69, 0x72, 0x79, 0x5f, 0x68, 0x65, + 0x69, 0x67, 0x68, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0c, 0x65, 0x78, 0x70, 0x69, + 0x72, 0x79, 0x48, 0x65, 0x69, 0x67, 0x68, 0x74, 0x12, 0x2d, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, + 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x17, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, + 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x48, 0x54, 0x4c, 0x43, 0x53, 0x74, 0x61, 0x74, 0x65, + 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x4c, 0x0a, 0x0e, 0x63, 0x75, 0x73, 0x74, 0x6f, + 0x6d, 0x5f, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x18, 0x09, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x25, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x48, + 0x54, 0x4c, 0x43, 0x2e, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, + 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0d, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x52, 0x65, + 0x63, 0x6f, 0x72, 0x64, 0x73, 0x12, 0x2b, 0x0a, 0x12, 0x6d, 0x70, 0x70, 0x5f, 0x74, 0x6f, 0x74, + 0x61, 0x6c, 0x5f, 0x61, 0x6d, 0x74, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x0a, 0x20, 0x01, 0x28, + 0x04, 0x52, 0x0f, 0x6d, 0x70, 0x70, 0x54, 0x6f, 0x74, 0x61, 0x6c, 0x41, 0x6d, 0x74, 0x4d, 0x73, + 0x61, 0x74, 0x12, 0x1c, 0x0a, 0x03, 0x61, 0x6d, 0x70, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x0a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x4d, 0x50, 0x52, 0x03, 0x61, 0x6d, 0x70, 0x1a, 0x40, 0x0a, 0x12, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, - 0x38, 0x01, 0x22, 0xcc, 0x03, 0x0a, 0x0b, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x45, 0x64, - 0x67, 0x65, 0x12, 0x21, 0x0a, 0x0a, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x5f, 0x69, 0x64, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x42, 0x02, 0x30, 0x01, 0x52, 0x09, 0x63, 0x68, 0x61, 0x6e, - 0x6e, 0x65, 0x6c, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x68, 0x61, 0x6e, 0x5f, 0x70, 0x6f, - 0x69, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x68, 0x61, 0x6e, 0x50, - 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x23, 0x0a, 0x0b, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x75, 0x70, 0x64, - 0x61, 0x74, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x42, 0x02, 0x18, 0x01, 0x52, 0x0a, 0x6c, - 0x61, 0x73, 0x74, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x6e, 0x6f, 0x64, - 0x65, 0x31, 0x5f, 0x70, 0x75, 0x62, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6e, 0x6f, - 0x64, 0x65, 0x31, 0x50, 0x75, 0x62, 0x12, 0x1b, 0x0a, 0x09, 0x6e, 0x6f, 0x64, 0x65, 0x32, 0x5f, - 0x70, 0x75, 0x62, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6e, 0x6f, 0x64, 0x65, 0x32, - 0x50, 0x75, 0x62, 0x12, 0x1a, 0x0a, 0x08, 0x63, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x18, - 0x06, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x63, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x12, - 0x37, 0x0a, 0x0c, 0x6e, 0x6f, 0x64, 0x65, 0x31, 0x5f, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x18, - 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x6f, - 0x75, 0x74, 0x69, 0x6e, 0x67, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x0b, 0x6e, 0x6f, 0x64, - 0x65, 0x31, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x37, 0x0a, 0x0c, 0x6e, 0x6f, 0x64, 0x65, - 0x32, 0x5f, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, - 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x50, 0x6f, - 0x6c, 0x69, 0x63, 0x79, 0x52, 0x0b, 0x6e, 0x6f, 0x64, 0x65, 0x32, 0x50, 0x6f, 0x6c, 0x69, 0x63, - 0x79, 0x12, 0x4c, 0x0a, 0x0e, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5f, 0x72, 0x65, 0x63, 0x6f, - 0x72, 0x64, 0x73, 0x18, 0x09, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x6c, 0x6e, 0x72, 0x70, - 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x45, 0x64, 0x67, 0x65, 0x2e, 0x43, 0x75, + 0x38, 0x01, 0x22, 0x8c, 0x01, 0x0a, 0x03, 0x41, 0x4d, 0x50, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x6f, + 0x6f, 0x74, 0x5f, 0x73, 0x68, 0x61, 0x72, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, + 0x72, 0x6f, 0x6f, 0x74, 0x53, 0x68, 0x61, 0x72, 0x65, 0x12, 0x15, 0x0a, 0x06, 0x73, 0x65, 0x74, + 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x73, 0x65, 0x74, 0x49, 0x64, + 0x12, 0x1f, 0x0a, 0x0b, 0x63, 0x68, 0x69, 0x6c, 0x64, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x63, 0x68, 0x69, 0x6c, 0x64, 0x49, 0x6e, 0x64, 0x65, + 0x78, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x61, 0x73, 0x68, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, + 0x04, 0x68, 0x61, 0x73, 0x68, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x72, 0x65, 0x69, 0x6d, 0x61, 0x67, + 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x70, 0x72, 0x65, 0x69, 0x6d, 0x61, 0x67, + 0x65, 0x22, 0x94, 0x01, 0x0a, 0x12, 0x41, 0x64, 0x64, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x15, 0x0a, 0x06, 0x72, 0x5f, 0x68, 0x61, + 0x73, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x72, 0x48, 0x61, 0x73, 0x68, 0x12, + 0x27, 0x0a, 0x0f, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, + 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x61, 0x64, 0x64, 0x5f, + 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x10, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x61, 0x64, 0x64, + 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, + 0x5f, 0x61, 0x64, 0x64, 0x72, 0x18, 0x11, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x70, 0x61, 0x79, + 0x6d, 0x65, 0x6e, 0x74, 0x41, 0x64, 0x64, 0x72, 0x22, 0x46, 0x0a, 0x0b, 0x50, 0x61, 0x79, 0x6d, + 0x65, 0x6e, 0x74, 0x48, 0x61, 0x73, 0x68, 0x12, 0x20, 0x0a, 0x0a, 0x72, 0x5f, 0x68, 0x61, 0x73, + 0x68, 0x5f, 0x73, 0x74, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x02, 0x18, 0x01, 0x52, + 0x08, 0x72, 0x48, 0x61, 0x73, 0x68, 0x53, 0x74, 0x72, 0x12, 0x15, 0x0a, 0x06, 0x72, 0x5f, 0x68, + 0x61, 0x73, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x72, 0x48, 0x61, 0x73, 0x68, + 0x22, 0xfc, 0x01, 0x0a, 0x12, 0x4c, 0x69, 0x73, 0x74, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x65, 0x6e, 0x64, 0x69, + 0x6e, 0x67, 0x5f, 0x6f, 0x6e, 0x6c, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x70, + 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x4f, 0x6e, 0x6c, 0x79, 0x12, 0x21, 0x0a, 0x0c, 0x69, 0x6e, + 0x64, 0x65, 0x78, 0x5f, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, + 0x52, 0x0b, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x12, 0x28, 0x0a, + 0x10, 0x6e, 0x75, 0x6d, 0x5f, 0x6d, 0x61, 0x78, 0x5f, 0x69, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, + 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0e, 0x6e, 0x75, 0x6d, 0x4d, 0x61, 0x78, 0x49, + 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x76, 0x65, 0x72, + 0x73, 0x65, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x72, 0x65, 0x76, 0x65, 0x72, + 0x73, 0x65, 0x64, 0x12, 0x2e, 0x0a, 0x13, 0x63, 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, + 0x64, 0x61, 0x74, 0x65, 0x5f, 0x73, 0x74, 0x61, 0x72, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x04, + 0x52, 0x11, 0x63, 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x61, 0x74, 0x65, 0x53, 0x74, + 0x61, 0x72, 0x74, 0x12, 0x2a, 0x0a, 0x11, 0x63, 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, + 0x64, 0x61, 0x74, 0x65, 0x5f, 0x65, 0x6e, 0x64, 0x18, 0x08, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0f, + 0x63, 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x61, 0x74, 0x65, 0x45, 0x6e, 0x64, 0x22, + 0x9b, 0x01, 0x0a, 0x13, 0x4c, 0x69, 0x73, 0x74, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2a, 0x0a, 0x08, 0x69, 0x6e, 0x76, 0x6f, 0x69, + 0x63, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x6c, 0x6e, 0x72, 0x70, + 0x63, 0x2e, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x52, 0x08, 0x69, 0x6e, 0x76, 0x6f, 0x69, + 0x63, 0x65, 0x73, 0x12, 0x2a, 0x0a, 0x11, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x69, 0x6e, 0x64, 0x65, + 0x78, 0x5f, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0f, + 0x6c, 0x61, 0x73, 0x74, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x12, + 0x2c, 0x0a, 0x12, 0x66, 0x69, 0x72, 0x73, 0x74, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x5f, 0x6f, + 0x66, 0x66, 0x73, 0x65, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x10, 0x66, 0x69, 0x72, + 0x73, 0x74, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x55, 0x0a, + 0x13, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, + 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1b, 0x0a, 0x09, 0x61, 0x64, 0x64, 0x5f, 0x69, 0x6e, 0x64, 0x65, + 0x78, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x61, 0x64, 0x64, 0x49, 0x6e, 0x64, 0x65, + 0x78, 0x12, 0x21, 0x0a, 0x0c, 0x73, 0x65, 0x74, 0x74, 0x6c, 0x65, 0x5f, 0x69, 0x6e, 0x64, 0x65, + 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x73, 0x65, 0x74, 0x74, 0x6c, 0x65, 0x49, + 0x6e, 0x64, 0x65, 0x78, 0x22, 0xcb, 0x06, 0x0a, 0x07, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, + 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x68, 0x61, 0x73, 0x68, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x48, + 0x61, 0x73, 0x68, 0x12, 0x18, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x03, 0x42, 0x02, 0x18, 0x01, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x27, 0x0a, + 0x0d, 0x63, 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x64, 0x61, 0x74, 0x65, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x03, 0x42, 0x02, 0x18, 0x01, 0x52, 0x0c, 0x63, 0x72, 0x65, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x44, 0x61, 0x74, 0x65, 0x12, 0x14, 0x0a, 0x03, 0x66, 0x65, 0x65, 0x18, 0x05, 0x20, + 0x01, 0x28, 0x03, 0x42, 0x02, 0x18, 0x01, 0x52, 0x03, 0x66, 0x65, 0x65, 0x12, 0x29, 0x0a, 0x10, + 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x70, 0x72, 0x65, 0x69, 0x6d, 0x61, 0x67, 0x65, + 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x50, + 0x72, 0x65, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x5f, 0x73, 0x61, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x53, 0x61, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x5f, 0x6d, 0x73, + 0x61, 0x74, 0x18, 0x08, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x4d, + 0x73, 0x61, 0x74, 0x12, 0x27, 0x0a, 0x0f, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x72, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x70, 0x61, + 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x34, 0x0a, 0x06, + 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1c, 0x2e, 0x6c, + 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x50, 0x61, 0x79, + 0x6d, 0x65, 0x6e, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, + 0x75, 0x73, 0x12, 0x17, 0x0a, 0x07, 0x66, 0x65, 0x65, 0x5f, 0x73, 0x61, 0x74, 0x18, 0x0b, 0x20, + 0x01, 0x28, 0x03, 0x52, 0x06, 0x66, 0x65, 0x65, 0x53, 0x61, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x66, + 0x65, 0x65, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x66, + 0x65, 0x65, 0x4d, 0x73, 0x61, 0x74, 0x12, 0x28, 0x0a, 0x10, 0x63, 0x72, 0x65, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x6e, 0x73, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x03, + 0x52, 0x0e, 0x63, 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x69, 0x6d, 0x65, 0x4e, 0x73, + 0x12, 0x28, 0x0a, 0x05, 0x68, 0x74, 0x6c, 0x63, 0x73, 0x18, 0x0e, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x12, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x48, 0x54, 0x4c, 0x43, 0x41, 0x74, 0x74, 0x65, + 0x6d, 0x70, 0x74, 0x52, 0x05, 0x68, 0x74, 0x6c, 0x63, 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x70, 0x61, + 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x0f, 0x20, 0x01, 0x28, + 0x04, 0x52, 0x0c, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, + 0x42, 0x0a, 0x0e, 0x66, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x5f, 0x72, 0x65, 0x61, 0x73, 0x6f, + 0x6e, 0x18, 0x10, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1b, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, + 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x46, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x52, 0x65, + 0x61, 0x73, 0x6f, 0x6e, 0x52, 0x0d, 0x66, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x52, 0x65, 0x61, + 0x73, 0x6f, 0x6e, 0x12, 0x62, 0x0a, 0x18, 0x66, 0x69, 0x72, 0x73, 0x74, 0x5f, 0x68, 0x6f, 0x70, + 0x5f, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5f, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x18, + 0x11, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x61, + 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x46, 0x69, 0x72, 0x73, 0x74, 0x48, 0x6f, 0x70, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, - 0x52, 0x0d, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x1a, - 0x40, 0x0a, 0x12, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, + 0x52, 0x15, 0x66, 0x69, 0x72, 0x73, 0x74, 0x48, 0x6f, 0x70, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, + 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x1a, 0x48, 0x0a, 0x1a, 0x46, 0x69, 0x72, 0x73, 0x74, + 0x48, 0x6f, 0x70, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, - 0x01, 0x22, 0x46, 0x0a, 0x13, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x47, 0x72, 0x61, 0x70, - 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2f, 0x0a, 0x13, 0x69, 0x6e, 0x63, 0x6c, - 0x75, 0x64, 0x65, 0x5f, 0x75, 0x6e, 0x61, 0x6e, 0x6e, 0x6f, 0x75, 0x6e, 0x63, 0x65, 0x64, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x12, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x55, 0x6e, - 0x61, 0x6e, 0x6e, 0x6f, 0x75, 0x6e, 0x63, 0x65, 0x64, 0x22, 0x64, 0x0a, 0x0c, 0x43, 0x68, 0x61, - 0x6e, 0x6e, 0x65, 0x6c, 0x47, 0x72, 0x61, 0x70, 0x68, 0x12, 0x2a, 0x0a, 0x05, 0x6e, 0x6f, 0x64, - 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, - 0x2e, 0x4c, 0x69, 0x67, 0x68, 0x74, 0x6e, 0x69, 0x6e, 0x67, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x05, - 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x12, 0x28, 0x0a, 0x05, 0x65, 0x64, 0x67, 0x65, 0x73, 0x18, 0x02, - 0x20, 0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, - 0x6e, 0x6e, 0x65, 0x6c, 0x45, 0x64, 0x67, 0x65, 0x52, 0x05, 0x65, 0x64, 0x67, 0x65, 0x73, 0x22, - 0x41, 0x0a, 0x12, 0x4e, 0x6f, 0x64, 0x65, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2b, 0x0a, 0x05, 0x74, 0x79, 0x70, 0x65, 0x73, 0x18, 0x01, - 0x20, 0x03, 0x28, 0x0e, 0x32, 0x15, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4e, 0x6f, 0x64, - 0x65, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x54, 0x79, 0x70, 0x65, 0x52, 0x05, 0x74, 0x79, 0x70, - 0x65, 0x73, 0x22, 0xe1, 0x01, 0x0a, 0x13, 0x4e, 0x6f, 0x64, 0x65, 0x4d, 0x65, 0x74, 0x72, 0x69, - 0x63, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x6c, 0x0a, 0x16, 0x62, 0x65, - 0x74, 0x77, 0x65, 0x65, 0x6e, 0x6e, 0x65, 0x73, 0x73, 0x5f, 0x63, 0x65, 0x6e, 0x74, 0x72, 0x61, - 0x6c, 0x69, 0x74, 0x79, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x35, 0x2e, 0x6c, 0x6e, 0x72, - 0x70, 0x63, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x42, 0x65, 0x74, 0x77, 0x65, 0x65, 0x6e, 0x6e, 0x65, - 0x73, 0x73, 0x43, 0x65, 0x6e, 0x74, 0x72, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x45, 0x6e, 0x74, 0x72, - 0x79, 0x52, 0x15, 0x62, 0x65, 0x74, 0x77, 0x65, 0x65, 0x6e, 0x6e, 0x65, 0x73, 0x73, 0x43, 0x65, - 0x6e, 0x74, 0x72, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x1a, 0x5c, 0x0a, 0x1a, 0x42, 0x65, 0x74, 0x77, - 0x65, 0x65, 0x6e, 0x6e, 0x65, 0x73, 0x73, 0x43, 0x65, 0x6e, 0x74, 0x72, 0x61, 0x6c, 0x69, 0x74, - 0x79, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x28, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, - 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, - 0x46, 0x6c, 0x6f, 0x61, 0x74, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x52, 0x05, 0x76, 0x61, 0x6c, - 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x4e, 0x0a, 0x0b, 0x46, 0x6c, 0x6f, 0x61, 0x74, 0x4d, - 0x65, 0x74, 0x72, 0x69, 0x63, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x01, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x29, 0x0a, 0x10, 0x6e, - 0x6f, 0x72, 0x6d, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x01, 0x52, 0x0f, 0x6e, 0x6f, 0x72, 0x6d, 0x61, 0x6c, 0x69, 0x7a, 0x65, - 0x64, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x4d, 0x0a, 0x0f, 0x43, 0x68, 0x61, 0x6e, 0x49, 0x6e, - 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x07, 0x63, 0x68, 0x61, - 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x42, 0x02, 0x30, 0x01, 0x52, 0x06, - 0x63, 0x68, 0x61, 0x6e, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x68, 0x61, 0x6e, 0x5f, 0x70, - 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x68, 0x61, 0x6e, - 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x22, 0x14, 0x0a, 0x12, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, - 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0xd5, 0x03, 0x0a, 0x0b, - 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x25, 0x0a, 0x0e, 0x67, - 0x72, 0x61, 0x70, 0x68, 0x5f, 0x64, 0x69, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x0d, 0x52, 0x0d, 0x67, 0x72, 0x61, 0x70, 0x68, 0x44, 0x69, 0x61, 0x6d, 0x65, 0x74, - 0x65, 0x72, 0x12, 0x24, 0x0a, 0x0e, 0x61, 0x76, 0x67, 0x5f, 0x6f, 0x75, 0x74, 0x5f, 0x64, 0x65, - 0x67, 0x72, 0x65, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x01, 0x52, 0x0c, 0x61, 0x76, 0x67, 0x4f, - 0x75, 0x74, 0x44, 0x65, 0x67, 0x72, 0x65, 0x65, 0x12, 0x24, 0x0a, 0x0e, 0x6d, 0x61, 0x78, 0x5f, - 0x6f, 0x75, 0x74, 0x5f, 0x64, 0x65, 0x67, 0x72, 0x65, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, - 0x52, 0x0c, 0x6d, 0x61, 0x78, 0x4f, 0x75, 0x74, 0x44, 0x65, 0x67, 0x72, 0x65, 0x65, 0x12, 0x1b, - 0x0a, 0x09, 0x6e, 0x75, 0x6d, 0x5f, 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, - 0x0d, 0x52, 0x08, 0x6e, 0x75, 0x6d, 0x4e, 0x6f, 0x64, 0x65, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x6e, - 0x75, 0x6d, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, - 0x0d, 0x52, 0x0b, 0x6e, 0x75, 0x6d, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x12, 0x34, - 0x0a, 0x16, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x5f, - 0x63, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, 0x52, 0x14, - 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x43, 0x61, 0x70, 0x61, - 0x63, 0x69, 0x74, 0x79, 0x12, 0x28, 0x0a, 0x10, 0x61, 0x76, 0x67, 0x5f, 0x63, 0x68, 0x61, 0x6e, - 0x6e, 0x65, 0x6c, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x01, 0x52, 0x0e, - 0x61, 0x76, 0x67, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x28, - 0x0a, 0x10, 0x6d, 0x69, 0x6e, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x5f, 0x73, 0x69, - 0x7a, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0e, 0x6d, 0x69, 0x6e, 0x43, 0x68, 0x61, - 0x6e, 0x6e, 0x65, 0x6c, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x28, 0x0a, 0x10, 0x6d, 0x61, 0x78, 0x5f, - 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x09, 0x20, 0x01, - 0x28, 0x03, 0x52, 0x0e, 0x6d, 0x61, 0x78, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x53, 0x69, - 0x7a, 0x65, 0x12, 0x35, 0x0a, 0x17, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x6e, 0x5f, 0x63, 0x68, 0x61, - 0x6e, 0x6e, 0x65, 0x6c, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x5f, 0x73, 0x61, 0x74, 0x18, 0x0a, 0x20, - 0x01, 0x28, 0x03, 0x52, 0x14, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x6e, - 0x65, 0x6c, 0x53, 0x69, 0x7a, 0x65, 0x53, 0x61, 0x74, 0x12, 0x28, 0x0a, 0x10, 0x6e, 0x75, 0x6d, - 0x5f, 0x7a, 0x6f, 0x6d, 0x62, 0x69, 0x65, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x73, 0x18, 0x0b, 0x20, - 0x01, 0x28, 0x04, 0x52, 0x0e, 0x6e, 0x75, 0x6d, 0x5a, 0x6f, 0x6d, 0x62, 0x69, 0x65, 0x43, 0x68, - 0x61, 0x6e, 0x73, 0x22, 0x0d, 0x0a, 0x0b, 0x53, 0x74, 0x6f, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x22, 0x0e, 0x0a, 0x0c, 0x53, 0x74, 0x6f, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x1b, 0x0a, 0x19, 0x47, 0x72, 0x61, 0x70, 0x68, 0x54, 0x6f, 0x70, 0x6f, 0x6c, - 0x6f, 0x67, 0x79, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x22, - 0xcd, 0x01, 0x0a, 0x13, 0x47, 0x72, 0x61, 0x70, 0x68, 0x54, 0x6f, 0x70, 0x6f, 0x6c, 0x6f, 0x67, - 0x79, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x34, 0x0a, 0x0c, 0x6e, 0x6f, 0x64, 0x65, 0x5f, - 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x11, 0x2e, - 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, - 0x52, 0x0b, 0x6e, 0x6f, 0x64, 0x65, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x12, 0x41, 0x0a, - 0x0f, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, - 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, - 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x45, 0x64, 0x67, 0x65, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, - 0x52, 0x0e, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, - 0x12, 0x3d, 0x0a, 0x0c, 0x63, 0x6c, 0x6f, 0x73, 0x65, 0x64, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x73, - 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, - 0x6c, 0x6f, 0x73, 0x65, 0x64, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x55, 0x70, 0x64, 0x61, - 0x74, 0x65, 0x52, 0x0b, 0x63, 0x6c, 0x6f, 0x73, 0x65, 0x64, 0x43, 0x68, 0x61, 0x6e, 0x73, 0x22, - 0xef, 0x02, 0x0a, 0x0a, 0x4e, 0x6f, 0x64, 0x65, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x20, - 0x0a, 0x09, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, - 0x09, 0x42, 0x02, 0x18, 0x01, 0x52, 0x09, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, - 0x12, 0x21, 0x0a, 0x0c, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x5f, 0x6b, 0x65, 0x79, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, - 0x4b, 0x65, 0x79, 0x12, 0x2b, 0x0a, 0x0f, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x5f, 0x66, 0x65, - 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x02, 0x18, 0x01, - 0x52, 0x0e, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, - 0x12, 0x14, 0x0a, 0x05, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x05, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x18, - 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x12, 0x39, 0x0a, 0x0e, - 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x18, 0x07, - 0x20, 0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4e, 0x6f, 0x64, - 0x65, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x0d, 0x6e, 0x6f, 0x64, 0x65, 0x41, 0x64, - 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x12, 0x3b, 0x0a, 0x08, 0x66, 0x65, 0x61, 0x74, 0x75, - 0x72, 0x65, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x6c, 0x6e, 0x72, 0x70, - 0x63, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x2e, 0x46, 0x65, 0x61, - 0x74, 0x75, 0x72, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x08, 0x66, 0x65, 0x61, 0x74, - 0x75, 0x72, 0x65, 0x73, 0x1a, 0x4b, 0x0a, 0x0d, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, - 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x0d, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x24, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x46, - 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, - 0x01, 0x22, 0x91, 0x02, 0x0a, 0x11, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x45, 0x64, 0x67, - 0x65, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x1b, 0x0a, 0x07, 0x63, 0x68, 0x61, 0x6e, 0x5f, - 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x42, 0x02, 0x30, 0x01, 0x52, 0x06, 0x63, 0x68, - 0x61, 0x6e, 0x49, 0x64, 0x12, 0x32, 0x0a, 0x0a, 0x63, 0x68, 0x61, 0x6e, 0x5f, 0x70, 0x6f, 0x69, - 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, - 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x09, 0x63, - 0x68, 0x61, 0x6e, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x63, 0x61, 0x70, 0x61, - 0x63, 0x69, 0x74, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x63, 0x61, 0x70, 0x61, - 0x63, 0x69, 0x74, 0x79, 0x12, 0x3b, 0x0a, 0x0e, 0x72, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x5f, - 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x6c, - 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x50, 0x6f, 0x6c, 0x69, - 0x63, 0x79, 0x52, 0x0d, 0x72, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x50, 0x6f, 0x6c, 0x69, 0x63, - 0x79, 0x12, 0x29, 0x0a, 0x10, 0x61, 0x64, 0x76, 0x65, 0x72, 0x74, 0x69, 0x73, 0x69, 0x6e, 0x67, - 0x5f, 0x6e, 0x6f, 0x64, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x61, 0x64, 0x76, - 0x65, 0x72, 0x74, 0x69, 0x73, 0x69, 0x6e, 0x67, 0x4e, 0x6f, 0x64, 0x65, 0x12, 0x27, 0x0a, 0x0f, - 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6e, 0x67, 0x5f, 0x6e, 0x6f, 0x64, 0x65, 0x18, - 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6e, - 0x67, 0x4e, 0x6f, 0x64, 0x65, 0x22, 0xa7, 0x01, 0x0a, 0x13, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x64, - 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x1b, 0x0a, - 0x07, 0x63, 0x68, 0x61, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x42, 0x02, - 0x30, 0x01, 0x52, 0x06, 0x63, 0x68, 0x61, 0x6e, 0x49, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x63, 0x61, - 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x63, 0x61, - 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x6c, 0x6f, 0x73, 0x65, 0x64, - 0x5f, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0c, 0x63, - 0x6c, 0x6f, 0x73, 0x65, 0x64, 0x48, 0x65, 0x69, 0x67, 0x68, 0x74, 0x12, 0x32, 0x0a, 0x0a, 0x63, - 0x68, 0x61, 0x6e, 0x5f, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x13, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x50, - 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x09, 0x63, 0x68, 0x61, 0x6e, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x22, - 0xcf, 0x01, 0x0a, 0x07, 0x48, 0x6f, 0x70, 0x48, 0x69, 0x6e, 0x74, 0x12, 0x17, 0x0a, 0x07, 0x6e, - 0x6f, 0x64, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6e, 0x6f, - 0x64, 0x65, 0x49, 0x64, 0x12, 0x1b, 0x0a, 0x07, 0x63, 0x68, 0x61, 0x6e, 0x5f, 0x69, 0x64, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x04, 0x42, 0x02, 0x30, 0x01, 0x52, 0x06, 0x63, 0x68, 0x61, 0x6e, 0x49, - 0x64, 0x12, 0x22, 0x0a, 0x0d, 0x66, 0x65, 0x65, 0x5f, 0x62, 0x61, 0x73, 0x65, 0x5f, 0x6d, 0x73, - 0x61, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x66, 0x65, 0x65, 0x42, 0x61, 0x73, - 0x65, 0x4d, 0x73, 0x61, 0x74, 0x12, 0x3e, 0x0a, 0x1b, 0x66, 0x65, 0x65, 0x5f, 0x70, 0x72, 0x6f, - 0x70, 0x6f, 0x72, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x5f, 0x6d, 0x69, 0x6c, 0x6c, 0x69, 0x6f, - 0x6e, 0x74, 0x68, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x19, 0x66, 0x65, 0x65, 0x50, - 0x72, 0x6f, 0x70, 0x6f, 0x72, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x4d, 0x69, 0x6c, 0x6c, 0x69, - 0x6f, 0x6e, 0x74, 0x68, 0x73, 0x12, 0x2a, 0x0a, 0x11, 0x63, 0x6c, 0x74, 0x76, 0x5f, 0x65, 0x78, - 0x70, 0x69, 0x72, 0x79, 0x5f, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, - 0x52, 0x0f, 0x63, 0x6c, 0x74, 0x76, 0x45, 0x78, 0x70, 0x69, 0x72, 0x79, 0x44, 0x65, 0x6c, 0x74, - 0x61, 0x22, 0x1e, 0x0a, 0x05, 0x53, 0x65, 0x74, 0x49, 0x44, 0x12, 0x15, 0x0a, 0x06, 0x73, 0x65, - 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x73, 0x65, 0x74, 0x49, - 0x64, 0x22, 0x38, 0x0a, 0x09, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x48, 0x69, 0x6e, 0x74, 0x12, 0x2b, - 0x0a, 0x09, 0x68, 0x6f, 0x70, 0x5f, 0x68, 0x69, 0x6e, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, - 0x0b, 0x32, 0x0e, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x48, 0x6f, 0x70, 0x48, 0x69, 0x6e, - 0x74, 0x52, 0x08, 0x68, 0x6f, 0x70, 0x48, 0x69, 0x6e, 0x74, 0x73, 0x22, 0xc4, 0x02, 0x0a, 0x12, - 0x42, 0x6c, 0x69, 0x6e, 0x64, 0x65, 0x64, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x50, 0x61, - 0x74, 0x68, 0x12, 0x35, 0x0a, 0x0c, 0x62, 0x6c, 0x69, 0x6e, 0x64, 0x65, 0x64, 0x5f, 0x70, 0x61, - 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, - 0x2e, 0x42, 0x6c, 0x69, 0x6e, 0x64, 0x65, 0x64, 0x50, 0x61, 0x74, 0x68, 0x52, 0x0b, 0x62, 0x6c, - 0x69, 0x6e, 0x64, 0x65, 0x64, 0x50, 0x61, 0x74, 0x68, 0x12, 0x22, 0x0a, 0x0d, 0x62, 0x61, 0x73, - 0x65, 0x5f, 0x66, 0x65, 0x65, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, - 0x52, 0x0b, 0x62, 0x61, 0x73, 0x65, 0x46, 0x65, 0x65, 0x4d, 0x73, 0x61, 0x74, 0x12, 0x32, 0x0a, - 0x15, 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x72, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x5f, 0x66, 0x65, - 0x65, 0x5f, 0x72, 0x61, 0x74, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x13, 0x70, 0x72, - 0x6f, 0x70, 0x6f, 0x72, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x46, 0x65, 0x65, 0x52, 0x61, 0x74, - 0x65, 0x12, 0x28, 0x0a, 0x10, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x63, 0x6c, 0x74, 0x76, 0x5f, - 0x64, 0x65, 0x6c, 0x74, 0x61, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0e, 0x74, 0x6f, 0x74, - 0x61, 0x6c, 0x43, 0x6c, 0x74, 0x76, 0x44, 0x65, 0x6c, 0x74, 0x61, 0x12, 0x22, 0x0a, 0x0d, 0x68, - 0x74, 0x6c, 0x63, 0x5f, 0x6d, 0x69, 0x6e, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x05, 0x20, 0x01, - 0x28, 0x04, 0x52, 0x0b, 0x68, 0x74, 0x6c, 0x63, 0x4d, 0x69, 0x6e, 0x4d, 0x73, 0x61, 0x74, 0x12, - 0x22, 0x0a, 0x0d, 0x68, 0x74, 0x6c, 0x63, 0x5f, 0x6d, 0x61, 0x78, 0x5f, 0x6d, 0x73, 0x61, 0x74, - 0x18, 0x06, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x68, 0x74, 0x6c, 0x63, 0x4d, 0x61, 0x78, 0x4d, - 0x73, 0x61, 0x74, 0x12, 0x2d, 0x0a, 0x08, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x18, - 0x07, 0x20, 0x03, 0x28, 0x0e, 0x32, 0x11, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x65, - 0x61, 0x74, 0x75, 0x72, 0x65, 0x42, 0x69, 0x74, 0x52, 0x08, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, - 0x65, 0x73, 0x22, 0x97, 0x01, 0x0a, 0x0b, 0x42, 0x6c, 0x69, 0x6e, 0x64, 0x65, 0x64, 0x50, 0x61, - 0x74, 0x68, 0x12, 0x2b, 0x0a, 0x11, 0x69, 0x6e, 0x74, 0x72, 0x6f, 0x64, 0x75, 0x63, 0x74, 0x69, - 0x6f, 0x6e, 0x5f, 0x6e, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x10, 0x69, - 0x6e, 0x74, 0x72, 0x6f, 0x64, 0x75, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x4e, 0x6f, 0x64, 0x65, 0x12, - 0x25, 0x0a, 0x0e, 0x62, 0x6c, 0x69, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x70, 0x6f, 0x69, 0x6e, - 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0d, 0x62, 0x6c, 0x69, 0x6e, 0x64, 0x69, 0x6e, - 0x67, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x34, 0x0a, 0x0c, 0x62, 0x6c, 0x69, 0x6e, 0x64, 0x65, - 0x64, 0x5f, 0x68, 0x6f, 0x70, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x6c, - 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x42, 0x6c, 0x69, 0x6e, 0x64, 0x65, 0x64, 0x48, 0x6f, 0x70, 0x52, - 0x0b, 0x62, 0x6c, 0x69, 0x6e, 0x64, 0x65, 0x64, 0x48, 0x6f, 0x70, 0x73, 0x22, 0x56, 0x0a, 0x0a, - 0x42, 0x6c, 0x69, 0x6e, 0x64, 0x65, 0x64, 0x48, 0x6f, 0x70, 0x12, 0x21, 0x0a, 0x0c, 0x62, 0x6c, - 0x69, 0x6e, 0x64, 0x65, 0x64, 0x5f, 0x6e, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, - 0x52, 0x0b, 0x62, 0x6c, 0x69, 0x6e, 0x64, 0x65, 0x64, 0x4e, 0x6f, 0x64, 0x65, 0x12, 0x25, 0x0a, - 0x0e, 0x65, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0d, 0x65, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, - 0x44, 0x61, 0x74, 0x61, 0x22, 0xa8, 0x01, 0x0a, 0x0f, 0x41, 0x4d, 0x50, 0x49, 0x6e, 0x76, 0x6f, - 0x69, 0x63, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x2d, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, - 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x17, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, - 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x48, 0x54, 0x4c, 0x43, 0x53, 0x74, 0x61, 0x74, 0x65, - 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x73, 0x65, 0x74, 0x74, 0x6c, - 0x65, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x73, - 0x65, 0x74, 0x74, 0x6c, 0x65, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x65, - 0x74, 0x74, 0x6c, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, - 0x0a, 0x73, 0x65, 0x74, 0x74, 0x6c, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x22, 0x0a, 0x0d, 0x61, - 0x6d, 0x74, 0x5f, 0x70, 0x61, 0x69, 0x64, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x05, 0x20, 0x01, - 0x28, 0x03, 0x52, 0x0b, 0x61, 0x6d, 0x74, 0x50, 0x61, 0x69, 0x64, 0x4d, 0x73, 0x61, 0x74, 0x22, - 0xac, 0x0a, 0x0a, 0x07, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6d, - 0x65, 0x6d, 0x6f, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6d, 0x65, 0x6d, 0x6f, 0x12, - 0x1d, 0x0a, 0x0a, 0x72, 0x5f, 0x70, 0x72, 0x65, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x0c, 0x52, 0x09, 0x72, 0x50, 0x72, 0x65, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x12, 0x15, - 0x0a, 0x06, 0x72, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, - 0x72, 0x48, 0x61, 0x73, 0x68, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x05, - 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x76, - 0x61, 0x6c, 0x75, 0x65, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x17, 0x20, 0x01, 0x28, 0x03, 0x52, - 0x09, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x4d, 0x73, 0x61, 0x74, 0x12, 0x1c, 0x0a, 0x07, 0x73, 0x65, - 0x74, 0x74, 0x6c, 0x65, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x42, 0x02, 0x18, 0x01, 0x52, - 0x07, 0x73, 0x65, 0x74, 0x74, 0x6c, 0x65, 0x64, 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x72, 0x65, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x64, 0x61, 0x74, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x03, 0x52, - 0x0c, 0x63, 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x61, 0x74, 0x65, 0x12, 0x1f, 0x0a, - 0x0b, 0x73, 0x65, 0x74, 0x74, 0x6c, 0x65, 0x5f, 0x64, 0x61, 0x74, 0x65, 0x18, 0x08, 0x20, 0x01, - 0x28, 0x03, 0x52, 0x0a, 0x73, 0x65, 0x74, 0x74, 0x6c, 0x65, 0x44, 0x61, 0x74, 0x65, 0x12, 0x27, - 0x0a, 0x0f, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x29, 0x0a, 0x10, 0x64, 0x65, 0x73, 0x63, 0x72, - 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x0a, 0x20, 0x01, 0x28, - 0x0c, 0x52, 0x0f, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x61, - 0x73, 0x68, 0x12, 0x16, 0x0a, 0x06, 0x65, 0x78, 0x70, 0x69, 0x72, 0x79, 0x18, 0x0b, 0x20, 0x01, - 0x28, 0x03, 0x52, 0x06, 0x65, 0x78, 0x70, 0x69, 0x72, 0x79, 0x12, 0x23, 0x0a, 0x0d, 0x66, 0x61, - 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x18, 0x0c, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x0c, 0x66, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x41, 0x64, 0x64, 0x72, 0x12, - 0x1f, 0x0a, 0x0b, 0x63, 0x6c, 0x74, 0x76, 0x5f, 0x65, 0x78, 0x70, 0x69, 0x72, 0x79, 0x18, 0x0d, - 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x63, 0x6c, 0x74, 0x76, 0x45, 0x78, 0x70, 0x69, 0x72, 0x79, - 0x12, 0x31, 0x0a, 0x0b, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x5f, 0x68, 0x69, 0x6e, 0x74, 0x73, 0x18, - 0x0e, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x6f, - 0x75, 0x74, 0x65, 0x48, 0x69, 0x6e, 0x74, 0x52, 0x0a, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x48, 0x69, - 0x6e, 0x74, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x18, 0x0f, - 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x12, 0x1b, 0x0a, - 0x09, 0x61, 0x64, 0x64, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x10, 0x20, 0x01, 0x28, 0x04, - 0x52, 0x08, 0x61, 0x64, 0x64, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x21, 0x0a, 0x0c, 0x73, 0x65, - 0x74, 0x74, 0x6c, 0x65, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x11, 0x20, 0x01, 0x28, 0x04, - 0x52, 0x0b, 0x73, 0x65, 0x74, 0x74, 0x6c, 0x65, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x1d, 0x0a, - 0x08, 0x61, 0x6d, 0x74, 0x5f, 0x70, 0x61, 0x69, 0x64, 0x18, 0x12, 0x20, 0x01, 0x28, 0x03, 0x42, - 0x02, 0x18, 0x01, 0x52, 0x07, 0x61, 0x6d, 0x74, 0x50, 0x61, 0x69, 0x64, 0x12, 0x20, 0x0a, 0x0c, - 0x61, 0x6d, 0x74, 0x5f, 0x70, 0x61, 0x69, 0x64, 0x5f, 0x73, 0x61, 0x74, 0x18, 0x13, 0x20, 0x01, - 0x28, 0x03, 0x52, 0x0a, 0x61, 0x6d, 0x74, 0x50, 0x61, 0x69, 0x64, 0x53, 0x61, 0x74, 0x12, 0x22, - 0x0a, 0x0d, 0x61, 0x6d, 0x74, 0x5f, 0x70, 0x61, 0x69, 0x64, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, - 0x14, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b, 0x61, 0x6d, 0x74, 0x50, 0x61, 0x69, 0x64, 0x4d, 0x73, - 0x61, 0x74, 0x12, 0x31, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x15, 0x20, 0x01, 0x28, - 0x0e, 0x32, 0x1b, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, - 0x65, 0x2e, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x05, - 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x28, 0x0a, 0x05, 0x68, 0x74, 0x6c, 0x63, 0x73, 0x18, 0x16, - 0x20, 0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x49, 0x6e, 0x76, - 0x6f, 0x69, 0x63, 0x65, 0x48, 0x54, 0x4c, 0x43, 0x52, 0x05, 0x68, 0x74, 0x6c, 0x63, 0x73, 0x12, - 0x38, 0x0a, 0x08, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x18, 0x18, 0x20, 0x03, 0x28, - 0x0b, 0x32, 0x1c, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, - 0x65, 0x2e, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, - 0x08, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x69, 0x73, 0x5f, - 0x6b, 0x65, 0x79, 0x73, 0x65, 0x6e, 0x64, 0x18, 0x19, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x69, - 0x73, 0x4b, 0x65, 0x79, 0x73, 0x65, 0x6e, 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x61, 0x79, 0x6d, - 0x65, 0x6e, 0x74, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x18, 0x1a, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, - 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x41, 0x64, 0x64, 0x72, 0x12, 0x15, 0x0a, 0x06, 0x69, - 0x73, 0x5f, 0x61, 0x6d, 0x70, 0x18, 0x1b, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x69, 0x73, 0x41, - 0x6d, 0x70, 0x12, 0x4f, 0x0a, 0x11, 0x61, 0x6d, 0x70, 0x5f, 0x69, 0x6e, 0x76, 0x6f, 0x69, 0x63, - 0x65, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x1c, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x23, 0x2e, - 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x2e, 0x41, 0x6d, - 0x70, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x45, 0x6e, 0x74, - 0x72, 0x79, 0x52, 0x0f, 0x61, 0x6d, 0x70, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x53, 0x74, - 0x61, 0x74, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x69, 0x73, 0x5f, 0x62, 0x6c, 0x69, 0x6e, 0x64, 0x65, - 0x64, 0x18, 0x1d, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x69, 0x73, 0x42, 0x6c, 0x69, 0x6e, 0x64, - 0x65, 0x64, 0x12, 0x48, 0x0a, 0x13, 0x62, 0x6c, 0x69, 0x6e, 0x64, 0x65, 0x64, 0x5f, 0x70, 0x61, - 0x74, 0x68, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x18, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x42, 0x6c, 0x69, 0x6e, 0x64, 0x65, 0x64, 0x50, - 0x61, 0x74, 0x68, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x11, 0x62, 0x6c, 0x69, 0x6e, 0x64, - 0x65, 0x64, 0x50, 0x61, 0x74, 0x68, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x1a, 0x4b, 0x0a, 0x0d, - 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, - 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, - 0x24, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, - 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x52, 0x05, - 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x5a, 0x0a, 0x14, 0x41, 0x6d, 0x70, - 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x45, 0x6e, 0x74, 0x72, - 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, - 0x6b, 0x65, 0x79, 0x12, 0x2c, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x4d, 0x50, 0x49, 0x6e, - 0x76, 0x6f, 0x69, 0x63, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, - 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x41, 0x0a, 0x0c, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, - 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x08, 0x0a, 0x04, 0x4f, 0x50, 0x45, 0x4e, 0x10, 0x00, 0x12, - 0x0b, 0x0a, 0x07, 0x53, 0x45, 0x54, 0x54, 0x4c, 0x45, 0x44, 0x10, 0x01, 0x12, 0x0c, 0x0a, 0x08, - 0x43, 0x41, 0x4e, 0x43, 0x45, 0x4c, 0x45, 0x44, 0x10, 0x02, 0x12, 0x0c, 0x0a, 0x08, 0x41, 0x43, - 0x43, 0x45, 0x50, 0x54, 0x45, 0x44, 0x10, 0x03, 0x4a, 0x04, 0x08, 0x02, 0x10, 0x03, 0x22, 0xef, - 0x01, 0x0a, 0x11, 0x42, 0x6c, 0x69, 0x6e, 0x64, 0x65, 0x64, 0x50, 0x61, 0x74, 0x68, 0x43, 0x6f, - 0x6e, 0x66, 0x69, 0x67, 0x12, 0x2e, 0x0a, 0x11, 0x6d, 0x69, 0x6e, 0x5f, 0x6e, 0x75, 0x6d, 0x5f, - 0x72, 0x65, 0x61, 0x6c, 0x5f, 0x68, 0x6f, 0x70, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x48, - 0x00, 0x52, 0x0e, 0x6d, 0x69, 0x6e, 0x4e, 0x75, 0x6d, 0x52, 0x65, 0x61, 0x6c, 0x48, 0x6f, 0x70, - 0x73, 0x88, 0x01, 0x01, 0x12, 0x1e, 0x0a, 0x08, 0x6e, 0x75, 0x6d, 0x5f, 0x68, 0x6f, 0x70, 0x73, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x48, 0x01, 0x52, 0x07, 0x6e, 0x75, 0x6d, 0x48, 0x6f, 0x70, - 0x73, 0x88, 0x01, 0x01, 0x12, 0x27, 0x0a, 0x0d, 0x6d, 0x61, 0x78, 0x5f, 0x6e, 0x75, 0x6d, 0x5f, - 0x70, 0x61, 0x74, 0x68, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x48, 0x02, 0x52, 0x0b, 0x6d, - 0x61, 0x78, 0x4e, 0x75, 0x6d, 0x50, 0x61, 0x74, 0x68, 0x73, 0x88, 0x01, 0x01, 0x12, 0x2c, 0x0a, - 0x12, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x6f, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x6c, - 0x69, 0x73, 0x74, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x10, 0x6e, 0x6f, 0x64, 0x65, 0x4f, - 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x4c, 0x69, 0x73, 0x74, 0x42, 0x14, 0x0a, 0x12, 0x5f, - 0x6d, 0x69, 0x6e, 0x5f, 0x6e, 0x75, 0x6d, 0x5f, 0x72, 0x65, 0x61, 0x6c, 0x5f, 0x68, 0x6f, 0x70, - 0x73, 0x42, 0x0b, 0x0a, 0x09, 0x5f, 0x6e, 0x75, 0x6d, 0x5f, 0x68, 0x6f, 0x70, 0x73, 0x42, 0x10, - 0x0a, 0x0e, 0x5f, 0x6d, 0x61, 0x78, 0x5f, 0x6e, 0x75, 0x6d, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x73, - 0x22, 0xfc, 0x03, 0x0a, 0x0b, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x48, 0x54, 0x4c, 0x43, - 0x12, 0x1b, 0x0a, 0x07, 0x63, 0x68, 0x61, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x04, 0x42, 0x02, 0x30, 0x01, 0x52, 0x06, 0x63, 0x68, 0x61, 0x6e, 0x49, 0x64, 0x12, 0x1d, 0x0a, - 0x0a, 0x68, 0x74, 0x6c, 0x63, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x04, 0x52, 0x09, 0x68, 0x74, 0x6c, 0x63, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x19, 0x0a, 0x08, - 0x61, 0x6d, 0x74, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, - 0x61, 0x6d, 0x74, 0x4d, 0x73, 0x61, 0x74, 0x12, 0x23, 0x0a, 0x0d, 0x61, 0x63, 0x63, 0x65, 0x70, - 0x74, 0x5f, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0c, - 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x48, 0x65, 0x69, 0x67, 0x68, 0x74, 0x12, 0x1f, 0x0a, 0x0b, - 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, - 0x03, 0x52, 0x0a, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x21, 0x0a, - 0x0c, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x06, 0x20, - 0x01, 0x28, 0x03, 0x52, 0x0b, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x54, 0x69, 0x6d, 0x65, - 0x12, 0x23, 0x0a, 0x0d, 0x65, 0x78, 0x70, 0x69, 0x72, 0x79, 0x5f, 0x68, 0x65, 0x69, 0x67, 0x68, - 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0c, 0x65, 0x78, 0x70, 0x69, 0x72, 0x79, 0x48, - 0x65, 0x69, 0x67, 0x68, 0x74, 0x12, 0x2d, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x08, - 0x20, 0x01, 0x28, 0x0e, 0x32, 0x17, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x49, 0x6e, 0x76, - 0x6f, 0x69, 0x63, 0x65, 0x48, 0x54, 0x4c, 0x43, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x05, 0x73, - 0x74, 0x61, 0x74, 0x65, 0x12, 0x4c, 0x0a, 0x0e, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5f, 0x72, - 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x18, 0x09, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x6c, - 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x48, 0x54, 0x4c, 0x43, - 0x2e, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x45, 0x6e, - 0x74, 0x72, 0x79, 0x52, 0x0d, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x52, 0x65, 0x63, 0x6f, 0x72, - 0x64, 0x73, 0x12, 0x2b, 0x0a, 0x12, 0x6d, 0x70, 0x70, 0x5f, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, - 0x61, 0x6d, 0x74, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0f, - 0x6d, 0x70, 0x70, 0x54, 0x6f, 0x74, 0x61, 0x6c, 0x41, 0x6d, 0x74, 0x4d, 0x73, 0x61, 0x74, 0x12, - 0x1c, 0x0a, 0x03, 0x61, 0x6d, 0x70, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0a, 0x2e, 0x6c, - 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x4d, 0x50, 0x52, 0x03, 0x61, 0x6d, 0x70, 0x1a, 0x40, 0x0a, - 0x12, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x45, 0x6e, - 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, - 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, - 0x8c, 0x01, 0x0a, 0x03, 0x41, 0x4d, 0x50, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x6f, 0x6f, 0x74, 0x5f, - 0x73, 0x68, 0x61, 0x72, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x72, 0x6f, 0x6f, - 0x74, 0x53, 0x68, 0x61, 0x72, 0x65, 0x12, 0x15, 0x0a, 0x06, 0x73, 0x65, 0x74, 0x5f, 0x69, 0x64, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x73, 0x65, 0x74, 0x49, 0x64, 0x12, 0x1f, 0x0a, - 0x0b, 0x63, 0x68, 0x69, 0x6c, 0x64, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x0d, 0x52, 0x0a, 0x63, 0x68, 0x69, 0x6c, 0x64, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x12, - 0x0a, 0x04, 0x68, 0x61, 0x73, 0x68, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x68, 0x61, - 0x73, 0x68, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x72, 0x65, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x18, 0x05, - 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x70, 0x72, 0x65, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x22, 0x94, - 0x01, 0x0a, 0x12, 0x41, 0x64, 0x64, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x15, 0x0a, 0x06, 0x72, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x72, 0x48, 0x61, 0x73, 0x68, 0x12, 0x27, 0x0a, 0x0f, - 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x61, 0x64, 0x64, 0x5f, 0x69, 0x6e, 0x64, - 0x65, 0x78, 0x18, 0x10, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x61, 0x64, 0x64, 0x49, 0x6e, 0x64, - 0x65, 0x78, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x61, 0x64, - 0x64, 0x72, 0x18, 0x11, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, - 0x74, 0x41, 0x64, 0x64, 0x72, 0x22, 0x46, 0x0a, 0x0b, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, - 0x48, 0x61, 0x73, 0x68, 0x12, 0x20, 0x0a, 0x0a, 0x72, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x5f, 0x73, - 0x74, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x02, 0x18, 0x01, 0x52, 0x08, 0x72, 0x48, - 0x61, 0x73, 0x68, 0x53, 0x74, 0x72, 0x12, 0x15, 0x0a, 0x06, 0x72, 0x5f, 0x68, 0x61, 0x73, 0x68, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x72, 0x48, 0x61, 0x73, 0x68, 0x22, 0xfc, 0x01, - 0x0a, 0x12, 0x4c, 0x69, 0x73, 0x74, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x5f, - 0x6f, 0x6e, 0x6c, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x70, 0x65, 0x6e, 0x64, - 0x69, 0x6e, 0x67, 0x4f, 0x6e, 0x6c, 0x79, 0x12, 0x21, 0x0a, 0x0c, 0x69, 0x6e, 0x64, 0x65, 0x78, - 0x5f, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x69, - 0x6e, 0x64, 0x65, 0x78, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x12, 0x28, 0x0a, 0x10, 0x6e, 0x75, - 0x6d, 0x5f, 0x6d, 0x61, 0x78, 0x5f, 0x69, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x73, 0x18, 0x05, - 0x20, 0x01, 0x28, 0x04, 0x52, 0x0e, 0x6e, 0x75, 0x6d, 0x4d, 0x61, 0x78, 0x49, 0x6e, 0x76, 0x6f, - 0x69, 0x63, 0x65, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x76, 0x65, 0x72, 0x73, 0x65, 0x64, - 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x72, 0x65, 0x76, 0x65, 0x72, 0x73, 0x65, 0x64, - 0x12, 0x2e, 0x0a, 0x13, 0x63, 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x64, 0x61, 0x74, - 0x65, 0x5f, 0x73, 0x74, 0x61, 0x72, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x04, 0x52, 0x11, 0x63, - 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x61, 0x74, 0x65, 0x53, 0x74, 0x61, 0x72, 0x74, - 0x12, 0x2a, 0x0a, 0x11, 0x63, 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x64, 0x61, 0x74, - 0x65, 0x5f, 0x65, 0x6e, 0x64, 0x18, 0x08, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0f, 0x63, 0x72, 0x65, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x61, 0x74, 0x65, 0x45, 0x6e, 0x64, 0x22, 0x9b, 0x01, 0x0a, - 0x13, 0x4c, 0x69, 0x73, 0x74, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2a, 0x0a, 0x08, 0x69, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x73, - 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x49, - 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x52, 0x08, 0x69, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x73, - 0x12, 0x2a, 0x0a, 0x11, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x5f, 0x6f, - 0x66, 0x66, 0x73, 0x65, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0f, 0x6c, 0x61, 0x73, - 0x74, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x12, 0x2c, 0x0a, 0x12, - 0x66, 0x69, 0x72, 0x73, 0x74, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x5f, 0x6f, 0x66, 0x66, 0x73, - 0x65, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x10, 0x66, 0x69, 0x72, 0x73, 0x74, 0x49, - 0x6e, 0x64, 0x65, 0x78, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x55, 0x0a, 0x13, 0x49, 0x6e, - 0x76, 0x6f, 0x69, 0x63, 0x65, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, - 0x6e, 0x12, 0x1b, 0x0a, 0x09, 0x61, 0x64, 0x64, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x61, 0x64, 0x64, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x21, - 0x0a, 0x0c, 0x73, 0x65, 0x74, 0x74, 0x6c, 0x65, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x73, 0x65, 0x74, 0x74, 0x6c, 0x65, 0x49, 0x6e, 0x64, 0x65, - 0x78, 0x22, 0xcb, 0x06, 0x0a, 0x07, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x21, 0x0a, - 0x0c, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x0b, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x48, 0x61, 0x73, 0x68, - 0x12, 0x18, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x42, - 0x02, 0x18, 0x01, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x27, 0x0a, 0x0d, 0x63, 0x72, - 0x65, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x64, 0x61, 0x74, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x03, 0x42, 0x02, 0x18, 0x01, 0x52, 0x0c, 0x63, 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x44, - 0x61, 0x74, 0x65, 0x12, 0x14, 0x0a, 0x03, 0x66, 0x65, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, - 0x42, 0x02, 0x18, 0x01, 0x52, 0x03, 0x66, 0x65, 0x65, 0x12, 0x29, 0x0a, 0x10, 0x70, 0x61, 0x79, - 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x70, 0x72, 0x65, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x18, 0x06, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x0f, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x50, 0x72, 0x65, 0x69, - 0x6d, 0x61, 0x67, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x5f, 0x73, 0x61, - 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x53, 0x61, - 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, - 0x08, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x4d, 0x73, 0x61, 0x74, - 0x12, 0x27, 0x0a, 0x0f, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x72, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x70, 0x61, 0x79, 0x6d, 0x65, - 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x34, 0x0a, 0x06, 0x73, 0x74, 0x61, - 0x74, 0x75, 0x73, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1c, 0x2e, 0x6c, 0x6e, 0x72, 0x70, - 0x63, 0x2e, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, - 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, - 0x17, 0x0a, 0x07, 0x66, 0x65, 0x65, 0x5f, 0x73, 0x61, 0x74, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x03, - 0x52, 0x06, 0x66, 0x65, 0x65, 0x53, 0x61, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x66, 0x65, 0x65, 0x5f, - 0x6d, 0x73, 0x61, 0x74, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x66, 0x65, 0x65, 0x4d, - 0x73, 0x61, 0x74, 0x12, 0x28, 0x0a, 0x10, 0x63, 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, - 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x6e, 0x73, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0e, 0x63, - 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x69, 0x6d, 0x65, 0x4e, 0x73, 0x12, 0x28, 0x0a, - 0x05, 0x68, 0x74, 0x6c, 0x63, 0x73, 0x18, 0x0e, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x6c, - 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x48, 0x54, 0x4c, 0x43, 0x41, 0x74, 0x74, 0x65, 0x6d, 0x70, 0x74, - 0x52, 0x05, 0x68, 0x74, 0x6c, 0x63, 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x70, 0x61, 0x79, 0x6d, 0x65, - 0x6e, 0x74, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0c, - 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x42, 0x0a, 0x0e, - 0x66, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x5f, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, 0x10, - 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1b, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x61, 0x79, - 0x6d, 0x65, 0x6e, 0x74, 0x46, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x52, 0x65, 0x61, 0x73, 0x6f, - 0x6e, 0x52, 0x0d, 0x66, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x52, 0x65, 0x61, 0x73, 0x6f, 0x6e, - 0x12, 0x62, 0x0a, 0x18, 0x66, 0x69, 0x72, 0x73, 0x74, 0x5f, 0x68, 0x6f, 0x70, 0x5f, 0x63, 0x75, - 0x73, 0x74, 0x6f, 0x6d, 0x5f, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x18, 0x11, 0x20, 0x03, - 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x61, 0x79, 0x6d, 0x65, - 0x6e, 0x74, 0x2e, 0x46, 0x69, 0x72, 0x73, 0x74, 0x48, 0x6f, 0x70, 0x43, 0x75, 0x73, 0x74, 0x6f, - 0x6d, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x15, 0x66, - 0x69, 0x72, 0x73, 0x74, 0x48, 0x6f, 0x70, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x52, 0x65, 0x63, - 0x6f, 0x72, 0x64, 0x73, 0x1a, 0x48, 0x0a, 0x1a, 0x46, 0x69, 0x72, 0x73, 0x74, 0x48, 0x6f, 0x70, - 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x45, 0x6e, 0x74, - 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, - 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x0c, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x59, - 0x0a, 0x0d, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, - 0x0f, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x1a, 0x02, 0x08, 0x01, - 0x12, 0x0d, 0x0a, 0x09, 0x49, 0x4e, 0x5f, 0x46, 0x4c, 0x49, 0x47, 0x48, 0x54, 0x10, 0x01, 0x12, - 0x0d, 0x0a, 0x09, 0x53, 0x55, 0x43, 0x43, 0x45, 0x45, 0x44, 0x45, 0x44, 0x10, 0x02, 0x12, 0x0a, - 0x0a, 0x06, 0x46, 0x41, 0x49, 0x4c, 0x45, 0x44, 0x10, 0x03, 0x12, 0x0d, 0x0a, 0x09, 0x49, 0x4e, - 0x49, 0x54, 0x49, 0x41, 0x54, 0x45, 0x44, 0x10, 0x04, 0x4a, 0x04, 0x08, 0x04, 0x10, 0x05, 0x22, - 0xd5, 0x02, 0x0a, 0x0b, 0x48, 0x54, 0x4c, 0x43, 0x41, 0x74, 0x74, 0x65, 0x6d, 0x70, 0x74, 0x12, - 0x1d, 0x0a, 0x0a, 0x61, 0x74, 0x74, 0x65, 0x6d, 0x70, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x07, 0x20, - 0x01, 0x28, 0x04, 0x52, 0x09, 0x61, 0x74, 0x74, 0x65, 0x6d, 0x70, 0x74, 0x49, 0x64, 0x12, 0x35, - 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1d, - 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x48, 0x54, 0x4c, 0x43, 0x41, 0x74, 0x74, 0x65, 0x6d, - 0x70, 0x74, 0x2e, 0x48, 0x54, 0x4c, 0x43, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, - 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x22, 0x0a, 0x05, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x6f, 0x75, - 0x74, 0x65, 0x52, 0x05, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x12, 0x26, 0x0a, 0x0f, 0x61, 0x74, 0x74, - 0x65, 0x6d, 0x70, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x6e, 0x73, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x03, 0x52, 0x0d, 0x61, 0x74, 0x74, 0x65, 0x6d, 0x70, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x4e, - 0x73, 0x12, 0x26, 0x0a, 0x0f, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x5f, 0x74, 0x69, 0x6d, - 0x65, 0x5f, 0x6e, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0d, 0x72, 0x65, 0x73, 0x6f, - 0x6c, 0x76, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x4e, 0x73, 0x12, 0x28, 0x0a, 0x07, 0x66, 0x61, 0x69, - 0x6c, 0x75, 0x72, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x6c, 0x6e, 0x72, - 0x70, 0x63, 0x2e, 0x46, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x52, 0x07, 0x66, 0x61, 0x69, 0x6c, - 0x75, 0x72, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x72, 0x65, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x18, - 0x06, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x70, 0x72, 0x65, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x22, - 0x36, 0x0a, 0x0a, 0x48, 0x54, 0x4c, 0x43, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x0d, 0x0a, - 0x09, 0x49, 0x4e, 0x5f, 0x46, 0x4c, 0x49, 0x47, 0x48, 0x54, 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, - 0x53, 0x55, 0x43, 0x43, 0x45, 0x45, 0x44, 0x45, 0x44, 0x10, 0x01, 0x12, 0x0a, 0x0a, 0x06, 0x46, - 0x41, 0x49, 0x4c, 0x45, 0x44, 0x10, 0x02, 0x22, 0xb4, 0x02, 0x0a, 0x13, 0x4c, 0x69, 0x73, 0x74, - 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, - 0x2d, 0x0a, 0x12, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x5f, 0x69, 0x6e, 0x63, 0x6f, 0x6d, - 0x70, 0x6c, 0x65, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x11, 0x69, 0x6e, 0x63, - 0x6c, 0x75, 0x64, 0x65, 0x49, 0x6e, 0x63, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x12, 0x21, - 0x0a, 0x0c, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x5f, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x4f, 0x66, 0x66, 0x73, 0x65, - 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x6d, 0x61, 0x78, 0x5f, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, - 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x6d, 0x61, 0x78, 0x50, 0x61, 0x79, 0x6d, - 0x65, 0x6e, 0x74, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x76, 0x65, 0x72, 0x73, 0x65, 0x64, - 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x72, 0x65, 0x76, 0x65, 0x72, 0x73, 0x65, 0x64, - 0x12, 0x30, 0x0a, 0x14, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, - 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x12, - 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x54, 0x6f, 0x74, 0x61, 0x6c, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, - 0x74, 0x73, 0x12, 0x2e, 0x0a, 0x13, 0x63, 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x64, - 0x61, 0x74, 0x65, 0x5f, 0x73, 0x74, 0x61, 0x72, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x04, 0x52, - 0x11, 0x63, 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x61, 0x74, 0x65, 0x53, 0x74, 0x61, - 0x72, 0x74, 0x12, 0x2a, 0x0a, 0x11, 0x63, 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x64, - 0x61, 0x74, 0x65, 0x5f, 0x65, 0x6e, 0x64, 0x18, 0x07, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0f, 0x63, - 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x61, 0x74, 0x65, 0x45, 0x6e, 0x64, 0x22, 0xca, - 0x01, 0x0a, 0x14, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2a, 0x0a, 0x08, 0x70, 0x61, 0x79, 0x6d, 0x65, - 0x6e, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x6c, 0x6e, 0x72, 0x70, - 0x63, 0x2e, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x08, 0x70, 0x61, 0x79, 0x6d, 0x65, - 0x6e, 0x74, 0x73, 0x12, 0x2c, 0x0a, 0x12, 0x66, 0x69, 0x72, 0x73, 0x74, 0x5f, 0x69, 0x6e, 0x64, - 0x65, 0x78, 0x5f, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, - 0x10, 0x66, 0x69, 0x72, 0x73, 0x74, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x4f, 0x66, 0x66, 0x73, 0x65, - 0x74, 0x12, 0x2a, 0x0a, 0x11, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x5f, - 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0f, 0x6c, 0x61, - 0x73, 0x74, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x12, 0x2c, 0x0a, - 0x12, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x6e, 0x75, 0x6d, 0x5f, 0x70, 0x61, 0x79, 0x6d, 0x65, - 0x6e, 0x74, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x10, 0x74, 0x6f, 0x74, 0x61, 0x6c, - 0x4e, 0x75, 0x6d, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x22, 0x65, 0x0a, 0x14, 0x44, - 0x65, 0x6c, 0x65, 0x74, 0x65, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x68, - 0x61, 0x73, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x70, 0x61, 0x79, 0x6d, 0x65, - 0x6e, 0x74, 0x48, 0x61, 0x73, 0x68, 0x12, 0x2a, 0x0a, 0x11, 0x66, 0x61, 0x69, 0x6c, 0x65, 0x64, - 0x5f, 0x68, 0x74, 0x6c, 0x63, 0x73, 0x5f, 0x6f, 0x6e, 0x6c, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x08, 0x52, 0x0f, 0x66, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x48, 0x74, 0x6c, 0x63, 0x73, 0x4f, 0x6e, - 0x6c, 0x79, 0x22, 0x9b, 0x01, 0x0a, 0x18, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x41, 0x6c, 0x6c, - 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, - 0x30, 0x0a, 0x14, 0x66, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x5f, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, - 0x74, 0x73, 0x5f, 0x6f, 0x6e, 0x6c, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x12, 0x66, - 0x61, 0x69, 0x6c, 0x65, 0x64, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x4f, 0x6e, 0x6c, - 0x79, 0x12, 0x2a, 0x0a, 0x11, 0x66, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x5f, 0x68, 0x74, 0x6c, 0x63, - 0x73, 0x5f, 0x6f, 0x6e, 0x6c, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, 0x66, 0x61, - 0x69, 0x6c, 0x65, 0x64, 0x48, 0x74, 0x6c, 0x63, 0x73, 0x4f, 0x6e, 0x6c, 0x79, 0x12, 0x21, 0x0a, - 0x0c, 0x61, 0x6c, 0x6c, 0x5f, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x08, 0x52, 0x0b, 0x61, 0x6c, 0x6c, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x73, - 0x22, 0x17, 0x0a, 0x15, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, - 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x0a, 0x19, 0x44, 0x65, 0x6c, - 0x65, 0x74, 0x65, 0x41, 0x6c, 0x6c, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xbf, 0x01, 0x0a, 0x15, 0x41, 0x62, 0x61, 0x6e, 0x64, - 0x6f, 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x12, 0x38, 0x0a, 0x0d, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x5f, 0x70, 0x6f, 0x69, 0x6e, - 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, - 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x0c, 0x63, 0x68, - 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x39, 0x0a, 0x19, 0x70, 0x65, - 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x66, 0x75, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x73, 0x68, - 0x69, 0x6d, 0x5f, 0x6f, 0x6e, 0x6c, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x16, 0x70, - 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x46, 0x75, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x53, 0x68, 0x69, - 0x6d, 0x4f, 0x6e, 0x6c, 0x79, 0x12, 0x31, 0x0a, 0x16, 0x69, 0x5f, 0x6b, 0x6e, 0x6f, 0x77, 0x5f, - 0x77, 0x68, 0x61, 0x74, 0x5f, 0x69, 0x5f, 0x61, 0x6d, 0x5f, 0x64, 0x6f, 0x69, 0x6e, 0x67, 0x18, - 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x11, 0x69, 0x4b, 0x6e, 0x6f, 0x77, 0x57, 0x68, 0x61, 0x74, - 0x49, 0x41, 0x6d, 0x44, 0x6f, 0x69, 0x6e, 0x67, 0x22, 0x18, 0x0a, 0x16, 0x41, 0x62, 0x61, 0x6e, - 0x64, 0x6f, 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x46, 0x0a, 0x11, 0x44, 0x65, 0x62, 0x75, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x68, 0x6f, 0x77, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x04, 0x73, 0x68, 0x6f, 0x77, 0x12, 0x1d, 0x0a, 0x0a, 0x6c, - 0x65, 0x76, 0x65, 0x6c, 0x5f, 0x73, 0x70, 0x65, 0x63, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x09, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x53, 0x70, 0x65, 0x63, 0x22, 0x35, 0x0a, 0x12, 0x44, 0x65, - 0x62, 0x75, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x75, 0x62, 0x5f, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x73, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x75, 0x62, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, - 0x73, 0x22, 0x27, 0x0a, 0x0c, 0x50, 0x61, 0x79, 0x52, 0x65, 0x71, 0x53, 0x74, 0x72, 0x69, 0x6e, - 0x67, 0x12, 0x17, 0x0a, 0x07, 0x70, 0x61, 0x79, 0x5f, 0x72, 0x65, 0x71, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x06, 0x70, 0x61, 0x79, 0x52, 0x65, 0x71, 0x22, 0xf0, 0x04, 0x0a, 0x06, 0x50, - 0x61, 0x79, 0x52, 0x65, 0x71, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x74, - 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x61, 0x79, 0x6d, 0x65, - 0x6e, 0x74, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x70, - 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x48, 0x61, 0x73, 0x68, 0x12, 0x21, 0x0a, 0x0c, 0x6e, 0x75, - 0x6d, 0x5f, 0x73, 0x61, 0x74, 0x6f, 0x73, 0x68, 0x69, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, - 0x52, 0x0b, 0x6e, 0x75, 0x6d, 0x53, 0x61, 0x74, 0x6f, 0x73, 0x68, 0x69, 0x73, 0x12, 0x1c, 0x0a, - 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, - 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x16, 0x0a, 0x06, 0x65, - 0x78, 0x70, 0x69, 0x72, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x65, 0x78, 0x70, - 0x69, 0x72, 0x79, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, - 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, - 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x29, 0x0a, 0x10, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, - 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x0f, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x61, 0x73, 0x68, - 0x12, 0x23, 0x0a, 0x0d, 0x66, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x5f, 0x61, 0x64, 0x64, - 0x72, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x66, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, - 0x6b, 0x41, 0x64, 0x64, 0x72, 0x12, 0x1f, 0x0a, 0x0b, 0x63, 0x6c, 0x74, 0x76, 0x5f, 0x65, 0x78, - 0x70, 0x69, 0x72, 0x79, 0x18, 0x09, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x63, 0x6c, 0x74, 0x76, - 0x45, 0x78, 0x70, 0x69, 0x72, 0x79, 0x12, 0x31, 0x0a, 0x0b, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x5f, - 0x68, 0x69, 0x6e, 0x74, 0x73, 0x18, 0x0a, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x6c, 0x6e, - 0x72, 0x70, 0x63, 0x2e, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x48, 0x69, 0x6e, 0x74, 0x52, 0x0a, 0x72, - 0x6f, 0x75, 0x74, 0x65, 0x48, 0x69, 0x6e, 0x74, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x61, 0x79, - 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0c, 0x52, - 0x0b, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x41, 0x64, 0x64, 0x72, 0x12, 0x19, 0x0a, 0x08, - 0x6e, 0x75, 0x6d, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, - 0x6e, 0x75, 0x6d, 0x4d, 0x73, 0x61, 0x74, 0x12, 0x37, 0x0a, 0x08, 0x66, 0x65, 0x61, 0x74, 0x75, - 0x72, 0x65, 0x73, 0x18, 0x0d, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x6c, 0x6e, 0x72, 0x70, - 0x63, 0x2e, 0x50, 0x61, 0x79, 0x52, 0x65, 0x71, 0x2e, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, - 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x08, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, - 0x12, 0x3e, 0x0a, 0x0d, 0x62, 0x6c, 0x69, 0x6e, 0x64, 0x65, 0x64, 0x5f, 0x70, 0x61, 0x74, 0x68, - 0x73, 0x18, 0x0e, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, - 0x42, 0x6c, 0x69, 0x6e, 0x64, 0x65, 0x64, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x50, 0x61, - 0x74, 0x68, 0x52, 0x0c, 0x62, 0x6c, 0x69, 0x6e, 0x64, 0x65, 0x64, 0x50, 0x61, 0x74, 0x68, 0x73, - 0x1a, 0x4b, 0x0a, 0x0d, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, - 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, - 0x6b, 0x65, 0x79, 0x12, 0x24, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x65, 0x61, 0x74, 0x75, - 0x72, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x59, 0x0a, - 0x07, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1f, 0x0a, 0x0b, - 0x69, 0x73, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x08, 0x52, 0x0a, 0x69, 0x73, 0x52, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x12, 0x19, 0x0a, - 0x08, 0x69, 0x73, 0x5f, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, - 0x07, 0x69, 0x73, 0x4b, 0x6e, 0x6f, 0x77, 0x6e, 0x22, 0x12, 0x0a, 0x10, 0x46, 0x65, 0x65, 0x52, - 0x65, 0x70, 0x6f, 0x72, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x95, 0x02, 0x0a, - 0x10, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x46, 0x65, 0x65, 0x52, 0x65, 0x70, 0x6f, 0x72, - 0x74, 0x12, 0x1b, 0x0a, 0x07, 0x63, 0x68, 0x61, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x05, 0x20, 0x01, - 0x28, 0x04, 0x42, 0x02, 0x30, 0x01, 0x52, 0x06, 0x63, 0x68, 0x61, 0x6e, 0x49, 0x64, 0x12, 0x23, - 0x0a, 0x0d, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x5f, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x50, 0x6f, - 0x69, 0x6e, 0x74, 0x12, 0x22, 0x0a, 0x0d, 0x62, 0x61, 0x73, 0x65, 0x5f, 0x66, 0x65, 0x65, 0x5f, - 0x6d, 0x73, 0x61, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b, 0x62, 0x61, 0x73, 0x65, - 0x46, 0x65, 0x65, 0x4d, 0x73, 0x61, 0x74, 0x12, 0x1e, 0x0a, 0x0b, 0x66, 0x65, 0x65, 0x5f, 0x70, - 0x65, 0x72, 0x5f, 0x6d, 0x69, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x66, 0x65, - 0x65, 0x50, 0x65, 0x72, 0x4d, 0x69, 0x6c, 0x12, 0x19, 0x0a, 0x08, 0x66, 0x65, 0x65, 0x5f, 0x72, - 0x61, 0x74, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x01, 0x52, 0x07, 0x66, 0x65, 0x65, 0x52, 0x61, - 0x74, 0x65, 0x12, 0x31, 0x0a, 0x15, 0x69, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x5f, 0x62, 0x61, - 0x73, 0x65, 0x5f, 0x66, 0x65, 0x65, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, - 0x05, 0x52, 0x12, 0x69, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x42, 0x61, 0x73, 0x65, 0x46, 0x65, - 0x65, 0x4d, 0x73, 0x61, 0x74, 0x12, 0x2d, 0x0a, 0x13, 0x69, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, - 0x5f, 0x66, 0x65, 0x65, 0x5f, 0x70, 0x65, 0x72, 0x5f, 0x6d, 0x69, 0x6c, 0x18, 0x07, 0x20, 0x01, - 0x28, 0x05, 0x52, 0x10, 0x69, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x46, 0x65, 0x65, 0x50, 0x65, - 0x72, 0x4d, 0x69, 0x6c, 0x22, 0xb5, 0x01, 0x0a, 0x11, 0x46, 0x65, 0x65, 0x52, 0x65, 0x70, 0x6f, - 0x72, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3a, 0x0a, 0x0c, 0x63, 0x68, - 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x5f, 0x66, 0x65, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x17, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, - 0x46, 0x65, 0x65, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x52, 0x0b, 0x63, 0x68, 0x61, 0x6e, 0x6e, - 0x65, 0x6c, 0x46, 0x65, 0x65, 0x73, 0x12, 0x1e, 0x0a, 0x0b, 0x64, 0x61, 0x79, 0x5f, 0x66, 0x65, - 0x65, 0x5f, 0x73, 0x75, 0x6d, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x64, 0x61, 0x79, - 0x46, 0x65, 0x65, 0x53, 0x75, 0x6d, 0x12, 0x20, 0x0a, 0x0c, 0x77, 0x65, 0x65, 0x6b, 0x5f, 0x66, - 0x65, 0x65, 0x5f, 0x73, 0x75, 0x6d, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x77, 0x65, - 0x65, 0x6b, 0x46, 0x65, 0x65, 0x53, 0x75, 0x6d, 0x12, 0x22, 0x0a, 0x0d, 0x6d, 0x6f, 0x6e, 0x74, - 0x68, 0x5f, 0x66, 0x65, 0x65, 0x5f, 0x73, 0x75, 0x6d, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, - 0x0b, 0x6d, 0x6f, 0x6e, 0x74, 0x68, 0x46, 0x65, 0x65, 0x53, 0x75, 0x6d, 0x22, 0x52, 0x0a, 0x0a, - 0x49, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x46, 0x65, 0x65, 0x12, 0x22, 0x0a, 0x0d, 0x62, 0x61, - 0x73, 0x65, 0x5f, 0x66, 0x65, 0x65, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x05, 0x52, 0x0b, 0x62, 0x61, 0x73, 0x65, 0x46, 0x65, 0x65, 0x4d, 0x73, 0x61, 0x74, 0x12, 0x20, - 0x0a, 0x0c, 0x66, 0x65, 0x65, 0x5f, 0x72, 0x61, 0x74, 0x65, 0x5f, 0x70, 0x70, 0x6d, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x66, 0x65, 0x65, 0x52, 0x61, 0x74, 0x65, 0x50, 0x70, 0x6d, - 0x22, 0xaa, 0x03, 0x0a, 0x13, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x55, 0x70, 0x64, 0x61, 0x74, - 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x06, 0x67, 0x6c, 0x6f, 0x62, - 0x61, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x48, 0x00, 0x52, 0x06, 0x67, 0x6c, 0x6f, 0x62, - 0x61, 0x6c, 0x12, 0x34, 0x0a, 0x0a, 0x63, 0x68, 0x61, 0x6e, 0x5f, 0x70, 0x6f, 0x69, 0x6e, 0x74, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, - 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x09, 0x63, - 0x68, 0x61, 0x6e, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x22, 0x0a, 0x0d, 0x62, 0x61, 0x73, 0x65, - 0x5f, 0x66, 0x65, 0x65, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, - 0x0b, 0x62, 0x61, 0x73, 0x65, 0x46, 0x65, 0x65, 0x4d, 0x73, 0x61, 0x74, 0x12, 0x19, 0x0a, 0x08, - 0x66, 0x65, 0x65, 0x5f, 0x72, 0x61, 0x74, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x01, 0x52, 0x07, - 0x66, 0x65, 0x65, 0x52, 0x61, 0x74, 0x65, 0x12, 0x20, 0x0a, 0x0c, 0x66, 0x65, 0x65, 0x5f, 0x72, - 0x61, 0x74, 0x65, 0x5f, 0x70, 0x70, 0x6d, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x66, - 0x65, 0x65, 0x52, 0x61, 0x74, 0x65, 0x50, 0x70, 0x6d, 0x12, 0x26, 0x0a, 0x0f, 0x74, 0x69, 0x6d, - 0x65, 0x5f, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x18, 0x05, 0x20, 0x01, - 0x28, 0x0d, 0x52, 0x0d, 0x74, 0x69, 0x6d, 0x65, 0x4c, 0x6f, 0x63, 0x6b, 0x44, 0x65, 0x6c, 0x74, - 0x61, 0x12, 0x22, 0x0a, 0x0d, 0x6d, 0x61, 0x78, 0x5f, 0x68, 0x74, 0x6c, 0x63, 0x5f, 0x6d, 0x73, - 0x61, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x6d, 0x61, 0x78, 0x48, 0x74, 0x6c, - 0x63, 0x4d, 0x73, 0x61, 0x74, 0x12, 0x22, 0x0a, 0x0d, 0x6d, 0x69, 0x6e, 0x5f, 0x68, 0x74, 0x6c, - 0x63, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x6d, 0x69, - 0x6e, 0x48, 0x74, 0x6c, 0x63, 0x4d, 0x73, 0x61, 0x74, 0x12, 0x35, 0x0a, 0x17, 0x6d, 0x69, 0x6e, - 0x5f, 0x68, 0x74, 0x6c, 0x63, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x5f, 0x73, 0x70, 0x65, 0x63, 0x69, - 0x66, 0x69, 0x65, 0x64, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, 0x14, 0x6d, 0x69, 0x6e, 0x48, - 0x74, 0x6c, 0x63, 0x4d, 0x73, 0x61, 0x74, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x65, 0x64, - 0x12, 0x32, 0x0a, 0x0b, 0x69, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x5f, 0x66, 0x65, 0x65, 0x18, - 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x49, 0x6e, - 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x46, 0x65, 0x65, 0x52, 0x0a, 0x69, 0x6e, 0x62, 0x6f, 0x75, 0x6e, - 0x64, 0x46, 0x65, 0x65, 0x42, 0x07, 0x0a, 0x05, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x22, 0x8c, 0x01, - 0x0a, 0x0c, 0x46, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x2b, - 0x0a, 0x08, 0x6f, 0x75, 0x74, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x0f, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4f, 0x75, 0x74, 0x50, 0x6f, 0x69, 0x6e, - 0x74, 0x52, 0x08, 0x6f, 0x75, 0x74, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x2c, 0x0a, 0x06, 0x72, - 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x14, 0x2e, 0x6c, 0x6e, - 0x72, 0x70, 0x63, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x46, 0x61, 0x69, 0x6c, 0x75, 0x72, - 0x65, 0x52, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x12, 0x21, 0x0a, 0x0c, 0x75, 0x70, 0x64, - 0x61, 0x74, 0x65, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x0b, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x22, 0x52, 0x0a, 0x14, - 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3a, 0x0a, 0x0e, 0x66, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x5f, 0x75, - 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x6c, - 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x55, 0x70, 0x64, 0x61, 0x74, - 0x65, 0x52, 0x0d, 0x66, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, - 0x22, 0xc9, 0x01, 0x0a, 0x18, 0x46, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x48, - 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, - 0x0a, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x04, 0x52, 0x09, 0x73, 0x74, 0x61, 0x72, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x19, 0x0a, 0x08, - 0x65, 0x6e, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, - 0x65, 0x6e, 0x64, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x69, 0x6e, 0x64, 0x65, 0x78, - 0x5f, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x69, - 0x6e, 0x64, 0x65, 0x78, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x12, 0x24, 0x0a, 0x0e, 0x6e, 0x75, - 0x6d, 0x5f, 0x6d, 0x61, 0x78, 0x5f, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x04, 0x20, 0x01, - 0x28, 0x0d, 0x52, 0x0c, 0x6e, 0x75, 0x6d, 0x4d, 0x61, 0x78, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, - 0x12, 0x2a, 0x0a, 0x11, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x5f, 0x6c, - 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, 0x70, 0x65, 0x65, - 0x72, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x22, 0x85, 0x03, 0x0a, - 0x0f, 0x46, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x45, 0x76, 0x65, 0x6e, 0x74, - 0x12, 0x20, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x04, 0x42, 0x02, 0x18, 0x01, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, - 0x6d, 0x70, 0x12, 0x20, 0x0a, 0x0a, 0x63, 0x68, 0x61, 0x6e, 0x5f, 0x69, 0x64, 0x5f, 0x69, 0x6e, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x42, 0x02, 0x30, 0x01, 0x52, 0x08, 0x63, 0x68, 0x61, 0x6e, - 0x49, 0x64, 0x49, 0x6e, 0x12, 0x22, 0x0a, 0x0b, 0x63, 0x68, 0x61, 0x6e, 0x5f, 0x69, 0x64, 0x5f, - 0x6f, 0x75, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x42, 0x02, 0x30, 0x01, 0x52, 0x09, 0x63, - 0x68, 0x61, 0x6e, 0x49, 0x64, 0x4f, 0x75, 0x74, 0x12, 0x15, 0x0a, 0x06, 0x61, 0x6d, 0x74, 0x5f, - 0x69, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x61, 0x6d, 0x74, 0x49, 0x6e, 0x12, - 0x17, 0x0a, 0x07, 0x61, 0x6d, 0x74, 0x5f, 0x6f, 0x75, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x04, - 0x52, 0x06, 0x61, 0x6d, 0x74, 0x4f, 0x75, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x66, 0x65, 0x65, 0x18, - 0x07, 0x20, 0x01, 0x28, 0x04, 0x52, 0x03, 0x66, 0x65, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x66, 0x65, - 0x65, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x08, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, 0x66, 0x65, - 0x65, 0x4d, 0x73, 0x61, 0x74, 0x12, 0x1e, 0x0a, 0x0b, 0x61, 0x6d, 0x74, 0x5f, 0x69, 0x6e, 0x5f, - 0x6d, 0x73, 0x61, 0x74, 0x18, 0x09, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x61, 0x6d, 0x74, 0x49, - 0x6e, 0x4d, 0x73, 0x61, 0x74, 0x12, 0x20, 0x0a, 0x0c, 0x61, 0x6d, 0x74, 0x5f, 0x6f, 0x75, 0x74, - 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x61, 0x6d, 0x74, - 0x4f, 0x75, 0x74, 0x4d, 0x73, 0x61, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x74, 0x69, 0x6d, 0x65, 0x73, - 0x74, 0x61, 0x6d, 0x70, 0x5f, 0x6e, 0x73, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x74, - 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x4e, 0x73, 0x12, 0x22, 0x0a, 0x0d, 0x70, 0x65, - 0x65, 0x72, 0x5f, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x5f, 0x69, 0x6e, 0x18, 0x0c, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x0b, 0x70, 0x65, 0x65, 0x72, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x49, 0x6e, 0x12, 0x24, - 0x0a, 0x0e, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x5f, 0x6f, 0x75, 0x74, - 0x18, 0x0d, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x70, 0x65, 0x65, 0x72, 0x41, 0x6c, 0x69, 0x61, - 0x73, 0x4f, 0x75, 0x74, 0x22, 0x8c, 0x01, 0x0a, 0x19, 0x46, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, - 0x69, 0x6e, 0x67, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x43, 0x0a, 0x11, 0x66, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, - 0x5f, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, - 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, - 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x10, 0x66, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x69, 0x6e, - 0x67, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x2a, 0x0a, 0x11, 0x6c, 0x61, 0x73, 0x74, 0x5f, - 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x0d, 0x52, 0x0f, 0x6c, 0x61, 0x73, 0x74, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x49, 0x6e, - 0x64, 0x65, 0x78, 0x22, 0x50, 0x0a, 0x1a, 0x45, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x43, 0x68, 0x61, - 0x6e, 0x6e, 0x65, 0x6c, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x12, 0x32, 0x0a, 0x0a, 0x63, 0x68, 0x61, 0x6e, 0x5f, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, - 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x09, 0x63, 0x68, 0x61, 0x6e, - 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x22, 0x64, 0x0a, 0x0d, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, - 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x12, 0x32, 0x0a, 0x0a, 0x63, 0x68, 0x61, 0x6e, 0x5f, 0x70, + 0x01, 0x22, 0x59, 0x0a, 0x0d, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x53, 0x74, 0x61, 0x74, + 0x75, 0x73, 0x12, 0x0f, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x1a, + 0x02, 0x08, 0x01, 0x12, 0x0d, 0x0a, 0x09, 0x49, 0x4e, 0x5f, 0x46, 0x4c, 0x49, 0x47, 0x48, 0x54, + 0x10, 0x01, 0x12, 0x0d, 0x0a, 0x09, 0x53, 0x55, 0x43, 0x43, 0x45, 0x45, 0x44, 0x45, 0x44, 0x10, + 0x02, 0x12, 0x0a, 0x0a, 0x06, 0x46, 0x41, 0x49, 0x4c, 0x45, 0x44, 0x10, 0x03, 0x12, 0x0d, 0x0a, + 0x09, 0x49, 0x4e, 0x49, 0x54, 0x49, 0x41, 0x54, 0x45, 0x44, 0x10, 0x04, 0x4a, 0x04, 0x08, 0x04, + 0x10, 0x05, 0x22, 0xd5, 0x02, 0x0a, 0x0b, 0x48, 0x54, 0x4c, 0x43, 0x41, 0x74, 0x74, 0x65, 0x6d, + 0x70, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x61, 0x74, 0x74, 0x65, 0x6d, 0x70, 0x74, 0x5f, 0x69, 0x64, + 0x18, 0x07, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x61, 0x74, 0x74, 0x65, 0x6d, 0x70, 0x74, 0x49, + 0x64, 0x12, 0x35, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0e, 0x32, 0x1d, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x48, 0x54, 0x4c, 0x43, 0x41, 0x74, + 0x74, 0x65, 0x6d, 0x70, 0x74, 0x2e, 0x48, 0x54, 0x4c, 0x43, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, + 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x22, 0x0a, 0x05, 0x72, 0x6f, 0x75, 0x74, + 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, + 0x52, 0x6f, 0x75, 0x74, 0x65, 0x52, 0x05, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x12, 0x26, 0x0a, 0x0f, + 0x61, 0x74, 0x74, 0x65, 0x6d, 0x70, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x6e, 0x73, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0d, 0x61, 0x74, 0x74, 0x65, 0x6d, 0x70, 0x74, 0x54, 0x69, + 0x6d, 0x65, 0x4e, 0x73, 0x12, 0x26, 0x0a, 0x0f, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x5f, + 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x6e, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0d, 0x72, + 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x4e, 0x73, 0x12, 0x28, 0x0a, 0x07, + 0x66, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, + 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x52, 0x07, 0x66, + 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x72, 0x65, 0x69, 0x6d, 0x61, + 0x67, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x70, 0x72, 0x65, 0x69, 0x6d, 0x61, + 0x67, 0x65, 0x22, 0x36, 0x0a, 0x0a, 0x48, 0x54, 0x4c, 0x43, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, + 0x12, 0x0d, 0x0a, 0x09, 0x49, 0x4e, 0x5f, 0x46, 0x4c, 0x49, 0x47, 0x48, 0x54, 0x10, 0x00, 0x12, + 0x0d, 0x0a, 0x09, 0x53, 0x55, 0x43, 0x43, 0x45, 0x45, 0x44, 0x45, 0x44, 0x10, 0x01, 0x12, 0x0a, + 0x0a, 0x06, 0x46, 0x41, 0x49, 0x4c, 0x45, 0x44, 0x10, 0x02, 0x22, 0xb4, 0x02, 0x0a, 0x13, 0x4c, + 0x69, 0x73, 0x74, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x12, 0x2d, 0x0a, 0x12, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x5f, 0x69, 0x6e, + 0x63, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x11, + 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x49, 0x6e, 0x63, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, + 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x5f, 0x6f, 0x66, 0x66, 0x73, 0x65, + 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x4f, 0x66, + 0x66, 0x73, 0x65, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x6d, 0x61, 0x78, 0x5f, 0x70, 0x61, 0x79, 0x6d, + 0x65, 0x6e, 0x74, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x6d, 0x61, 0x78, 0x50, + 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x76, 0x65, 0x72, + 0x73, 0x65, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x72, 0x65, 0x76, 0x65, 0x72, + 0x73, 0x65, 0x64, 0x12, 0x30, 0x0a, 0x14, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x74, 0x6f, 0x74, + 0x61, 0x6c, 0x5f, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, + 0x08, 0x52, 0x12, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x54, 0x6f, 0x74, 0x61, 0x6c, 0x50, 0x61, 0x79, + 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x2e, 0x0a, 0x13, 0x63, 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x5f, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x73, 0x74, 0x61, 0x72, 0x74, 0x18, 0x06, 0x20, 0x01, + 0x28, 0x04, 0x52, 0x11, 0x63, 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x61, 0x74, 0x65, + 0x53, 0x74, 0x61, 0x72, 0x74, 0x12, 0x2a, 0x0a, 0x11, 0x63, 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x5f, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x65, 0x6e, 0x64, 0x18, 0x07, 0x20, 0x01, 0x28, 0x04, + 0x52, 0x0f, 0x63, 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x61, 0x74, 0x65, 0x45, 0x6e, + 0x64, 0x22, 0xca, 0x01, 0x0a, 0x14, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, + 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2a, 0x0a, 0x08, 0x70, 0x61, + 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x6c, + 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x08, 0x70, 0x61, + 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x2c, 0x0a, 0x12, 0x66, 0x69, 0x72, 0x73, 0x74, 0x5f, + 0x69, 0x6e, 0x64, 0x65, 0x78, 0x5f, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x04, 0x52, 0x10, 0x66, 0x69, 0x72, 0x73, 0x74, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x4f, 0x66, + 0x66, 0x73, 0x65, 0x74, 0x12, 0x2a, 0x0a, 0x11, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x69, 0x6e, 0x64, + 0x65, 0x78, 0x5f, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, + 0x0f, 0x6c, 0x61, 0x73, 0x74, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, + 0x12, 0x2c, 0x0a, 0x12, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x6e, 0x75, 0x6d, 0x5f, 0x70, 0x61, + 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x10, 0x74, 0x6f, + 0x74, 0x61, 0x6c, 0x4e, 0x75, 0x6d, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x22, 0x65, + 0x0a, 0x14, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, + 0x74, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x70, 0x61, + 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x48, 0x61, 0x73, 0x68, 0x12, 0x2a, 0x0a, 0x11, 0x66, 0x61, 0x69, + 0x6c, 0x65, 0x64, 0x5f, 0x68, 0x74, 0x6c, 0x63, 0x73, 0x5f, 0x6f, 0x6e, 0x6c, 0x79, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, 0x66, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x48, 0x74, 0x6c, 0x63, + 0x73, 0x4f, 0x6e, 0x6c, 0x79, 0x22, 0x9b, 0x01, 0x0a, 0x18, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, + 0x41, 0x6c, 0x6c, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x12, 0x30, 0x0a, 0x14, 0x66, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x5f, 0x70, 0x61, 0x79, + 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x5f, 0x6f, 0x6e, 0x6c, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, + 0x52, 0x12, 0x66, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x73, + 0x4f, 0x6e, 0x6c, 0x79, 0x12, 0x2a, 0x0a, 0x11, 0x66, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x5f, 0x68, + 0x74, 0x6c, 0x63, 0x73, 0x5f, 0x6f, 0x6e, 0x6c, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, + 0x0f, 0x66, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x48, 0x74, 0x6c, 0x63, 0x73, 0x4f, 0x6e, 0x6c, 0x79, + 0x12, 0x21, 0x0a, 0x0c, 0x61, 0x6c, 0x6c, 0x5f, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x73, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x61, 0x6c, 0x6c, 0x50, 0x61, 0x79, 0x6d, 0x65, + 0x6e, 0x74, 0x73, 0x22, 0x17, 0x0a, 0x15, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x50, 0x61, 0x79, + 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x0a, 0x19, + 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x41, 0x6c, 0x6c, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, + 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xbf, 0x01, 0x0a, 0x15, 0x41, 0x62, + 0x61, 0x6e, 0x64, 0x6f, 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x12, 0x38, 0x0a, 0x0d, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x5f, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x52, - 0x09, 0x63, 0x68, 0x61, 0x6e, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x63, 0x68, - 0x61, 0x6e, 0x5f, 0x62, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, - 0x0a, 0x63, 0x68, 0x61, 0x6e, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x22, 0x73, 0x0a, 0x0f, 0x4d, - 0x75, 0x6c, 0x74, 0x69, 0x43, 0x68, 0x61, 0x6e, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x12, 0x34, - 0x0a, 0x0b, 0x63, 0x68, 0x61, 0x6e, 0x5f, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x18, 0x01, 0x20, - 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, - 0x6e, 0x65, 0x6c, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x0a, 0x63, 0x68, 0x61, 0x6e, 0x50, 0x6f, - 0x69, 0x6e, 0x74, 0x73, 0x12, 0x2a, 0x0a, 0x11, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x5f, 0x63, 0x68, - 0x61, 0x6e, 0x5f, 0x62, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, + 0x0c, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x39, 0x0a, + 0x19, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x66, 0x75, 0x6e, 0x64, 0x69, 0x6e, 0x67, + 0x5f, 0x73, 0x68, 0x69, 0x6d, 0x5f, 0x6f, 0x6e, 0x6c, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, + 0x52, 0x16, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x46, 0x75, 0x6e, 0x64, 0x69, 0x6e, 0x67, + 0x53, 0x68, 0x69, 0x6d, 0x4f, 0x6e, 0x6c, 0x79, 0x12, 0x31, 0x0a, 0x16, 0x69, 0x5f, 0x6b, 0x6e, + 0x6f, 0x77, 0x5f, 0x77, 0x68, 0x61, 0x74, 0x5f, 0x69, 0x5f, 0x61, 0x6d, 0x5f, 0x64, 0x6f, 0x69, + 0x6e, 0x67, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x11, 0x69, 0x4b, 0x6e, 0x6f, 0x77, 0x57, + 0x68, 0x61, 0x74, 0x49, 0x41, 0x6d, 0x44, 0x6f, 0x69, 0x6e, 0x67, 0x22, 0x18, 0x0a, 0x16, 0x41, + 0x62, 0x61, 0x6e, 0x64, 0x6f, 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x46, 0x0a, 0x11, 0x44, 0x65, 0x62, 0x75, 0x67, 0x4c, 0x65, + 0x76, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x68, + 0x6f, 0x77, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x04, 0x73, 0x68, 0x6f, 0x77, 0x12, 0x1d, + 0x0a, 0x0a, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x5f, 0x73, 0x70, 0x65, 0x63, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x09, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x53, 0x70, 0x65, 0x63, 0x22, 0x35, 0x0a, + 0x12, 0x44, 0x65, 0x62, 0x75, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x75, 0x62, 0x5f, 0x73, 0x79, 0x73, 0x74, 0x65, + 0x6d, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x75, 0x62, 0x53, 0x79, 0x73, + 0x74, 0x65, 0x6d, 0x73, 0x22, 0x27, 0x0a, 0x0c, 0x50, 0x61, 0x79, 0x52, 0x65, 0x71, 0x53, 0x74, + 0x72, 0x69, 0x6e, 0x67, 0x12, 0x17, 0x0a, 0x07, 0x70, 0x61, 0x79, 0x5f, 0x72, 0x65, 0x71, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x70, 0x61, 0x79, 0x52, 0x65, 0x71, 0x22, 0xf0, 0x04, + 0x0a, 0x06, 0x50, 0x61, 0x79, 0x52, 0x65, 0x71, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x74, + 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, + 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x61, + 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0b, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x48, 0x61, 0x73, 0x68, 0x12, 0x21, 0x0a, + 0x0c, 0x6e, 0x75, 0x6d, 0x5f, 0x73, 0x61, 0x74, 0x6f, 0x73, 0x68, 0x69, 0x73, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x03, 0x52, 0x0b, 0x6e, 0x75, 0x6d, 0x53, 0x61, 0x74, 0x6f, 0x73, 0x68, 0x69, 0x73, + 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x04, 0x20, + 0x01, 0x28, 0x03, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x16, + 0x0a, 0x06, 0x65, 0x78, 0x70, 0x69, 0x72, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, + 0x65, 0x78, 0x70, 0x69, 0x72, 0x79, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, + 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, + 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x29, 0x0a, 0x10, 0x64, 0x65, 0x73, 0x63, + 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x07, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x0f, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x48, + 0x61, 0x73, 0x68, 0x12, 0x23, 0x0a, 0x0d, 0x66, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x5f, + 0x61, 0x64, 0x64, 0x72, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x66, 0x61, 0x6c, 0x6c, + 0x62, 0x61, 0x63, 0x6b, 0x41, 0x64, 0x64, 0x72, 0x12, 0x1f, 0x0a, 0x0b, 0x63, 0x6c, 0x74, 0x76, + 0x5f, 0x65, 0x78, 0x70, 0x69, 0x72, 0x79, 0x18, 0x09, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x63, + 0x6c, 0x74, 0x76, 0x45, 0x78, 0x70, 0x69, 0x72, 0x79, 0x12, 0x31, 0x0a, 0x0b, 0x72, 0x6f, 0x75, + 0x74, 0x65, 0x5f, 0x68, 0x69, 0x6e, 0x74, 0x73, 0x18, 0x0a, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x10, + 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x48, 0x69, 0x6e, 0x74, + 0x52, 0x0a, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x48, 0x69, 0x6e, 0x74, 0x73, 0x12, 0x21, 0x0a, 0x0c, + 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x18, 0x0b, 0x20, 0x01, + 0x28, 0x0c, 0x52, 0x0b, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x41, 0x64, 0x64, 0x72, 0x12, + 0x19, 0x0a, 0x08, 0x6e, 0x75, 0x6d, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x0c, 0x20, 0x01, 0x28, + 0x03, 0x52, 0x07, 0x6e, 0x75, 0x6d, 0x4d, 0x73, 0x61, 0x74, 0x12, 0x37, 0x0a, 0x08, 0x66, 0x65, + 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x18, 0x0d, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x6c, + 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x61, 0x79, 0x52, 0x65, 0x71, 0x2e, 0x46, 0x65, 0x61, 0x74, + 0x75, 0x72, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x08, 0x66, 0x65, 0x61, 0x74, 0x75, + 0x72, 0x65, 0x73, 0x12, 0x3e, 0x0a, 0x0d, 0x62, 0x6c, 0x69, 0x6e, 0x64, 0x65, 0x64, 0x5f, 0x70, + 0x61, 0x74, 0x68, 0x73, 0x18, 0x0e, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x6c, 0x6e, 0x72, + 0x70, 0x63, 0x2e, 0x42, 0x6c, 0x69, 0x6e, 0x64, 0x65, 0x64, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, + 0x74, 0x50, 0x61, 0x74, 0x68, 0x52, 0x0c, 0x62, 0x6c, 0x69, 0x6e, 0x64, 0x65, 0x64, 0x50, 0x61, + 0x74, 0x68, 0x73, 0x1a, 0x4b, 0x0a, 0x0d, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x45, + 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0d, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x24, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x65, + 0x61, 0x74, 0x75, 0x72, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, + 0x22, 0x59, 0x0a, 0x07, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, + 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, + 0x1f, 0x0a, 0x0b, 0x69, 0x73, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x69, 0x73, 0x52, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, + 0x12, 0x19, 0x0a, 0x08, 0x69, 0x73, 0x5f, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x07, 0x69, 0x73, 0x4b, 0x6e, 0x6f, 0x77, 0x6e, 0x22, 0x12, 0x0a, 0x10, 0x46, + 0x65, 0x65, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, + 0x95, 0x02, 0x0a, 0x10, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x46, 0x65, 0x65, 0x52, 0x65, + 0x70, 0x6f, 0x72, 0x74, 0x12, 0x1b, 0x0a, 0x07, 0x63, 0x68, 0x61, 0x6e, 0x5f, 0x69, 0x64, 0x18, + 0x05, 0x20, 0x01, 0x28, 0x04, 0x42, 0x02, 0x30, 0x01, 0x52, 0x06, 0x63, 0x68, 0x61, 0x6e, 0x49, + 0x64, 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x5f, 0x70, 0x6f, 0x69, + 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, + 0x6c, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x22, 0x0a, 0x0d, 0x62, 0x61, 0x73, 0x65, 0x5f, 0x66, + 0x65, 0x65, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b, 0x62, + 0x61, 0x73, 0x65, 0x46, 0x65, 0x65, 0x4d, 0x73, 0x61, 0x74, 0x12, 0x1e, 0x0a, 0x0b, 0x66, 0x65, + 0x65, 0x5f, 0x70, 0x65, 0x72, 0x5f, 0x6d, 0x69, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, + 0x09, 0x66, 0x65, 0x65, 0x50, 0x65, 0x72, 0x4d, 0x69, 0x6c, 0x12, 0x19, 0x0a, 0x08, 0x66, 0x65, + 0x65, 0x5f, 0x72, 0x61, 0x74, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x01, 0x52, 0x07, 0x66, 0x65, + 0x65, 0x52, 0x61, 0x74, 0x65, 0x12, 0x31, 0x0a, 0x15, 0x69, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, + 0x5f, 0x62, 0x61, 0x73, 0x65, 0x5f, 0x66, 0x65, 0x65, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x06, + 0x20, 0x01, 0x28, 0x05, 0x52, 0x12, 0x69, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x42, 0x61, 0x73, + 0x65, 0x46, 0x65, 0x65, 0x4d, 0x73, 0x61, 0x74, 0x12, 0x2d, 0x0a, 0x13, 0x69, 0x6e, 0x62, 0x6f, + 0x75, 0x6e, 0x64, 0x5f, 0x66, 0x65, 0x65, 0x5f, 0x70, 0x65, 0x72, 0x5f, 0x6d, 0x69, 0x6c, 0x18, + 0x07, 0x20, 0x01, 0x28, 0x05, 0x52, 0x10, 0x69, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x46, 0x65, + 0x65, 0x50, 0x65, 0x72, 0x4d, 0x69, 0x6c, 0x22, 0xb5, 0x01, 0x0a, 0x11, 0x46, 0x65, 0x65, 0x52, + 0x65, 0x70, 0x6f, 0x72, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3a, 0x0a, + 0x0c, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x5f, 0x66, 0x65, 0x65, 0x73, 0x18, 0x01, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, + 0x6e, 0x65, 0x6c, 0x46, 0x65, 0x65, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x52, 0x0b, 0x63, 0x68, + 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x46, 0x65, 0x65, 0x73, 0x12, 0x1e, 0x0a, 0x0b, 0x64, 0x61, 0x79, + 0x5f, 0x66, 0x65, 0x65, 0x5f, 0x73, 0x75, 0x6d, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, + 0x64, 0x61, 0x79, 0x46, 0x65, 0x65, 0x53, 0x75, 0x6d, 0x12, 0x20, 0x0a, 0x0c, 0x77, 0x65, 0x65, + 0x6b, 0x5f, 0x66, 0x65, 0x65, 0x5f, 0x73, 0x75, 0x6d, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, + 0x0a, 0x77, 0x65, 0x65, 0x6b, 0x46, 0x65, 0x65, 0x53, 0x75, 0x6d, 0x12, 0x22, 0x0a, 0x0d, 0x6d, + 0x6f, 0x6e, 0x74, 0x68, 0x5f, 0x66, 0x65, 0x65, 0x5f, 0x73, 0x75, 0x6d, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x04, 0x52, 0x0b, 0x6d, 0x6f, 0x6e, 0x74, 0x68, 0x46, 0x65, 0x65, 0x53, 0x75, 0x6d, 0x22, + 0x52, 0x0a, 0x0a, 0x49, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x46, 0x65, 0x65, 0x12, 0x22, 0x0a, + 0x0d, 0x62, 0x61, 0x73, 0x65, 0x5f, 0x66, 0x65, 0x65, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x05, 0x52, 0x0b, 0x62, 0x61, 0x73, 0x65, 0x46, 0x65, 0x65, 0x4d, 0x73, 0x61, + 0x74, 0x12, 0x20, 0x0a, 0x0c, 0x66, 0x65, 0x65, 0x5f, 0x72, 0x61, 0x74, 0x65, 0x5f, 0x70, 0x70, + 0x6d, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x66, 0x65, 0x65, 0x52, 0x61, 0x74, 0x65, + 0x50, 0x70, 0x6d, 0x22, 0xaa, 0x03, 0x0a, 0x13, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x55, 0x70, + 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x06, 0x67, + 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x48, 0x00, 0x52, 0x06, 0x67, + 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x12, 0x34, 0x0a, 0x0a, 0x63, 0x68, 0x61, 0x6e, 0x5f, 0x70, 0x6f, + 0x69, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x6c, 0x6e, 0x72, 0x70, + 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x48, 0x00, + 0x52, 0x09, 0x63, 0x68, 0x61, 0x6e, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x22, 0x0a, 0x0d, 0x62, + 0x61, 0x73, 0x65, 0x5f, 0x66, 0x65, 0x65, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x03, 0x52, 0x0b, 0x62, 0x61, 0x73, 0x65, 0x46, 0x65, 0x65, 0x4d, 0x73, 0x61, 0x74, 0x12, + 0x19, 0x0a, 0x08, 0x66, 0x65, 0x65, 0x5f, 0x72, 0x61, 0x74, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x01, 0x52, 0x07, 0x66, 0x65, 0x65, 0x52, 0x61, 0x74, 0x65, 0x12, 0x20, 0x0a, 0x0c, 0x66, 0x65, + 0x65, 0x5f, 0x72, 0x61, 0x74, 0x65, 0x5f, 0x70, 0x70, 0x6d, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0d, + 0x52, 0x0a, 0x66, 0x65, 0x65, 0x52, 0x61, 0x74, 0x65, 0x50, 0x70, 0x6d, 0x12, 0x26, 0x0a, 0x0f, + 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x18, + 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0d, 0x74, 0x69, 0x6d, 0x65, 0x4c, 0x6f, 0x63, 0x6b, 0x44, + 0x65, 0x6c, 0x74, 0x61, 0x12, 0x22, 0x0a, 0x0d, 0x6d, 0x61, 0x78, 0x5f, 0x68, 0x74, 0x6c, 0x63, + 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x6d, 0x61, 0x78, + 0x48, 0x74, 0x6c, 0x63, 0x4d, 0x73, 0x61, 0x74, 0x12, 0x22, 0x0a, 0x0d, 0x6d, 0x69, 0x6e, 0x5f, + 0x68, 0x74, 0x6c, 0x63, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x04, 0x52, + 0x0b, 0x6d, 0x69, 0x6e, 0x48, 0x74, 0x6c, 0x63, 0x4d, 0x73, 0x61, 0x74, 0x12, 0x35, 0x0a, 0x17, + 0x6d, 0x69, 0x6e, 0x5f, 0x68, 0x74, 0x6c, 0x63, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x5f, 0x73, 0x70, + 0x65, 0x63, 0x69, 0x66, 0x69, 0x65, 0x64, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, 0x14, 0x6d, + 0x69, 0x6e, 0x48, 0x74, 0x6c, 0x63, 0x4d, 0x73, 0x61, 0x74, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, + 0x69, 0x65, 0x64, 0x12, 0x32, 0x0a, 0x0b, 0x69, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x5f, 0x66, + 0x65, 0x65, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, + 0x2e, 0x49, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x46, 0x65, 0x65, 0x52, 0x0a, 0x69, 0x6e, 0x62, + 0x6f, 0x75, 0x6e, 0x64, 0x46, 0x65, 0x65, 0x42, 0x07, 0x0a, 0x05, 0x73, 0x63, 0x6f, 0x70, 0x65, + 0x22, 0x8c, 0x01, 0x0a, 0x0c, 0x46, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x55, 0x70, 0x64, 0x61, 0x74, + 0x65, 0x12, 0x2b, 0x0a, 0x08, 0x6f, 0x75, 0x74, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4f, 0x75, 0x74, 0x50, + 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x08, 0x6f, 0x75, 0x74, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x2c, + 0x0a, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x14, + 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x46, 0x61, 0x69, + 0x6c, 0x75, 0x72, 0x65, 0x52, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x12, 0x21, 0x0a, 0x0c, + 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x0b, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x22, + 0x52, 0x0a, 0x14, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3a, 0x0a, 0x0e, 0x66, 0x61, 0x69, 0x6c, 0x65, + 0x64, 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x13, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x55, 0x70, + 0x64, 0x61, 0x74, 0x65, 0x52, 0x0d, 0x66, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x55, 0x70, 0x64, 0x61, + 0x74, 0x65, 0x73, 0x22, 0xc9, 0x01, 0x0a, 0x18, 0x46, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x69, + 0x6e, 0x67, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x73, 0x74, 0x61, 0x72, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x12, + 0x19, 0x0a, 0x08, 0x65, 0x6e, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x04, 0x52, 0x07, 0x65, 0x6e, 0x64, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x69, 0x6e, + 0x64, 0x65, 0x78, 0x5f, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, + 0x52, 0x0b, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x12, 0x24, 0x0a, + 0x0e, 0x6e, 0x75, 0x6d, 0x5f, 0x6d, 0x61, 0x78, 0x5f, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0c, 0x6e, 0x75, 0x6d, 0x4d, 0x61, 0x78, 0x45, 0x76, 0x65, + 0x6e, 0x74, 0x73, 0x12, 0x2a, 0x0a, 0x11, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x61, 0x6c, 0x69, 0x61, + 0x73, 0x5f, 0x6c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, + 0x70, 0x65, 0x65, 0x72, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x22, + 0x85, 0x03, 0x0a, 0x0f, 0x46, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x45, 0x76, + 0x65, 0x6e, 0x74, 0x12, 0x20, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x42, 0x02, 0x18, 0x01, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, + 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x20, 0x0a, 0x0a, 0x63, 0x68, 0x61, 0x6e, 0x5f, 0x69, 0x64, + 0x5f, 0x69, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x42, 0x02, 0x30, 0x01, 0x52, 0x08, 0x63, + 0x68, 0x61, 0x6e, 0x49, 0x64, 0x49, 0x6e, 0x12, 0x22, 0x0a, 0x0b, 0x63, 0x68, 0x61, 0x6e, 0x5f, + 0x69, 0x64, 0x5f, 0x6f, 0x75, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x42, 0x02, 0x30, 0x01, + 0x52, 0x09, 0x63, 0x68, 0x61, 0x6e, 0x49, 0x64, 0x4f, 0x75, 0x74, 0x12, 0x15, 0x0a, 0x06, 0x61, + 0x6d, 0x74, 0x5f, 0x69, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x61, 0x6d, 0x74, + 0x49, 0x6e, 0x12, 0x17, 0x0a, 0x07, 0x61, 0x6d, 0x74, 0x5f, 0x6f, 0x75, 0x74, 0x18, 0x06, 0x20, + 0x01, 0x28, 0x04, 0x52, 0x06, 0x61, 0x6d, 0x74, 0x4f, 0x75, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x66, + 0x65, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x04, 0x52, 0x03, 0x66, 0x65, 0x65, 0x12, 0x19, 0x0a, + 0x08, 0x66, 0x65, 0x65, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x08, 0x20, 0x01, 0x28, 0x04, 0x52, + 0x07, 0x66, 0x65, 0x65, 0x4d, 0x73, 0x61, 0x74, 0x12, 0x1e, 0x0a, 0x0b, 0x61, 0x6d, 0x74, 0x5f, + 0x69, 0x6e, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x09, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x61, + 0x6d, 0x74, 0x49, 0x6e, 0x4d, 0x73, 0x61, 0x74, 0x12, 0x20, 0x0a, 0x0c, 0x61, 0x6d, 0x74, 0x5f, + 0x6f, 0x75, 0x74, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, + 0x61, 0x6d, 0x74, 0x4f, 0x75, 0x74, 0x4d, 0x73, 0x61, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x74, 0x69, + 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x5f, 0x6e, 0x73, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x04, + 0x52, 0x0b, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x4e, 0x73, 0x12, 0x22, 0x0a, + 0x0d, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x5f, 0x69, 0x6e, 0x18, 0x0c, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x70, 0x65, 0x65, 0x72, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x49, + 0x6e, 0x12, 0x24, 0x0a, 0x0e, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x5f, + 0x6f, 0x75, 0x74, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x70, 0x65, 0x65, 0x72, 0x41, + 0x6c, 0x69, 0x61, 0x73, 0x4f, 0x75, 0x74, 0x22, 0x8c, 0x01, 0x0a, 0x19, 0x46, 0x6f, 0x72, 0x77, + 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x43, 0x0a, 0x11, 0x66, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, + 0x69, 0x6e, 0x67, 0x5f, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x16, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, + 0x69, 0x6e, 0x67, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x10, 0x66, 0x6f, 0x72, 0x77, 0x61, 0x72, + 0x64, 0x69, 0x6e, 0x67, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x2a, 0x0a, 0x11, 0x6c, 0x61, + 0x73, 0x74, 0x5f, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0f, 0x6c, 0x61, 0x73, 0x74, 0x4f, 0x66, 0x66, 0x73, 0x65, + 0x74, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x22, 0x50, 0x0a, 0x1a, 0x45, 0x78, 0x70, 0x6f, 0x72, 0x74, + 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x12, 0x32, 0x0a, 0x0a, 0x63, 0x68, 0x61, 0x6e, 0x5f, 0x70, 0x6f, 0x69, + 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, + 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x09, 0x63, + 0x68, 0x61, 0x6e, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x22, 0x64, 0x0a, 0x0d, 0x43, 0x68, 0x61, 0x6e, + 0x6e, 0x65, 0x6c, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x12, 0x32, 0x0a, 0x0a, 0x63, 0x68, 0x61, + 0x6e, 0x5f, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, + 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x50, 0x6f, 0x69, + 0x6e, 0x74, 0x52, 0x09, 0x63, 0x68, 0x61, 0x6e, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x1f, 0x0a, + 0x0b, 0x63, 0x68, 0x61, 0x6e, 0x5f, 0x62, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0c, 0x52, 0x0a, 0x63, 0x68, 0x61, 0x6e, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x22, 0x73, + 0x0a, 0x0f, 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x43, 0x68, 0x61, 0x6e, 0x42, 0x61, 0x63, 0x6b, 0x75, + 0x70, 0x12, 0x34, 0x0a, 0x0b, 0x63, 0x68, 0x61, 0x6e, 0x5f, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, + 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, + 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x0a, 0x63, 0x68, 0x61, + 0x6e, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x12, 0x2a, 0x0a, 0x11, 0x6d, 0x75, 0x6c, 0x74, 0x69, + 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x5f, 0x62, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0c, 0x52, 0x0f, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x43, 0x68, 0x61, 0x6e, 0x42, 0x61, 0x63, + 0x6b, 0x75, 0x70, 0x22, 0x19, 0x0a, 0x17, 0x43, 0x68, 0x61, 0x6e, 0x42, 0x61, 0x63, 0x6b, 0x75, + 0x70, 0x45, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x9f, + 0x01, 0x0a, 0x12, 0x43, 0x68, 0x61, 0x6e, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x53, 0x6e, 0x61, + 0x70, 0x73, 0x68, 0x6f, 0x74, 0x12, 0x45, 0x0a, 0x13, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x5f, + 0x63, 0x68, 0x61, 0x6e, 0x5f, 0x62, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x73, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, + 0x65, 0x6c, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x73, 0x52, 0x11, 0x73, 0x69, 0x6e, 0x67, 0x6c, + 0x65, 0x43, 0x68, 0x61, 0x6e, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x73, 0x12, 0x42, 0x0a, 0x11, + 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x5f, 0x62, 0x61, 0x63, 0x6b, 0x75, + 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, + 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x43, 0x68, 0x61, 0x6e, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x52, 0x0f, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x43, 0x68, 0x61, 0x6e, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, - 0x22, 0x19, 0x0a, 0x17, 0x43, 0x68, 0x61, 0x6e, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x45, 0x78, - 0x70, 0x6f, 0x72, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x9f, 0x01, 0x0a, 0x12, - 0x43, 0x68, 0x61, 0x6e, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, - 0x6f, 0x74, 0x12, 0x45, 0x0a, 0x13, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x5f, 0x63, 0x68, 0x61, - 0x6e, 0x5f, 0x62, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x15, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x42, - 0x61, 0x63, 0x6b, 0x75, 0x70, 0x73, 0x52, 0x11, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x43, 0x68, - 0x61, 0x6e, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x73, 0x12, 0x42, 0x0a, 0x11, 0x6d, 0x75, 0x6c, - 0x74, 0x69, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x5f, 0x62, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4d, 0x75, 0x6c, - 0x74, 0x69, 0x43, 0x68, 0x61, 0x6e, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x52, 0x0f, 0x6d, 0x75, - 0x6c, 0x74, 0x69, 0x43, 0x68, 0x61, 0x6e, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x22, 0x49, 0x0a, - 0x0e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x73, 0x12, - 0x37, 0x0a, 0x0c, 0x63, 0x68, 0x61, 0x6e, 0x5f, 0x62, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x73, 0x18, - 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, - 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x52, 0x0b, 0x63, 0x68, 0x61, - 0x6e, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x73, 0x22, 0x8e, 0x01, 0x0a, 0x18, 0x52, 0x65, 0x73, - 0x74, 0x6f, 0x72, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x3a, 0x0a, 0x0c, 0x63, 0x68, 0x61, 0x6e, 0x5f, 0x62, 0x61, - 0x63, 0x6b, 0x75, 0x70, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x6c, 0x6e, - 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x42, 0x61, 0x63, 0x6b, 0x75, - 0x70, 0x73, 0x48, 0x00, 0x52, 0x0b, 0x63, 0x68, 0x61, 0x6e, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, - 0x73, 0x12, 0x2c, 0x0a, 0x11, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x5f, - 0x62, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x0f, - 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x43, 0x68, 0x61, 0x6e, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x42, - 0x08, 0x0a, 0x06, 0x62, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x22, 0x17, 0x0a, 0x15, 0x52, 0x65, 0x73, - 0x74, 0x6f, 0x72, 0x65, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x1b, 0x0a, 0x19, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x42, 0x61, 0x63, - 0x6b, 0x75, 0x70, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x22, - 0x1a, 0x0a, 0x18, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x43, 0x68, 0x61, 0x6e, 0x42, 0x61, 0x63, - 0x6b, 0x75, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x44, 0x0a, 0x12, 0x4d, - 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, - 0x6e, 0x12, 0x16, 0x0a, 0x06, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x06, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x12, 0x16, 0x0a, 0x06, 0x61, 0x63, 0x74, - 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, - 0x6e, 0x22, 0xb0, 0x01, 0x0a, 0x13, 0x42, 0x61, 0x6b, 0x65, 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, - 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x3b, 0x0a, 0x0b, 0x70, 0x65, 0x72, - 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, - 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x50, - 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x0b, 0x70, 0x65, 0x72, 0x6d, 0x69, - 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x1e, 0x0a, 0x0b, 0x72, 0x6f, 0x6f, 0x74, 0x5f, 0x6b, - 0x65, 0x79, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x72, 0x6f, 0x6f, - 0x74, 0x4b, 0x65, 0x79, 0x49, 0x64, 0x12, 0x3c, 0x0a, 0x1a, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x5f, - 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5f, 0x70, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, - 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x18, 0x61, 0x6c, 0x6c, 0x6f, - 0x77, 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, - 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x32, 0x0a, 0x14, 0x42, 0x61, 0x6b, 0x65, 0x4d, 0x61, 0x63, 0x61, - 0x72, 0x6f, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1a, 0x0a, 0x08, - 0x6d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, - 0x6d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x22, 0x18, 0x0a, 0x16, 0x4c, 0x69, 0x73, 0x74, - 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x49, 0x44, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x22, 0x3b, 0x0a, 0x17, 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, - 0x6f, 0x6e, 0x49, 0x44, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x20, 0x0a, - 0x0c, 0x72, 0x6f, 0x6f, 0x74, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x01, 0x20, - 0x03, 0x28, 0x04, 0x52, 0x0a, 0x72, 0x6f, 0x6f, 0x74, 0x4b, 0x65, 0x79, 0x49, 0x64, 0x73, 0x22, - 0x39, 0x0a, 0x17, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, - 0x6e, 0x49, 0x44, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1e, 0x0a, 0x0b, 0x72, 0x6f, - 0x6f, 0x74, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, - 0x09, 0x72, 0x6f, 0x6f, 0x74, 0x4b, 0x65, 0x79, 0x49, 0x64, 0x22, 0x34, 0x0a, 0x18, 0x44, 0x65, - 0x6c, 0x65, 0x74, 0x65, 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x49, 0x44, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, - 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, - 0x22, 0x55, 0x0a, 0x16, 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x50, 0x65, 0x72, 0x6d, - 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x3b, 0x0a, 0x0b, 0x70, 0x65, - 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, - 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x0b, 0x70, 0x65, 0x72, 0x6d, - 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x18, 0x0a, 0x16, 0x4c, 0x69, 0x73, 0x74, 0x50, - 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x22, 0xe4, 0x01, 0x0a, 0x17, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, - 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x64, 0x0a, - 0x12, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x5f, 0x70, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, - 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x35, 0x2e, 0x6c, 0x6e, 0x72, 0x70, - 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, - 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, + 0x22, 0x49, 0x0a, 0x0e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x42, 0x61, 0x63, 0x6b, 0x75, + 0x70, 0x73, 0x12, 0x37, 0x0a, 0x0c, 0x63, 0x68, 0x61, 0x6e, 0x5f, 0x62, 0x61, 0x63, 0x6b, 0x75, + 0x70, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, + 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x52, 0x0b, + 0x63, 0x68, 0x61, 0x6e, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x73, 0x22, 0x8e, 0x01, 0x0a, 0x18, + 0x52, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x42, 0x61, 0x63, 0x6b, 0x75, + 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x3a, 0x0a, 0x0c, 0x63, 0x68, 0x61, 0x6e, + 0x5f, 0x62, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, + 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x42, 0x61, + 0x63, 0x6b, 0x75, 0x70, 0x73, 0x48, 0x00, 0x52, 0x0b, 0x63, 0x68, 0x61, 0x6e, 0x42, 0x61, 0x63, + 0x6b, 0x75, 0x70, 0x73, 0x12, 0x2c, 0x0a, 0x11, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x5f, 0x63, 0x68, + 0x61, 0x6e, 0x5f, 0x62, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x48, + 0x00, 0x52, 0x0f, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x43, 0x68, 0x61, 0x6e, 0x42, 0x61, 0x63, 0x6b, + 0x75, 0x70, 0x42, 0x08, 0x0a, 0x06, 0x62, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x22, 0x17, 0x0a, 0x15, + 0x52, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x0a, 0x19, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, + 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, + 0x6f, 0x6e, 0x22, 0x1a, 0x0a, 0x18, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x43, 0x68, 0x61, 0x6e, + 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x44, + 0x0a, 0x12, 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, + 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x16, 0x0a, 0x06, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x12, 0x16, 0x0a, 0x06, + 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x61, 0x63, + 0x74, 0x69, 0x6f, 0x6e, 0x22, 0xb0, 0x01, 0x0a, 0x13, 0x42, 0x61, 0x6b, 0x65, 0x4d, 0x61, 0x63, + 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x3b, 0x0a, 0x0b, + 0x70, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, + 0x6f, 0x6e, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x0b, 0x70, 0x65, + 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x1e, 0x0a, 0x0b, 0x72, 0x6f, 0x6f, + 0x74, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, + 0x72, 0x6f, 0x6f, 0x74, 0x4b, 0x65, 0x79, 0x49, 0x64, 0x12, 0x3c, 0x0a, 0x1a, 0x61, 0x6c, 0x6c, + 0x6f, 0x77, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5f, 0x70, 0x65, 0x72, 0x6d, + 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x18, 0x61, + 0x6c, 0x6c, 0x6f, 0x77, 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x50, 0x65, 0x72, 0x6d, + 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x32, 0x0a, 0x14, 0x42, 0x61, 0x6b, 0x65, 0x4d, + 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x1a, 0x0a, 0x08, 0x6d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x08, 0x6d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x22, 0x18, 0x0a, 0x16, 0x4c, + 0x69, 0x73, 0x74, 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x49, 0x44, 0x73, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x3b, 0x0a, 0x17, 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x61, 0x63, + 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x49, 0x44, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x20, 0x0a, 0x0c, 0x72, 0x6f, 0x6f, 0x74, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x69, 0x64, 0x73, + 0x18, 0x01, 0x20, 0x03, 0x28, 0x04, 0x52, 0x0a, 0x72, 0x6f, 0x6f, 0x74, 0x4b, 0x65, 0x79, 0x49, + 0x64, 0x73, 0x22, 0x39, 0x0a, 0x17, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4d, 0x61, 0x63, 0x61, + 0x72, 0x6f, 0x6f, 0x6e, 0x49, 0x44, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1e, 0x0a, + 0x0b, 0x72, 0x6f, 0x6f, 0x74, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x04, 0x52, 0x09, 0x72, 0x6f, 0x6f, 0x74, 0x4b, 0x65, 0x79, 0x49, 0x64, 0x22, 0x34, 0x0a, + 0x18, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x49, + 0x44, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x64, 0x65, 0x6c, + 0x65, 0x74, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x64, 0x65, 0x6c, 0x65, + 0x74, 0x65, 0x64, 0x22, 0x55, 0x0a, 0x16, 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x50, + 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x3b, 0x0a, + 0x0b, 0x70, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4d, 0x61, 0x63, 0x61, 0x72, + 0x6f, 0x6f, 0x6e, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x0b, 0x70, + 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x18, 0x0a, 0x16, 0x4c, 0x69, + 0x73, 0x74, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x22, 0xe4, 0x01, 0x0a, 0x17, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x65, 0x72, + 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x64, 0x0a, 0x12, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x5f, 0x70, 0x65, 0x72, 0x6d, 0x69, + 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x35, 0x2e, 0x6c, + 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, + 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x4d, 0x65, 0x74, + 0x68, 0x6f, 0x64, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x45, 0x6e, + 0x74, 0x72, 0x79, 0x52, 0x11, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x50, 0x65, 0x72, 0x6d, 0x69, + 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x1a, 0x63, 0x0a, 0x16, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, - 0x52, 0x11, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, - 0x6f, 0x6e, 0x73, 0x1a, 0x63, 0x0a, 0x16, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x50, 0x65, 0x72, - 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, - 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, - 0x33, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, - 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x50, - 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x05, 0x76, - 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xcc, 0x08, 0x0a, 0x07, 0x46, 0x61, 0x69, - 0x6c, 0x75, 0x72, 0x65, 0x12, 0x2e, 0x0a, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x0e, 0x32, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x61, 0x69, 0x6c, 0x75, - 0x72, 0x65, 0x2e, 0x46, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x43, 0x6f, 0x64, 0x65, 0x52, 0x04, - 0x63, 0x6f, 0x64, 0x65, 0x12, 0x3b, 0x0a, 0x0e, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x5f, - 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x6c, - 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x55, 0x70, 0x64, 0x61, - 0x74, 0x65, 0x52, 0x0d, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x55, 0x70, 0x64, 0x61, 0x74, - 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x68, 0x74, 0x6c, 0x63, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x04, - 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x68, 0x74, 0x6c, 0x63, 0x4d, 0x73, 0x61, 0x74, 0x12, 0x22, - 0x0a, 0x0d, 0x6f, 0x6e, 0x69, 0x6f, 0x6e, 0x5f, 0x73, 0x68, 0x61, 0x5f, 0x32, 0x35, 0x36, 0x18, - 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x6f, 0x6e, 0x69, 0x6f, 0x6e, 0x53, 0x68, 0x61, 0x32, - 0x35, 0x36, 0x12, 0x1f, 0x0a, 0x0b, 0x63, 0x6c, 0x74, 0x76, 0x5f, 0x65, 0x78, 0x70, 0x69, 0x72, - 0x79, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x63, 0x6c, 0x74, 0x76, 0x45, 0x78, 0x70, - 0x69, 0x72, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x18, 0x07, 0x20, 0x01, - 0x28, 0x0d, 0x52, 0x05, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x12, 0x30, 0x0a, 0x14, 0x66, 0x61, 0x69, - 0x6c, 0x75, 0x72, 0x65, 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x69, 0x6e, 0x64, 0x65, - 0x78, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x12, 0x66, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, - 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x16, 0x0a, 0x06, 0x68, - 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x68, 0x65, 0x69, - 0x67, 0x68, 0x74, 0x22, 0x8b, 0x06, 0x0a, 0x0b, 0x46, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x43, - 0x6f, 0x64, 0x65, 0x12, 0x0c, 0x0a, 0x08, 0x52, 0x45, 0x53, 0x45, 0x52, 0x56, 0x45, 0x44, 0x10, - 0x00, 0x12, 0x28, 0x0a, 0x24, 0x49, 0x4e, 0x43, 0x4f, 0x52, 0x52, 0x45, 0x43, 0x54, 0x5f, 0x4f, - 0x52, 0x5f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x5f, 0x50, 0x41, 0x59, 0x4d, 0x45, 0x4e, - 0x54, 0x5f, 0x44, 0x45, 0x54, 0x41, 0x49, 0x4c, 0x53, 0x10, 0x01, 0x12, 0x1c, 0x0a, 0x18, 0x49, - 0x4e, 0x43, 0x4f, 0x52, 0x52, 0x45, 0x43, 0x54, 0x5f, 0x50, 0x41, 0x59, 0x4d, 0x45, 0x4e, 0x54, - 0x5f, 0x41, 0x4d, 0x4f, 0x55, 0x4e, 0x54, 0x10, 0x02, 0x12, 0x1f, 0x0a, 0x1b, 0x46, 0x49, 0x4e, - 0x41, 0x4c, 0x5f, 0x49, 0x4e, 0x43, 0x4f, 0x52, 0x52, 0x45, 0x43, 0x54, 0x5f, 0x43, 0x4c, 0x54, - 0x56, 0x5f, 0x45, 0x58, 0x50, 0x49, 0x52, 0x59, 0x10, 0x03, 0x12, 0x1f, 0x0a, 0x1b, 0x46, 0x49, - 0x4e, 0x41, 0x4c, 0x5f, 0x49, 0x4e, 0x43, 0x4f, 0x52, 0x52, 0x45, 0x43, 0x54, 0x5f, 0x48, 0x54, - 0x4c, 0x43, 0x5f, 0x41, 0x4d, 0x4f, 0x55, 0x4e, 0x54, 0x10, 0x04, 0x12, 0x19, 0x0a, 0x15, 0x46, - 0x49, 0x4e, 0x41, 0x4c, 0x5f, 0x45, 0x58, 0x50, 0x49, 0x52, 0x59, 0x5f, 0x54, 0x4f, 0x4f, 0x5f, - 0x53, 0x4f, 0x4f, 0x4e, 0x10, 0x05, 0x12, 0x11, 0x0a, 0x0d, 0x49, 0x4e, 0x56, 0x41, 0x4c, 0x49, - 0x44, 0x5f, 0x52, 0x45, 0x41, 0x4c, 0x4d, 0x10, 0x06, 0x12, 0x13, 0x0a, 0x0f, 0x45, 0x58, 0x50, - 0x49, 0x52, 0x59, 0x5f, 0x54, 0x4f, 0x4f, 0x5f, 0x53, 0x4f, 0x4f, 0x4e, 0x10, 0x07, 0x12, 0x19, - 0x0a, 0x15, 0x49, 0x4e, 0x56, 0x41, 0x4c, 0x49, 0x44, 0x5f, 0x4f, 0x4e, 0x49, 0x4f, 0x4e, 0x5f, - 0x56, 0x45, 0x52, 0x53, 0x49, 0x4f, 0x4e, 0x10, 0x08, 0x12, 0x16, 0x0a, 0x12, 0x49, 0x4e, 0x56, - 0x41, 0x4c, 0x49, 0x44, 0x5f, 0x4f, 0x4e, 0x49, 0x4f, 0x4e, 0x5f, 0x48, 0x4d, 0x41, 0x43, 0x10, - 0x09, 0x12, 0x15, 0x0a, 0x11, 0x49, 0x4e, 0x56, 0x41, 0x4c, 0x49, 0x44, 0x5f, 0x4f, 0x4e, 0x49, - 0x4f, 0x4e, 0x5f, 0x4b, 0x45, 0x59, 0x10, 0x0a, 0x12, 0x18, 0x0a, 0x14, 0x41, 0x4d, 0x4f, 0x55, - 0x4e, 0x54, 0x5f, 0x42, 0x45, 0x4c, 0x4f, 0x57, 0x5f, 0x4d, 0x49, 0x4e, 0x49, 0x4d, 0x55, 0x4d, - 0x10, 0x0b, 0x12, 0x14, 0x0a, 0x10, 0x46, 0x45, 0x45, 0x5f, 0x49, 0x4e, 0x53, 0x55, 0x46, 0x46, - 0x49, 0x43, 0x49, 0x45, 0x4e, 0x54, 0x10, 0x0c, 0x12, 0x19, 0x0a, 0x15, 0x49, 0x4e, 0x43, 0x4f, - 0x52, 0x52, 0x45, 0x43, 0x54, 0x5f, 0x43, 0x4c, 0x54, 0x56, 0x5f, 0x45, 0x58, 0x50, 0x49, 0x52, - 0x59, 0x10, 0x0d, 0x12, 0x14, 0x0a, 0x10, 0x43, 0x48, 0x41, 0x4e, 0x4e, 0x45, 0x4c, 0x5f, 0x44, - 0x49, 0x53, 0x41, 0x42, 0x4c, 0x45, 0x44, 0x10, 0x0e, 0x12, 0x1d, 0x0a, 0x19, 0x54, 0x45, 0x4d, - 0x50, 0x4f, 0x52, 0x41, 0x52, 0x59, 0x5f, 0x43, 0x48, 0x41, 0x4e, 0x4e, 0x45, 0x4c, 0x5f, 0x46, - 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x10, 0x0f, 0x12, 0x21, 0x0a, 0x1d, 0x52, 0x45, 0x51, 0x55, - 0x49, 0x52, 0x45, 0x44, 0x5f, 0x4e, 0x4f, 0x44, 0x45, 0x5f, 0x46, 0x45, 0x41, 0x54, 0x55, 0x52, - 0x45, 0x5f, 0x4d, 0x49, 0x53, 0x53, 0x49, 0x4e, 0x47, 0x10, 0x10, 0x12, 0x24, 0x0a, 0x20, 0x52, - 0x45, 0x51, 0x55, 0x49, 0x52, 0x45, 0x44, 0x5f, 0x43, 0x48, 0x41, 0x4e, 0x4e, 0x45, 0x4c, 0x5f, - 0x46, 0x45, 0x41, 0x54, 0x55, 0x52, 0x45, 0x5f, 0x4d, 0x49, 0x53, 0x53, 0x49, 0x4e, 0x47, 0x10, - 0x11, 0x12, 0x15, 0x0a, 0x11, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x5f, 0x4e, 0x45, 0x58, - 0x54, 0x5f, 0x50, 0x45, 0x45, 0x52, 0x10, 0x12, 0x12, 0x1a, 0x0a, 0x16, 0x54, 0x45, 0x4d, 0x50, - 0x4f, 0x52, 0x41, 0x52, 0x59, 0x5f, 0x4e, 0x4f, 0x44, 0x45, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x55, - 0x52, 0x45, 0x10, 0x13, 0x12, 0x1a, 0x0a, 0x16, 0x50, 0x45, 0x52, 0x4d, 0x41, 0x4e, 0x45, 0x4e, - 0x54, 0x5f, 0x4e, 0x4f, 0x44, 0x45, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x10, 0x14, - 0x12, 0x1d, 0x0a, 0x19, 0x50, 0x45, 0x52, 0x4d, 0x41, 0x4e, 0x45, 0x4e, 0x54, 0x5f, 0x43, 0x48, - 0x41, 0x4e, 0x4e, 0x45, 0x4c, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x10, 0x15, 0x12, - 0x12, 0x0a, 0x0e, 0x45, 0x58, 0x50, 0x49, 0x52, 0x59, 0x5f, 0x54, 0x4f, 0x4f, 0x5f, 0x46, 0x41, - 0x52, 0x10, 0x16, 0x12, 0x0f, 0x0a, 0x0b, 0x4d, 0x50, 0x50, 0x5f, 0x54, 0x49, 0x4d, 0x45, 0x4f, - 0x55, 0x54, 0x10, 0x17, 0x12, 0x19, 0x0a, 0x15, 0x49, 0x4e, 0x56, 0x41, 0x4c, 0x49, 0x44, 0x5f, - 0x4f, 0x4e, 0x49, 0x4f, 0x4e, 0x5f, 0x50, 0x41, 0x59, 0x4c, 0x4f, 0x41, 0x44, 0x10, 0x18, 0x12, - 0x1a, 0x0a, 0x16, 0x49, 0x4e, 0x56, 0x41, 0x4c, 0x49, 0x44, 0x5f, 0x4f, 0x4e, 0x49, 0x4f, 0x4e, - 0x5f, 0x42, 0x4c, 0x49, 0x4e, 0x44, 0x49, 0x4e, 0x47, 0x10, 0x19, 0x12, 0x15, 0x0a, 0x10, 0x49, - 0x4e, 0x54, 0x45, 0x52, 0x4e, 0x41, 0x4c, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x10, - 0xe5, 0x07, 0x12, 0x14, 0x0a, 0x0f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x5f, 0x46, 0x41, - 0x49, 0x4c, 0x55, 0x52, 0x45, 0x10, 0xe6, 0x07, 0x12, 0x17, 0x0a, 0x12, 0x55, 0x4e, 0x52, 0x45, - 0x41, 0x44, 0x41, 0x42, 0x4c, 0x45, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x10, 0xe7, - 0x07, 0x4a, 0x04, 0x08, 0x02, 0x10, 0x03, 0x22, 0xb3, 0x03, 0x0a, 0x0d, 0x43, 0x68, 0x61, 0x6e, - 0x6e, 0x65, 0x6c, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x69, 0x67, - 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x73, 0x69, - 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x68, 0x61, 0x69, 0x6e, - 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x63, 0x68, 0x61, - 0x69, 0x6e, 0x48, 0x61, 0x73, 0x68, 0x12, 0x1b, 0x0a, 0x07, 0x63, 0x68, 0x61, 0x6e, 0x5f, 0x69, - 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x42, 0x02, 0x30, 0x01, 0x52, 0x06, 0x63, 0x68, 0x61, - 0x6e, 0x49, 0x64, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, - 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, - 0x70, 0x12, 0x23, 0x0a, 0x0d, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x66, 0x6c, 0x61, - 0x67, 0x73, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0c, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, - 0x65, 0x46, 0x6c, 0x61, 0x67, 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, - 0x6c, 0x5f, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0c, 0x63, - 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x46, 0x6c, 0x61, 0x67, 0x73, 0x12, 0x26, 0x0a, 0x0f, 0x74, - 0x69, 0x6d, 0x65, 0x5f, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x18, 0x06, - 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0d, 0x74, 0x69, 0x6d, 0x65, 0x4c, 0x6f, 0x63, 0x6b, 0x44, 0x65, - 0x6c, 0x74, 0x61, 0x12, 0x2a, 0x0a, 0x11, 0x68, 0x74, 0x6c, 0x63, 0x5f, 0x6d, 0x69, 0x6e, 0x69, - 0x6d, 0x75, 0x6d, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0f, - 0x68, 0x74, 0x6c, 0x63, 0x4d, 0x69, 0x6e, 0x69, 0x6d, 0x75, 0x6d, 0x4d, 0x73, 0x61, 0x74, 0x12, - 0x19, 0x0a, 0x08, 0x62, 0x61, 0x73, 0x65, 0x5f, 0x66, 0x65, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, - 0x0d, 0x52, 0x07, 0x62, 0x61, 0x73, 0x65, 0x46, 0x65, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x66, 0x65, - 0x65, 0x5f, 0x72, 0x61, 0x74, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x66, 0x65, - 0x65, 0x52, 0x61, 0x74, 0x65, 0x12, 0x2a, 0x0a, 0x11, 0x68, 0x74, 0x6c, 0x63, 0x5f, 0x6d, 0x61, - 0x78, 0x69, 0x6d, 0x75, 0x6d, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x04, - 0x52, 0x0f, 0x68, 0x74, 0x6c, 0x63, 0x4d, 0x61, 0x78, 0x69, 0x6d, 0x75, 0x6d, 0x4d, 0x73, 0x61, - 0x74, 0x12, 0x2a, 0x0a, 0x11, 0x65, 0x78, 0x74, 0x72, 0x61, 0x5f, 0x6f, 0x70, 0x61, 0x71, 0x75, - 0x65, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0f, 0x65, 0x78, - 0x74, 0x72, 0x61, 0x4f, 0x70, 0x61, 0x71, 0x75, 0x65, 0x44, 0x61, 0x74, 0x61, 0x22, 0x5d, 0x0a, - 0x0a, 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x6e, - 0x6f, 0x6e, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x6e, 0x6f, 0x6e, 0x63, - 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x49, 0x64, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x49, 0x64, 0x12, - 0x1b, 0x0a, 0x03, 0x6f, 0x70, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x6c, - 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4f, 0x70, 0x52, 0x03, 0x6f, 0x70, 0x73, 0x22, 0x36, 0x0a, 0x02, - 0x4f, 0x70, 0x12, 0x16, 0x0a, 0x06, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x06, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x63, - 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x61, 0x63, 0x74, - 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x8e, 0x01, 0x0a, 0x13, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x4d, 0x61, - 0x63, 0x50, 0x65, 0x72, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, - 0x6d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, - 0x6d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x12, 0x3b, 0x0a, 0x0b, 0x70, 0x65, 0x72, 0x6d, - 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, - 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x50, 0x65, - 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x0b, 0x70, 0x65, 0x72, 0x6d, 0x69, 0x73, - 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x1e, 0x0a, 0x0a, 0x66, 0x75, 0x6c, 0x6c, 0x4d, 0x65, 0x74, - 0x68, 0x6f, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x66, 0x75, 0x6c, 0x6c, 0x4d, - 0x65, 0x74, 0x68, 0x6f, 0x64, 0x22, 0x2c, 0x0a, 0x14, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x4d, 0x61, - 0x63, 0x50, 0x65, 0x72, 0x6d, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x14, 0x0a, - 0x05, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x76, 0x61, - 0x6c, 0x69, 0x64, 0x22, 0xf4, 0x02, 0x0a, 0x14, 0x52, 0x50, 0x43, 0x4d, 0x69, 0x64, 0x64, 0x6c, - 0x65, 0x77, 0x61, 0x72, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, - 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, - 0x52, 0x09, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x49, 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x72, - 0x61, 0x77, 0x5f, 0x6d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x0c, 0x52, 0x0b, 0x72, 0x61, 0x77, 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x12, 0x36, - 0x0a, 0x17, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5f, 0x63, 0x61, 0x76, 0x65, 0x61, 0x74, 0x5f, - 0x63, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x15, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x43, 0x61, 0x76, 0x65, 0x61, 0x74, 0x43, 0x6f, 0x6e, - 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x34, 0x0a, 0x0b, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, - 0x5f, 0x61, 0x75, 0x74, 0x68, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x6c, 0x6e, - 0x72, 0x70, 0x63, 0x2e, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x41, 0x75, 0x74, 0x68, 0x48, 0x00, - 0x52, 0x0a, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x41, 0x75, 0x74, 0x68, 0x12, 0x2d, 0x0a, 0x07, - 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, - 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x50, 0x43, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, - 0x48, 0x00, 0x52, 0x07, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2f, 0x0a, 0x08, 0x72, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, - 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x50, 0x43, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, - 0x48, 0x00, 0x52, 0x08, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x23, 0x0a, 0x0c, - 0x72, 0x65, 0x67, 0x5f, 0x63, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x18, 0x08, 0x20, 0x01, - 0x28, 0x08, 0x48, 0x00, 0x52, 0x0b, 0x72, 0x65, 0x67, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, - 0x65, 0x12, 0x15, 0x0a, 0x06, 0x6d, 0x73, 0x67, 0x5f, 0x69, 0x64, 0x18, 0x07, 0x20, 0x01, 0x28, - 0x04, 0x52, 0x05, 0x6d, 0x73, 0x67, 0x49, 0x64, 0x42, 0x10, 0x0a, 0x0e, 0x69, 0x6e, 0x74, 0x65, - 0x72, 0x63, 0x65, 0x70, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x22, 0x34, 0x0a, 0x0a, 0x53, 0x74, - 0x72, 0x65, 0x61, 0x6d, 0x41, 0x75, 0x74, 0x68, 0x12, 0x26, 0x0a, 0x0f, 0x6d, 0x65, 0x74, 0x68, - 0x6f, 0x64, 0x5f, 0x66, 0x75, 0x6c, 0x6c, 0x5f, 0x75, 0x72, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x0d, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x46, 0x75, 0x6c, 0x6c, 0x55, 0x72, 0x69, - 0x22, 0xab, 0x01, 0x0a, 0x0a, 0x52, 0x50, 0x43, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, - 0x26, 0x0a, 0x0f, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x5f, 0x66, 0x75, 0x6c, 0x6c, 0x5f, 0x75, - 0x72, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, - 0x46, 0x75, 0x6c, 0x6c, 0x55, 0x72, 0x69, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x74, 0x72, 0x65, 0x61, - 0x6d, 0x5f, 0x72, 0x70, 0x63, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x73, 0x74, 0x72, - 0x65, 0x61, 0x6d, 0x52, 0x70, 0x63, 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x6e, - 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x79, 0x70, 0x65, 0x4e, - 0x61, 0x6d, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, - 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, - 0x7a, 0x65, 0x64, 0x12, 0x19, 0x0a, 0x08, 0x69, 0x73, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, - 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x69, 0x73, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x22, 0xc0, - 0x01, 0x0a, 0x15, 0x52, 0x50, 0x43, 0x4d, 0x69, 0x64, 0x64, 0x6c, 0x65, 0x77, 0x61, 0x72, 0x65, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1c, 0x0a, 0x0a, 0x72, 0x65, 0x66, 0x5f, - 0x6d, 0x73, 0x67, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x72, 0x65, - 0x66, 0x4d, 0x73, 0x67, 0x49, 0x64, 0x12, 0x3b, 0x0a, 0x08, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, - 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, - 0x2e, 0x4d, 0x69, 0x64, 0x64, 0x6c, 0x65, 0x77, 0x61, 0x72, 0x65, 0x52, 0x65, 0x67, 0x69, 0x73, - 0x74, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x00, 0x52, 0x08, 0x72, 0x65, 0x67, 0x69, 0x73, - 0x74, 0x65, 0x72, 0x12, 0x36, 0x0a, 0x08, 0x66, 0x65, 0x65, 0x64, 0x62, 0x61, 0x63, 0x6b, 0x18, - 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x49, 0x6e, - 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x46, 0x65, 0x65, 0x64, 0x62, 0x61, 0x63, 0x6b, 0x48, - 0x00, 0x52, 0x08, 0x66, 0x65, 0x65, 0x64, 0x62, 0x61, 0x63, 0x6b, 0x42, 0x14, 0x0a, 0x12, 0x6d, - 0x69, 0x64, 0x64, 0x6c, 0x65, 0x77, 0x61, 0x72, 0x65, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, - 0x65, 0x22, 0xa6, 0x01, 0x0a, 0x16, 0x4d, 0x69, 0x64, 0x64, 0x6c, 0x65, 0x77, 0x61, 0x72, 0x65, - 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x27, 0x0a, 0x0f, - 0x6d, 0x69, 0x64, 0x64, 0x6c, 0x65, 0x77, 0x61, 0x72, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x6d, 0x69, 0x64, 0x64, 0x6c, 0x65, 0x77, 0x61, 0x72, - 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x3d, 0x0a, 0x1b, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5f, - 0x6d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x5f, 0x63, 0x61, 0x76, 0x65, 0x61, 0x74, 0x5f, - 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x18, 0x63, 0x75, 0x73, 0x74, - 0x6f, 0x6d, 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x43, 0x61, 0x76, 0x65, 0x61, 0x74, - 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x24, 0x0a, 0x0e, 0x72, 0x65, 0x61, 0x64, 0x5f, 0x6f, 0x6e, 0x6c, - 0x79, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x72, 0x65, - 0x61, 0x64, 0x4f, 0x6e, 0x6c, 0x79, 0x4d, 0x6f, 0x64, 0x65, 0x22, 0x8b, 0x01, 0x0a, 0x11, 0x49, - 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x46, 0x65, 0x65, 0x64, 0x62, 0x61, 0x63, 0x6b, - 0x12, 0x14, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x29, 0x0a, 0x10, 0x72, 0x65, 0x70, 0x6c, 0x61, 0x63, - 0x65, 0x5f, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, - 0x52, 0x0f, 0x72, 0x65, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x35, 0x0a, 0x16, 0x72, 0x65, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x6d, 0x65, 0x6e, 0x74, - 0x5f, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x0c, 0x52, 0x15, 0x72, 0x65, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x53, 0x65, - 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x2a, 0xcb, 0x02, 0x0a, 0x10, 0x4f, 0x75, 0x74, - 0x70, 0x75, 0x74, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1b, 0x0a, - 0x17, 0x53, 0x43, 0x52, 0x49, 0x50, 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x50, 0x55, 0x42, - 0x4b, 0x45, 0x59, 0x5f, 0x48, 0x41, 0x53, 0x48, 0x10, 0x00, 0x12, 0x1b, 0x0a, 0x17, 0x53, 0x43, - 0x52, 0x49, 0x50, 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x53, 0x43, 0x52, 0x49, 0x50, 0x54, - 0x5f, 0x48, 0x41, 0x53, 0x48, 0x10, 0x01, 0x12, 0x26, 0x0a, 0x22, 0x53, 0x43, 0x52, 0x49, 0x50, - 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x57, 0x49, 0x54, 0x4e, 0x45, 0x53, 0x53, 0x5f, 0x56, - 0x30, 0x5f, 0x50, 0x55, 0x42, 0x4b, 0x45, 0x59, 0x5f, 0x48, 0x41, 0x53, 0x48, 0x10, 0x02, 0x12, - 0x26, 0x0a, 0x22, 0x53, 0x43, 0x52, 0x49, 0x50, 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x57, - 0x49, 0x54, 0x4e, 0x45, 0x53, 0x53, 0x5f, 0x56, 0x30, 0x5f, 0x53, 0x43, 0x52, 0x49, 0x50, 0x54, - 0x5f, 0x48, 0x41, 0x53, 0x48, 0x10, 0x03, 0x12, 0x16, 0x0a, 0x12, 0x53, 0x43, 0x52, 0x49, 0x50, - 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x50, 0x55, 0x42, 0x4b, 0x45, 0x59, 0x10, 0x04, 0x12, - 0x18, 0x0a, 0x14, 0x53, 0x43, 0x52, 0x49, 0x50, 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x4d, - 0x55, 0x4c, 0x54, 0x49, 0x53, 0x49, 0x47, 0x10, 0x05, 0x12, 0x18, 0x0a, 0x14, 0x53, 0x43, 0x52, - 0x49, 0x50, 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x4e, 0x55, 0x4c, 0x4c, 0x44, 0x41, 0x54, - 0x41, 0x10, 0x06, 0x12, 0x1c, 0x0a, 0x18, 0x53, 0x43, 0x52, 0x49, 0x50, 0x54, 0x5f, 0x54, 0x59, - 0x50, 0x45, 0x5f, 0x4e, 0x4f, 0x4e, 0x5f, 0x53, 0x54, 0x41, 0x4e, 0x44, 0x41, 0x52, 0x44, 0x10, - 0x07, 0x12, 0x1f, 0x0a, 0x1b, 0x53, 0x43, 0x52, 0x49, 0x50, 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, - 0x5f, 0x57, 0x49, 0x54, 0x4e, 0x45, 0x53, 0x53, 0x5f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, - 0x10, 0x08, 0x12, 0x22, 0x0a, 0x1e, 0x53, 0x43, 0x52, 0x49, 0x50, 0x54, 0x5f, 0x54, 0x59, 0x50, - 0x45, 0x5f, 0x57, 0x49, 0x54, 0x4e, 0x45, 0x53, 0x53, 0x5f, 0x56, 0x31, 0x5f, 0x54, 0x41, 0x50, - 0x52, 0x4f, 0x4f, 0x54, 0x10, 0x09, 0x2a, 0x62, 0x0a, 0x15, 0x43, 0x6f, 0x69, 0x6e, 0x53, 0x65, - 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x12, - 0x1e, 0x0a, 0x1a, 0x53, 0x54, 0x52, 0x41, 0x54, 0x45, 0x47, 0x59, 0x5f, 0x55, 0x53, 0x45, 0x5f, - 0x47, 0x4c, 0x4f, 0x42, 0x41, 0x4c, 0x5f, 0x43, 0x4f, 0x4e, 0x46, 0x49, 0x47, 0x10, 0x00, 0x12, - 0x14, 0x0a, 0x10, 0x53, 0x54, 0x52, 0x41, 0x54, 0x45, 0x47, 0x59, 0x5f, 0x4c, 0x41, 0x52, 0x47, - 0x45, 0x53, 0x54, 0x10, 0x01, 0x12, 0x13, 0x0a, 0x0f, 0x53, 0x54, 0x52, 0x41, 0x54, 0x45, 0x47, - 0x59, 0x5f, 0x52, 0x41, 0x4e, 0x44, 0x4f, 0x4d, 0x10, 0x02, 0x2a, 0xac, 0x01, 0x0a, 0x0b, 0x41, - 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x54, 0x79, 0x70, 0x65, 0x12, 0x17, 0x0a, 0x13, 0x57, 0x49, - 0x54, 0x4e, 0x45, 0x53, 0x53, 0x5f, 0x50, 0x55, 0x42, 0x4b, 0x45, 0x59, 0x5f, 0x48, 0x41, 0x53, - 0x48, 0x10, 0x00, 0x12, 0x16, 0x0a, 0x12, 0x4e, 0x45, 0x53, 0x54, 0x45, 0x44, 0x5f, 0x50, 0x55, - 0x42, 0x4b, 0x45, 0x59, 0x5f, 0x48, 0x41, 0x53, 0x48, 0x10, 0x01, 0x12, 0x1e, 0x0a, 0x1a, 0x55, - 0x4e, 0x55, 0x53, 0x45, 0x44, 0x5f, 0x57, 0x49, 0x54, 0x4e, 0x45, 0x53, 0x53, 0x5f, 0x50, 0x55, - 0x42, 0x4b, 0x45, 0x59, 0x5f, 0x48, 0x41, 0x53, 0x48, 0x10, 0x02, 0x12, 0x1d, 0x0a, 0x19, 0x55, - 0x4e, 0x55, 0x53, 0x45, 0x44, 0x5f, 0x4e, 0x45, 0x53, 0x54, 0x45, 0x44, 0x5f, 0x50, 0x55, 0x42, - 0x4b, 0x45, 0x59, 0x5f, 0x48, 0x41, 0x53, 0x48, 0x10, 0x03, 0x12, 0x12, 0x0a, 0x0e, 0x54, 0x41, - 0x50, 0x52, 0x4f, 0x4f, 0x54, 0x5f, 0x50, 0x55, 0x42, 0x4b, 0x45, 0x59, 0x10, 0x04, 0x12, 0x19, - 0x0a, 0x15, 0x55, 0x4e, 0x55, 0x53, 0x45, 0x44, 0x5f, 0x54, 0x41, 0x50, 0x52, 0x4f, 0x4f, 0x54, - 0x5f, 0x50, 0x55, 0x42, 0x4b, 0x45, 0x59, 0x10, 0x05, 0x2a, 0x8c, 0x01, 0x0a, 0x0e, 0x43, 0x6f, - 0x6d, 0x6d, 0x69, 0x74, 0x6d, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1b, 0x0a, 0x17, - 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x5f, 0x43, 0x4f, 0x4d, 0x4d, 0x49, 0x54, 0x4d, 0x45, - 0x4e, 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x4c, 0x45, 0x47, - 0x41, 0x43, 0x59, 0x10, 0x01, 0x12, 0x15, 0x0a, 0x11, 0x53, 0x54, 0x41, 0x54, 0x49, 0x43, 0x5f, - 0x52, 0x45, 0x4d, 0x4f, 0x54, 0x45, 0x5f, 0x4b, 0x45, 0x59, 0x10, 0x02, 0x12, 0x0b, 0x0a, 0x07, - 0x41, 0x4e, 0x43, 0x48, 0x4f, 0x52, 0x53, 0x10, 0x03, 0x12, 0x19, 0x0a, 0x15, 0x53, 0x43, 0x52, - 0x49, 0x50, 0x54, 0x5f, 0x45, 0x4e, 0x46, 0x4f, 0x52, 0x43, 0x45, 0x44, 0x5f, 0x4c, 0x45, 0x41, - 0x53, 0x45, 0x10, 0x04, 0x12, 0x12, 0x0a, 0x0e, 0x53, 0x49, 0x4d, 0x50, 0x4c, 0x45, 0x5f, 0x54, - 0x41, 0x50, 0x52, 0x4f, 0x4f, 0x54, 0x10, 0x05, 0x2a, 0x61, 0x0a, 0x09, 0x49, 0x6e, 0x69, 0x74, - 0x69, 0x61, 0x74, 0x6f, 0x72, 0x12, 0x15, 0x0a, 0x11, 0x49, 0x4e, 0x49, 0x54, 0x49, 0x41, 0x54, - 0x4f, 0x52, 0x5f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x13, 0x0a, 0x0f, - 0x49, 0x4e, 0x49, 0x54, 0x49, 0x41, 0x54, 0x4f, 0x52, 0x5f, 0x4c, 0x4f, 0x43, 0x41, 0x4c, 0x10, - 0x01, 0x12, 0x14, 0x0a, 0x10, 0x49, 0x4e, 0x49, 0x54, 0x49, 0x41, 0x54, 0x4f, 0x52, 0x5f, 0x52, - 0x45, 0x4d, 0x4f, 0x54, 0x45, 0x10, 0x02, 0x12, 0x12, 0x0a, 0x0e, 0x49, 0x4e, 0x49, 0x54, 0x49, - 0x41, 0x54, 0x4f, 0x52, 0x5f, 0x42, 0x4f, 0x54, 0x48, 0x10, 0x03, 0x2a, 0x60, 0x0a, 0x0e, 0x52, - 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x12, 0x10, 0x0a, - 0x0c, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, - 0x0a, 0x0a, 0x06, 0x41, 0x4e, 0x43, 0x48, 0x4f, 0x52, 0x10, 0x01, 0x12, 0x11, 0x0a, 0x0d, 0x49, - 0x4e, 0x43, 0x4f, 0x4d, 0x49, 0x4e, 0x47, 0x5f, 0x48, 0x54, 0x4c, 0x43, 0x10, 0x02, 0x12, 0x11, - 0x0a, 0x0d, 0x4f, 0x55, 0x54, 0x47, 0x4f, 0x49, 0x4e, 0x47, 0x5f, 0x48, 0x54, 0x4c, 0x43, 0x10, - 0x03, 0x12, 0x0a, 0x0a, 0x06, 0x43, 0x4f, 0x4d, 0x4d, 0x49, 0x54, 0x10, 0x04, 0x2a, 0x71, 0x0a, - 0x11, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x4f, 0x75, 0x74, 0x63, 0x6f, - 0x6d, 0x65, 0x12, 0x13, 0x0a, 0x0f, 0x4f, 0x55, 0x54, 0x43, 0x4f, 0x4d, 0x45, 0x5f, 0x55, 0x4e, - 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x43, 0x4c, 0x41, 0x49, 0x4d, - 0x45, 0x44, 0x10, 0x01, 0x12, 0x0d, 0x0a, 0x09, 0x55, 0x4e, 0x43, 0x4c, 0x41, 0x49, 0x4d, 0x45, - 0x44, 0x10, 0x02, 0x12, 0x0d, 0x0a, 0x09, 0x41, 0x42, 0x41, 0x4e, 0x44, 0x4f, 0x4e, 0x45, 0x44, - 0x10, 0x03, 0x12, 0x0f, 0x0a, 0x0b, 0x46, 0x49, 0x52, 0x53, 0x54, 0x5f, 0x53, 0x54, 0x41, 0x47, - 0x45, 0x10, 0x04, 0x12, 0x0b, 0x0a, 0x07, 0x54, 0x49, 0x4d, 0x45, 0x4f, 0x55, 0x54, 0x10, 0x05, - 0x2a, 0x39, 0x0a, 0x0e, 0x4e, 0x6f, 0x64, 0x65, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x54, 0x79, - 0x70, 0x65, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, - 0x1a, 0x0a, 0x16, 0x42, 0x45, 0x54, 0x57, 0x45, 0x45, 0x4e, 0x4e, 0x45, 0x53, 0x53, 0x5f, 0x43, - 0x45, 0x4e, 0x54, 0x52, 0x41, 0x4c, 0x49, 0x54, 0x59, 0x10, 0x01, 0x2a, 0x3b, 0x0a, 0x10, 0x49, - 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x48, 0x54, 0x4c, 0x43, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, - 0x0c, 0x0a, 0x08, 0x41, 0x43, 0x43, 0x45, 0x50, 0x54, 0x45, 0x44, 0x10, 0x00, 0x12, 0x0b, 0x0a, - 0x07, 0x53, 0x45, 0x54, 0x54, 0x4c, 0x45, 0x44, 0x10, 0x01, 0x12, 0x0c, 0x0a, 0x08, 0x43, 0x41, - 0x4e, 0x43, 0x45, 0x4c, 0x45, 0x44, 0x10, 0x02, 0x2a, 0xf6, 0x01, 0x0a, 0x14, 0x50, 0x61, 0x79, - 0x6d, 0x65, 0x6e, 0x74, 0x46, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x52, 0x65, 0x61, 0x73, 0x6f, - 0x6e, 0x12, 0x17, 0x0a, 0x13, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x52, 0x45, 0x41, - 0x53, 0x4f, 0x4e, 0x5f, 0x4e, 0x4f, 0x4e, 0x45, 0x10, 0x00, 0x12, 0x1a, 0x0a, 0x16, 0x46, 0x41, - 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x54, 0x49, 0x4d, - 0x45, 0x4f, 0x55, 0x54, 0x10, 0x01, 0x12, 0x1b, 0x0a, 0x17, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, - 0x45, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x4e, 0x4f, 0x5f, 0x52, 0x4f, 0x55, 0x54, - 0x45, 0x10, 0x02, 0x12, 0x18, 0x0a, 0x14, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x52, - 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, 0x03, 0x12, 0x2c, 0x0a, - 0x28, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, - 0x49, 0x4e, 0x43, 0x4f, 0x52, 0x52, 0x45, 0x43, 0x54, 0x5f, 0x50, 0x41, 0x59, 0x4d, 0x45, 0x4e, - 0x54, 0x5f, 0x44, 0x45, 0x54, 0x41, 0x49, 0x4c, 0x53, 0x10, 0x04, 0x12, 0x27, 0x0a, 0x23, 0x46, - 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x49, 0x4e, - 0x53, 0x55, 0x46, 0x46, 0x49, 0x43, 0x49, 0x45, 0x4e, 0x54, 0x5f, 0x42, 0x41, 0x4c, 0x41, 0x4e, - 0x43, 0x45, 0x10, 0x05, 0x12, 0x1b, 0x0a, 0x17, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, - 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x43, 0x41, 0x4e, 0x43, 0x45, 0x4c, 0x45, 0x44, 0x10, - 0x06, 0x2a, 0x89, 0x05, 0x0a, 0x0a, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x42, 0x69, 0x74, - 0x12, 0x18, 0x0a, 0x14, 0x44, 0x41, 0x54, 0x41, 0x4c, 0x4f, 0x53, 0x53, 0x5f, 0x50, 0x52, 0x4f, - 0x54, 0x45, 0x43, 0x54, 0x5f, 0x52, 0x45, 0x51, 0x10, 0x00, 0x12, 0x18, 0x0a, 0x14, 0x44, 0x41, - 0x54, 0x41, 0x4c, 0x4f, 0x53, 0x53, 0x5f, 0x50, 0x52, 0x4f, 0x54, 0x45, 0x43, 0x54, 0x5f, 0x4f, - 0x50, 0x54, 0x10, 0x01, 0x12, 0x17, 0x0a, 0x13, 0x49, 0x4e, 0x49, 0x54, 0x49, 0x41, 0x4c, 0x5f, - 0x52, 0x4f, 0x55, 0x49, 0x4e, 0x47, 0x5f, 0x53, 0x59, 0x4e, 0x43, 0x10, 0x03, 0x12, 0x1f, 0x0a, - 0x1b, 0x55, 0x50, 0x46, 0x52, 0x4f, 0x4e, 0x54, 0x5f, 0x53, 0x48, 0x55, 0x54, 0x44, 0x4f, 0x57, - 0x4e, 0x5f, 0x53, 0x43, 0x52, 0x49, 0x50, 0x54, 0x5f, 0x52, 0x45, 0x51, 0x10, 0x04, 0x12, 0x1f, - 0x0a, 0x1b, 0x55, 0x50, 0x46, 0x52, 0x4f, 0x4e, 0x54, 0x5f, 0x53, 0x48, 0x55, 0x54, 0x44, 0x4f, - 0x57, 0x4e, 0x5f, 0x53, 0x43, 0x52, 0x49, 0x50, 0x54, 0x5f, 0x4f, 0x50, 0x54, 0x10, 0x05, 0x12, - 0x16, 0x0a, 0x12, 0x47, 0x4f, 0x53, 0x53, 0x49, 0x50, 0x5f, 0x51, 0x55, 0x45, 0x52, 0x49, 0x45, - 0x53, 0x5f, 0x52, 0x45, 0x51, 0x10, 0x06, 0x12, 0x16, 0x0a, 0x12, 0x47, 0x4f, 0x53, 0x53, 0x49, - 0x50, 0x5f, 0x51, 0x55, 0x45, 0x52, 0x49, 0x45, 0x53, 0x5f, 0x4f, 0x50, 0x54, 0x10, 0x07, 0x12, - 0x11, 0x0a, 0x0d, 0x54, 0x4c, 0x56, 0x5f, 0x4f, 0x4e, 0x49, 0x4f, 0x4e, 0x5f, 0x52, 0x45, 0x51, - 0x10, 0x08, 0x12, 0x11, 0x0a, 0x0d, 0x54, 0x4c, 0x56, 0x5f, 0x4f, 0x4e, 0x49, 0x4f, 0x4e, 0x5f, - 0x4f, 0x50, 0x54, 0x10, 0x09, 0x12, 0x1a, 0x0a, 0x16, 0x45, 0x58, 0x54, 0x5f, 0x47, 0x4f, 0x53, - 0x53, 0x49, 0x50, 0x5f, 0x51, 0x55, 0x45, 0x52, 0x49, 0x45, 0x53, 0x5f, 0x52, 0x45, 0x51, 0x10, - 0x0a, 0x12, 0x1a, 0x0a, 0x16, 0x45, 0x58, 0x54, 0x5f, 0x47, 0x4f, 0x53, 0x53, 0x49, 0x50, 0x5f, - 0x51, 0x55, 0x45, 0x52, 0x49, 0x45, 0x53, 0x5f, 0x4f, 0x50, 0x54, 0x10, 0x0b, 0x12, 0x19, 0x0a, - 0x15, 0x53, 0x54, 0x41, 0x54, 0x49, 0x43, 0x5f, 0x52, 0x45, 0x4d, 0x4f, 0x54, 0x45, 0x5f, 0x4b, - 0x45, 0x59, 0x5f, 0x52, 0x45, 0x51, 0x10, 0x0c, 0x12, 0x19, 0x0a, 0x15, 0x53, 0x54, 0x41, 0x54, - 0x49, 0x43, 0x5f, 0x52, 0x45, 0x4d, 0x4f, 0x54, 0x45, 0x5f, 0x4b, 0x45, 0x59, 0x5f, 0x4f, 0x50, - 0x54, 0x10, 0x0d, 0x12, 0x14, 0x0a, 0x10, 0x50, 0x41, 0x59, 0x4d, 0x45, 0x4e, 0x54, 0x5f, 0x41, - 0x44, 0x44, 0x52, 0x5f, 0x52, 0x45, 0x51, 0x10, 0x0e, 0x12, 0x14, 0x0a, 0x10, 0x50, 0x41, 0x59, - 0x4d, 0x45, 0x4e, 0x54, 0x5f, 0x41, 0x44, 0x44, 0x52, 0x5f, 0x4f, 0x50, 0x54, 0x10, 0x0f, 0x12, - 0x0b, 0x0a, 0x07, 0x4d, 0x50, 0x50, 0x5f, 0x52, 0x45, 0x51, 0x10, 0x10, 0x12, 0x0b, 0x0a, 0x07, - 0x4d, 0x50, 0x50, 0x5f, 0x4f, 0x50, 0x54, 0x10, 0x11, 0x12, 0x16, 0x0a, 0x12, 0x57, 0x55, 0x4d, - 0x42, 0x4f, 0x5f, 0x43, 0x48, 0x41, 0x4e, 0x4e, 0x45, 0x4c, 0x53, 0x5f, 0x52, 0x45, 0x51, 0x10, - 0x12, 0x12, 0x16, 0x0a, 0x12, 0x57, 0x55, 0x4d, 0x42, 0x4f, 0x5f, 0x43, 0x48, 0x41, 0x4e, 0x4e, - 0x45, 0x4c, 0x53, 0x5f, 0x4f, 0x50, 0x54, 0x10, 0x13, 0x12, 0x0f, 0x0a, 0x0b, 0x41, 0x4e, 0x43, - 0x48, 0x4f, 0x52, 0x53, 0x5f, 0x52, 0x45, 0x51, 0x10, 0x14, 0x12, 0x0f, 0x0a, 0x0b, 0x41, 0x4e, - 0x43, 0x48, 0x4f, 0x52, 0x53, 0x5f, 0x4f, 0x50, 0x54, 0x10, 0x15, 0x12, 0x1d, 0x0a, 0x19, 0x41, - 0x4e, 0x43, 0x48, 0x4f, 0x52, 0x53, 0x5f, 0x5a, 0x45, 0x52, 0x4f, 0x5f, 0x46, 0x45, 0x45, 0x5f, - 0x48, 0x54, 0x4c, 0x43, 0x5f, 0x52, 0x45, 0x51, 0x10, 0x16, 0x12, 0x1d, 0x0a, 0x19, 0x41, 0x4e, - 0x43, 0x48, 0x4f, 0x52, 0x53, 0x5f, 0x5a, 0x45, 0x52, 0x4f, 0x5f, 0x46, 0x45, 0x45, 0x5f, 0x48, - 0x54, 0x4c, 0x43, 0x5f, 0x4f, 0x50, 0x54, 0x10, 0x17, 0x12, 0x1b, 0x0a, 0x17, 0x52, 0x4f, 0x55, - 0x54, 0x45, 0x5f, 0x42, 0x4c, 0x49, 0x4e, 0x44, 0x49, 0x4e, 0x47, 0x5f, 0x52, 0x45, 0x51, 0x55, - 0x49, 0x52, 0x45, 0x44, 0x10, 0x18, 0x12, 0x1b, 0x0a, 0x17, 0x52, 0x4f, 0x55, 0x54, 0x45, 0x5f, - 0x42, 0x4c, 0x49, 0x4e, 0x44, 0x49, 0x4e, 0x47, 0x5f, 0x4f, 0x50, 0x54, 0x49, 0x4f, 0x4e, 0x41, - 0x4c, 0x10, 0x19, 0x12, 0x0b, 0x0a, 0x07, 0x41, 0x4d, 0x50, 0x5f, 0x52, 0x45, 0x51, 0x10, 0x1e, - 0x12, 0x0b, 0x0a, 0x07, 0x41, 0x4d, 0x50, 0x5f, 0x4f, 0x50, 0x54, 0x10, 0x1f, 0x2a, 0xac, 0x01, - 0x0a, 0x0d, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x46, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x12, - 0x1a, 0x0a, 0x16, 0x55, 0x50, 0x44, 0x41, 0x54, 0x45, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, - 0x45, 0x5f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x1a, 0x0a, 0x16, 0x55, - 0x50, 0x44, 0x41, 0x54, 0x45, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x50, 0x45, - 0x4e, 0x44, 0x49, 0x4e, 0x47, 0x10, 0x01, 0x12, 0x1c, 0x0a, 0x18, 0x55, 0x50, 0x44, 0x41, 0x54, - 0x45, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x4e, 0x4f, 0x54, 0x5f, 0x46, 0x4f, - 0x55, 0x4e, 0x44, 0x10, 0x02, 0x12, 0x1f, 0x0a, 0x1b, 0x55, 0x50, 0x44, 0x41, 0x54, 0x45, 0x5f, - 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x49, 0x4e, 0x54, 0x45, 0x52, 0x4e, 0x41, 0x4c, - 0x5f, 0x45, 0x52, 0x52, 0x10, 0x03, 0x12, 0x24, 0x0a, 0x20, 0x55, 0x50, 0x44, 0x41, 0x54, 0x45, - 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x49, 0x4e, 0x56, 0x41, 0x4c, 0x49, 0x44, - 0x5f, 0x50, 0x41, 0x52, 0x41, 0x4d, 0x45, 0x54, 0x45, 0x52, 0x10, 0x04, 0x32, 0xb9, 0x27, 0x0a, - 0x09, 0x4c, 0x69, 0x67, 0x68, 0x74, 0x6e, 0x69, 0x6e, 0x67, 0x12, 0x4a, 0x0a, 0x0d, 0x57, 0x61, - 0x6c, 0x6c, 0x65, 0x74, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x12, 0x1b, 0x2e, 0x6c, 0x6e, + 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, + 0x65, 0x79, 0x12, 0x33, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x1d, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, + 0x6f, 0x6e, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x4c, 0x69, 0x73, 0x74, + 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xcc, 0x08, 0x0a, 0x07, + 0x46, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x12, 0x2e, 0x0a, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x61, + 0x69, 0x6c, 0x75, 0x72, 0x65, 0x2e, 0x46, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x43, 0x6f, 0x64, + 0x65, 0x52, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x12, 0x3b, 0x0a, 0x0e, 0x63, 0x68, 0x61, 0x6e, 0x6e, + 0x65, 0x6c, 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x14, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x55, + 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x0d, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x55, 0x70, + 0x64, 0x61, 0x74, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x68, 0x74, 0x6c, 0x63, 0x5f, 0x6d, 0x73, 0x61, + 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x68, 0x74, 0x6c, 0x63, 0x4d, 0x73, 0x61, + 0x74, 0x12, 0x22, 0x0a, 0x0d, 0x6f, 0x6e, 0x69, 0x6f, 0x6e, 0x5f, 0x73, 0x68, 0x61, 0x5f, 0x32, + 0x35, 0x36, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x6f, 0x6e, 0x69, 0x6f, 0x6e, 0x53, + 0x68, 0x61, 0x32, 0x35, 0x36, 0x12, 0x1f, 0x0a, 0x0b, 0x63, 0x6c, 0x74, 0x76, 0x5f, 0x65, 0x78, + 0x70, 0x69, 0x72, 0x79, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x63, 0x6c, 0x74, 0x76, + 0x45, 0x78, 0x70, 0x69, 0x72, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x18, + 0x07, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x12, 0x30, 0x0a, 0x14, + 0x66, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x69, + 0x6e, 0x64, 0x65, 0x78, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x12, 0x66, 0x61, 0x69, 0x6c, + 0x75, 0x72, 0x65, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x16, + 0x0a, 0x06, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, + 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x22, 0x8b, 0x06, 0x0a, 0x0b, 0x46, 0x61, 0x69, 0x6c, 0x75, + 0x72, 0x65, 0x43, 0x6f, 0x64, 0x65, 0x12, 0x0c, 0x0a, 0x08, 0x52, 0x45, 0x53, 0x45, 0x52, 0x56, + 0x45, 0x44, 0x10, 0x00, 0x12, 0x28, 0x0a, 0x24, 0x49, 0x4e, 0x43, 0x4f, 0x52, 0x52, 0x45, 0x43, + 0x54, 0x5f, 0x4f, 0x52, 0x5f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x5f, 0x50, 0x41, 0x59, + 0x4d, 0x45, 0x4e, 0x54, 0x5f, 0x44, 0x45, 0x54, 0x41, 0x49, 0x4c, 0x53, 0x10, 0x01, 0x12, 0x1c, + 0x0a, 0x18, 0x49, 0x4e, 0x43, 0x4f, 0x52, 0x52, 0x45, 0x43, 0x54, 0x5f, 0x50, 0x41, 0x59, 0x4d, + 0x45, 0x4e, 0x54, 0x5f, 0x41, 0x4d, 0x4f, 0x55, 0x4e, 0x54, 0x10, 0x02, 0x12, 0x1f, 0x0a, 0x1b, + 0x46, 0x49, 0x4e, 0x41, 0x4c, 0x5f, 0x49, 0x4e, 0x43, 0x4f, 0x52, 0x52, 0x45, 0x43, 0x54, 0x5f, + 0x43, 0x4c, 0x54, 0x56, 0x5f, 0x45, 0x58, 0x50, 0x49, 0x52, 0x59, 0x10, 0x03, 0x12, 0x1f, 0x0a, + 0x1b, 0x46, 0x49, 0x4e, 0x41, 0x4c, 0x5f, 0x49, 0x4e, 0x43, 0x4f, 0x52, 0x52, 0x45, 0x43, 0x54, + 0x5f, 0x48, 0x54, 0x4c, 0x43, 0x5f, 0x41, 0x4d, 0x4f, 0x55, 0x4e, 0x54, 0x10, 0x04, 0x12, 0x19, + 0x0a, 0x15, 0x46, 0x49, 0x4e, 0x41, 0x4c, 0x5f, 0x45, 0x58, 0x50, 0x49, 0x52, 0x59, 0x5f, 0x54, + 0x4f, 0x4f, 0x5f, 0x53, 0x4f, 0x4f, 0x4e, 0x10, 0x05, 0x12, 0x11, 0x0a, 0x0d, 0x49, 0x4e, 0x56, + 0x41, 0x4c, 0x49, 0x44, 0x5f, 0x52, 0x45, 0x41, 0x4c, 0x4d, 0x10, 0x06, 0x12, 0x13, 0x0a, 0x0f, + 0x45, 0x58, 0x50, 0x49, 0x52, 0x59, 0x5f, 0x54, 0x4f, 0x4f, 0x5f, 0x53, 0x4f, 0x4f, 0x4e, 0x10, + 0x07, 0x12, 0x19, 0x0a, 0x15, 0x49, 0x4e, 0x56, 0x41, 0x4c, 0x49, 0x44, 0x5f, 0x4f, 0x4e, 0x49, + 0x4f, 0x4e, 0x5f, 0x56, 0x45, 0x52, 0x53, 0x49, 0x4f, 0x4e, 0x10, 0x08, 0x12, 0x16, 0x0a, 0x12, + 0x49, 0x4e, 0x56, 0x41, 0x4c, 0x49, 0x44, 0x5f, 0x4f, 0x4e, 0x49, 0x4f, 0x4e, 0x5f, 0x48, 0x4d, + 0x41, 0x43, 0x10, 0x09, 0x12, 0x15, 0x0a, 0x11, 0x49, 0x4e, 0x56, 0x41, 0x4c, 0x49, 0x44, 0x5f, + 0x4f, 0x4e, 0x49, 0x4f, 0x4e, 0x5f, 0x4b, 0x45, 0x59, 0x10, 0x0a, 0x12, 0x18, 0x0a, 0x14, 0x41, + 0x4d, 0x4f, 0x55, 0x4e, 0x54, 0x5f, 0x42, 0x45, 0x4c, 0x4f, 0x57, 0x5f, 0x4d, 0x49, 0x4e, 0x49, + 0x4d, 0x55, 0x4d, 0x10, 0x0b, 0x12, 0x14, 0x0a, 0x10, 0x46, 0x45, 0x45, 0x5f, 0x49, 0x4e, 0x53, + 0x55, 0x46, 0x46, 0x49, 0x43, 0x49, 0x45, 0x4e, 0x54, 0x10, 0x0c, 0x12, 0x19, 0x0a, 0x15, 0x49, + 0x4e, 0x43, 0x4f, 0x52, 0x52, 0x45, 0x43, 0x54, 0x5f, 0x43, 0x4c, 0x54, 0x56, 0x5f, 0x45, 0x58, + 0x50, 0x49, 0x52, 0x59, 0x10, 0x0d, 0x12, 0x14, 0x0a, 0x10, 0x43, 0x48, 0x41, 0x4e, 0x4e, 0x45, + 0x4c, 0x5f, 0x44, 0x49, 0x53, 0x41, 0x42, 0x4c, 0x45, 0x44, 0x10, 0x0e, 0x12, 0x1d, 0x0a, 0x19, + 0x54, 0x45, 0x4d, 0x50, 0x4f, 0x52, 0x41, 0x52, 0x59, 0x5f, 0x43, 0x48, 0x41, 0x4e, 0x4e, 0x45, + 0x4c, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x10, 0x0f, 0x12, 0x21, 0x0a, 0x1d, 0x52, + 0x45, 0x51, 0x55, 0x49, 0x52, 0x45, 0x44, 0x5f, 0x4e, 0x4f, 0x44, 0x45, 0x5f, 0x46, 0x45, 0x41, + 0x54, 0x55, 0x52, 0x45, 0x5f, 0x4d, 0x49, 0x53, 0x53, 0x49, 0x4e, 0x47, 0x10, 0x10, 0x12, 0x24, + 0x0a, 0x20, 0x52, 0x45, 0x51, 0x55, 0x49, 0x52, 0x45, 0x44, 0x5f, 0x43, 0x48, 0x41, 0x4e, 0x4e, + 0x45, 0x4c, 0x5f, 0x46, 0x45, 0x41, 0x54, 0x55, 0x52, 0x45, 0x5f, 0x4d, 0x49, 0x53, 0x53, 0x49, + 0x4e, 0x47, 0x10, 0x11, 0x12, 0x15, 0x0a, 0x11, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x5f, + 0x4e, 0x45, 0x58, 0x54, 0x5f, 0x50, 0x45, 0x45, 0x52, 0x10, 0x12, 0x12, 0x1a, 0x0a, 0x16, 0x54, + 0x45, 0x4d, 0x50, 0x4f, 0x52, 0x41, 0x52, 0x59, 0x5f, 0x4e, 0x4f, 0x44, 0x45, 0x5f, 0x46, 0x41, + 0x49, 0x4c, 0x55, 0x52, 0x45, 0x10, 0x13, 0x12, 0x1a, 0x0a, 0x16, 0x50, 0x45, 0x52, 0x4d, 0x41, + 0x4e, 0x45, 0x4e, 0x54, 0x5f, 0x4e, 0x4f, 0x44, 0x45, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, + 0x45, 0x10, 0x14, 0x12, 0x1d, 0x0a, 0x19, 0x50, 0x45, 0x52, 0x4d, 0x41, 0x4e, 0x45, 0x4e, 0x54, + 0x5f, 0x43, 0x48, 0x41, 0x4e, 0x4e, 0x45, 0x4c, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, + 0x10, 0x15, 0x12, 0x12, 0x0a, 0x0e, 0x45, 0x58, 0x50, 0x49, 0x52, 0x59, 0x5f, 0x54, 0x4f, 0x4f, + 0x5f, 0x46, 0x41, 0x52, 0x10, 0x16, 0x12, 0x0f, 0x0a, 0x0b, 0x4d, 0x50, 0x50, 0x5f, 0x54, 0x49, + 0x4d, 0x45, 0x4f, 0x55, 0x54, 0x10, 0x17, 0x12, 0x19, 0x0a, 0x15, 0x49, 0x4e, 0x56, 0x41, 0x4c, + 0x49, 0x44, 0x5f, 0x4f, 0x4e, 0x49, 0x4f, 0x4e, 0x5f, 0x50, 0x41, 0x59, 0x4c, 0x4f, 0x41, 0x44, + 0x10, 0x18, 0x12, 0x1a, 0x0a, 0x16, 0x49, 0x4e, 0x56, 0x41, 0x4c, 0x49, 0x44, 0x5f, 0x4f, 0x4e, + 0x49, 0x4f, 0x4e, 0x5f, 0x42, 0x4c, 0x49, 0x4e, 0x44, 0x49, 0x4e, 0x47, 0x10, 0x19, 0x12, 0x15, + 0x0a, 0x10, 0x49, 0x4e, 0x54, 0x45, 0x52, 0x4e, 0x41, 0x4c, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x55, + 0x52, 0x45, 0x10, 0xe5, 0x07, 0x12, 0x14, 0x0a, 0x0f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, + 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x10, 0xe6, 0x07, 0x12, 0x17, 0x0a, 0x12, 0x55, + 0x4e, 0x52, 0x45, 0x41, 0x44, 0x41, 0x42, 0x4c, 0x45, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, + 0x45, 0x10, 0xe7, 0x07, 0x4a, 0x04, 0x08, 0x02, 0x10, 0x03, 0x22, 0xb3, 0x03, 0x0a, 0x0d, 0x43, + 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x1c, 0x0a, 0x09, + 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, + 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x68, + 0x61, 0x69, 0x6e, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, + 0x63, 0x68, 0x61, 0x69, 0x6e, 0x48, 0x61, 0x73, 0x68, 0x12, 0x1b, 0x0a, 0x07, 0x63, 0x68, 0x61, + 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x42, 0x02, 0x30, 0x01, 0x52, 0x06, + 0x63, 0x68, 0x61, 0x6e, 0x49, 0x64, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, + 0x61, 0x6d, 0x70, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, + 0x74, 0x61, 0x6d, 0x70, 0x12, 0x23, 0x0a, 0x0d, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, + 0x66, 0x6c, 0x61, 0x67, 0x73, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0c, 0x6d, 0x65, 0x73, + 0x73, 0x61, 0x67, 0x65, 0x46, 0x6c, 0x61, 0x67, 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x68, 0x61, + 0x6e, 0x6e, 0x65, 0x6c, 0x5f, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, + 0x52, 0x0c, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x46, 0x6c, 0x61, 0x67, 0x73, 0x12, 0x26, + 0x0a, 0x0f, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x64, 0x65, 0x6c, 0x74, + 0x61, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0d, 0x74, 0x69, 0x6d, 0x65, 0x4c, 0x6f, 0x63, + 0x6b, 0x44, 0x65, 0x6c, 0x74, 0x61, 0x12, 0x2a, 0x0a, 0x11, 0x68, 0x74, 0x6c, 0x63, 0x5f, 0x6d, + 0x69, 0x6e, 0x69, 0x6d, 0x75, 0x6d, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, + 0x04, 0x52, 0x0f, 0x68, 0x74, 0x6c, 0x63, 0x4d, 0x69, 0x6e, 0x69, 0x6d, 0x75, 0x6d, 0x4d, 0x73, + 0x61, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x62, 0x61, 0x73, 0x65, 0x5f, 0x66, 0x65, 0x65, 0x18, 0x08, + 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x62, 0x61, 0x73, 0x65, 0x46, 0x65, 0x65, 0x12, 0x19, 0x0a, + 0x08, 0x66, 0x65, 0x65, 0x5f, 0x72, 0x61, 0x74, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0d, 0x52, + 0x07, 0x66, 0x65, 0x65, 0x52, 0x61, 0x74, 0x65, 0x12, 0x2a, 0x0a, 0x11, 0x68, 0x74, 0x6c, 0x63, + 0x5f, 0x6d, 0x61, 0x78, 0x69, 0x6d, 0x75, 0x6d, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x0b, 0x20, + 0x01, 0x28, 0x04, 0x52, 0x0f, 0x68, 0x74, 0x6c, 0x63, 0x4d, 0x61, 0x78, 0x69, 0x6d, 0x75, 0x6d, + 0x4d, 0x73, 0x61, 0x74, 0x12, 0x2a, 0x0a, 0x11, 0x65, 0x78, 0x74, 0x72, 0x61, 0x5f, 0x6f, 0x70, + 0x61, 0x71, 0x75, 0x65, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0c, 0x52, + 0x0f, 0x65, 0x78, 0x74, 0x72, 0x61, 0x4f, 0x70, 0x61, 0x71, 0x75, 0x65, 0x44, 0x61, 0x74, 0x61, + 0x22, 0x5d, 0x0a, 0x0a, 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x14, + 0x0a, 0x05, 0x6e, 0x6f, 0x6e, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x6e, + 0x6f, 0x6e, 0x63, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x49, + 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, + 0x49, 0x64, 0x12, 0x1b, 0x0a, 0x03, 0x6f, 0x70, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x09, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4f, 0x70, 0x52, 0x03, 0x6f, 0x70, 0x73, 0x22, + 0x36, 0x0a, 0x02, 0x4f, 0x70, 0x12, 0x16, 0x0a, 0x06, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x12, 0x18, 0x0a, + 0x07, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, + 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x8e, 0x01, 0x0a, 0x13, 0x43, 0x68, 0x65, 0x63, + 0x6b, 0x4d, 0x61, 0x63, 0x50, 0x65, 0x72, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, + 0x1a, 0x0a, 0x08, 0x6d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0c, 0x52, 0x08, 0x6d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x12, 0x3b, 0x0a, 0x0b, 0x70, + 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, + 0x6e, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x0b, 0x70, 0x65, 0x72, + 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x1e, 0x0a, 0x0a, 0x66, 0x75, 0x6c, 0x6c, + 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x66, 0x75, + 0x6c, 0x6c, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x22, 0x2c, 0x0a, 0x14, 0x43, 0x68, 0x65, 0x63, + 0x6b, 0x4d, 0x61, 0x63, 0x50, 0x65, 0x72, 0x6d, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, + 0x05, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x22, 0xf4, 0x02, 0x0a, 0x14, 0x52, 0x50, 0x43, 0x4d, 0x69, + 0x64, 0x64, 0x6c, 0x65, 0x77, 0x61, 0x72, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, + 0x1d, 0x0a, 0x0a, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x04, 0x52, 0x09, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x49, 0x64, 0x12, 0x21, + 0x0a, 0x0c, 0x72, 0x61, 0x77, 0x5f, 0x6d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x72, 0x61, 0x77, 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, + 0x6e, 0x12, 0x36, 0x0a, 0x17, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5f, 0x63, 0x61, 0x76, 0x65, + 0x61, 0x74, 0x5f, 0x63, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x15, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x43, 0x61, 0x76, 0x65, 0x61, 0x74, + 0x43, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x34, 0x0a, 0x0b, 0x73, 0x74, 0x72, + 0x65, 0x61, 0x6d, 0x5f, 0x61, 0x75, 0x74, 0x68, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, + 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x41, 0x75, 0x74, + 0x68, 0x48, 0x00, 0x52, 0x0a, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x41, 0x75, 0x74, 0x68, 0x12, + 0x2d, 0x0a, 0x07, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x11, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x50, 0x43, 0x4d, 0x65, 0x73, 0x73, + 0x61, 0x67, 0x65, 0x48, 0x00, 0x52, 0x07, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2f, + 0x0a, 0x08, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x11, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x50, 0x43, 0x4d, 0x65, 0x73, 0x73, + 0x61, 0x67, 0x65, 0x48, 0x00, 0x52, 0x08, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x23, 0x0a, 0x0c, 0x72, 0x65, 0x67, 0x5f, 0x63, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x18, + 0x08, 0x20, 0x01, 0x28, 0x08, 0x48, 0x00, 0x52, 0x0b, 0x72, 0x65, 0x67, 0x43, 0x6f, 0x6d, 0x70, + 0x6c, 0x65, 0x74, 0x65, 0x12, 0x15, 0x0a, 0x06, 0x6d, 0x73, 0x67, 0x5f, 0x69, 0x64, 0x18, 0x07, + 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x6d, 0x73, 0x67, 0x49, 0x64, 0x42, 0x10, 0x0a, 0x0e, 0x69, + 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x22, 0x34, 0x0a, + 0x0a, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x41, 0x75, 0x74, 0x68, 0x12, 0x26, 0x0a, 0x0f, 0x6d, + 0x65, 0x74, 0x68, 0x6f, 0x64, 0x5f, 0x66, 0x75, 0x6c, 0x6c, 0x5f, 0x75, 0x72, 0x69, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x46, 0x75, 0x6c, 0x6c, + 0x55, 0x72, 0x69, 0x22, 0xab, 0x01, 0x0a, 0x0a, 0x52, 0x50, 0x43, 0x4d, 0x65, 0x73, 0x73, 0x61, + 0x67, 0x65, 0x12, 0x26, 0x0a, 0x0f, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x5f, 0x66, 0x75, 0x6c, + 0x6c, 0x5f, 0x75, 0x72, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x6d, 0x65, 0x74, + 0x68, 0x6f, 0x64, 0x46, 0x75, 0x6c, 0x6c, 0x55, 0x72, 0x69, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x74, + 0x72, 0x65, 0x61, 0x6d, 0x5f, 0x72, 0x70, 0x63, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, + 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x70, 0x63, 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x79, 0x70, + 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x79, + 0x70, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, + 0x69, 0x7a, 0x65, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x73, 0x65, 0x72, 0x69, + 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x12, 0x19, 0x0a, 0x08, 0x69, 0x73, 0x5f, 0x65, 0x72, 0x72, + 0x6f, 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x69, 0x73, 0x45, 0x72, 0x72, 0x6f, + 0x72, 0x22, 0xc0, 0x01, 0x0a, 0x15, 0x52, 0x50, 0x43, 0x4d, 0x69, 0x64, 0x64, 0x6c, 0x65, 0x77, + 0x61, 0x72, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1c, 0x0a, 0x0a, 0x72, + 0x65, 0x66, 0x5f, 0x6d, 0x73, 0x67, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, + 0x08, 0x72, 0x65, 0x66, 0x4d, 0x73, 0x67, 0x49, 0x64, 0x12, 0x3b, 0x0a, 0x08, 0x72, 0x65, 0x67, + 0x69, 0x73, 0x74, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x6c, 0x6e, + 0x72, 0x70, 0x63, 0x2e, 0x4d, 0x69, 0x64, 0x64, 0x6c, 0x65, 0x77, 0x61, 0x72, 0x65, 0x52, 0x65, + 0x67, 0x69, 0x73, 0x74, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x00, 0x52, 0x08, 0x72, 0x65, + 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x12, 0x36, 0x0a, 0x08, 0x66, 0x65, 0x65, 0x64, 0x62, 0x61, + 0x63, 0x6b, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, + 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x46, 0x65, 0x65, 0x64, 0x62, 0x61, + 0x63, 0x6b, 0x48, 0x00, 0x52, 0x08, 0x66, 0x65, 0x65, 0x64, 0x62, 0x61, 0x63, 0x6b, 0x42, 0x14, + 0x0a, 0x12, 0x6d, 0x69, 0x64, 0x64, 0x6c, 0x65, 0x77, 0x61, 0x72, 0x65, 0x5f, 0x6d, 0x65, 0x73, + 0x73, 0x61, 0x67, 0x65, 0x22, 0xa6, 0x01, 0x0a, 0x16, 0x4d, 0x69, 0x64, 0x64, 0x6c, 0x65, 0x77, + 0x61, 0x72, 0x65, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, + 0x27, 0x0a, 0x0f, 0x6d, 0x69, 0x64, 0x64, 0x6c, 0x65, 0x77, 0x61, 0x72, 0x65, 0x5f, 0x6e, 0x61, + 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x6d, 0x69, 0x64, 0x64, 0x6c, 0x65, + 0x77, 0x61, 0x72, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x3d, 0x0a, 0x1b, 0x63, 0x75, 0x73, 0x74, + 0x6f, 0x6d, 0x5f, 0x6d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x5f, 0x63, 0x61, 0x76, 0x65, + 0x61, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x18, 0x63, + 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x43, 0x61, 0x76, + 0x65, 0x61, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x24, 0x0a, 0x0e, 0x72, 0x65, 0x61, 0x64, 0x5f, + 0x6f, 0x6e, 0x6c, 0x79, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, + 0x0c, 0x72, 0x65, 0x61, 0x64, 0x4f, 0x6e, 0x6c, 0x79, 0x4d, 0x6f, 0x64, 0x65, 0x22, 0x8b, 0x01, + 0x0a, 0x11, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x46, 0x65, 0x65, 0x64, 0x62, + 0x61, 0x63, 0x6b, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x29, 0x0a, 0x10, 0x72, 0x65, 0x70, + 0x6c, 0x61, 0x63, 0x65, 0x5f, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x08, 0x52, 0x0f, 0x72, 0x65, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x35, 0x0a, 0x16, 0x72, 0x65, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x6d, + 0x65, 0x6e, 0x74, 0x5f, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x0c, 0x52, 0x15, 0x72, 0x65, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x6d, 0x65, 0x6e, + 0x74, 0x53, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x2a, 0xcb, 0x02, 0x0a, 0x10, + 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x54, 0x79, 0x70, 0x65, + 0x12, 0x1b, 0x0a, 0x17, 0x53, 0x43, 0x52, 0x49, 0x50, 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, + 0x50, 0x55, 0x42, 0x4b, 0x45, 0x59, 0x5f, 0x48, 0x41, 0x53, 0x48, 0x10, 0x00, 0x12, 0x1b, 0x0a, + 0x17, 0x53, 0x43, 0x52, 0x49, 0x50, 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x53, 0x43, 0x52, + 0x49, 0x50, 0x54, 0x5f, 0x48, 0x41, 0x53, 0x48, 0x10, 0x01, 0x12, 0x26, 0x0a, 0x22, 0x53, 0x43, + 0x52, 0x49, 0x50, 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x57, 0x49, 0x54, 0x4e, 0x45, 0x53, + 0x53, 0x5f, 0x56, 0x30, 0x5f, 0x50, 0x55, 0x42, 0x4b, 0x45, 0x59, 0x5f, 0x48, 0x41, 0x53, 0x48, + 0x10, 0x02, 0x12, 0x26, 0x0a, 0x22, 0x53, 0x43, 0x52, 0x49, 0x50, 0x54, 0x5f, 0x54, 0x59, 0x50, + 0x45, 0x5f, 0x57, 0x49, 0x54, 0x4e, 0x45, 0x53, 0x53, 0x5f, 0x56, 0x30, 0x5f, 0x53, 0x43, 0x52, + 0x49, 0x50, 0x54, 0x5f, 0x48, 0x41, 0x53, 0x48, 0x10, 0x03, 0x12, 0x16, 0x0a, 0x12, 0x53, 0x43, + 0x52, 0x49, 0x50, 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x50, 0x55, 0x42, 0x4b, 0x45, 0x59, + 0x10, 0x04, 0x12, 0x18, 0x0a, 0x14, 0x53, 0x43, 0x52, 0x49, 0x50, 0x54, 0x5f, 0x54, 0x59, 0x50, + 0x45, 0x5f, 0x4d, 0x55, 0x4c, 0x54, 0x49, 0x53, 0x49, 0x47, 0x10, 0x05, 0x12, 0x18, 0x0a, 0x14, + 0x53, 0x43, 0x52, 0x49, 0x50, 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x4e, 0x55, 0x4c, 0x4c, + 0x44, 0x41, 0x54, 0x41, 0x10, 0x06, 0x12, 0x1c, 0x0a, 0x18, 0x53, 0x43, 0x52, 0x49, 0x50, 0x54, + 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x4e, 0x4f, 0x4e, 0x5f, 0x53, 0x54, 0x41, 0x4e, 0x44, 0x41, + 0x52, 0x44, 0x10, 0x07, 0x12, 0x1f, 0x0a, 0x1b, 0x53, 0x43, 0x52, 0x49, 0x50, 0x54, 0x5f, 0x54, + 0x59, 0x50, 0x45, 0x5f, 0x57, 0x49, 0x54, 0x4e, 0x45, 0x53, 0x53, 0x5f, 0x55, 0x4e, 0x4b, 0x4e, + 0x4f, 0x57, 0x4e, 0x10, 0x08, 0x12, 0x22, 0x0a, 0x1e, 0x53, 0x43, 0x52, 0x49, 0x50, 0x54, 0x5f, + 0x54, 0x59, 0x50, 0x45, 0x5f, 0x57, 0x49, 0x54, 0x4e, 0x45, 0x53, 0x53, 0x5f, 0x56, 0x31, 0x5f, + 0x54, 0x41, 0x50, 0x52, 0x4f, 0x4f, 0x54, 0x10, 0x09, 0x2a, 0x62, 0x0a, 0x15, 0x43, 0x6f, 0x69, + 0x6e, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, + 0x67, 0x79, 0x12, 0x1e, 0x0a, 0x1a, 0x53, 0x54, 0x52, 0x41, 0x54, 0x45, 0x47, 0x59, 0x5f, 0x55, + 0x53, 0x45, 0x5f, 0x47, 0x4c, 0x4f, 0x42, 0x41, 0x4c, 0x5f, 0x43, 0x4f, 0x4e, 0x46, 0x49, 0x47, + 0x10, 0x00, 0x12, 0x14, 0x0a, 0x10, 0x53, 0x54, 0x52, 0x41, 0x54, 0x45, 0x47, 0x59, 0x5f, 0x4c, + 0x41, 0x52, 0x47, 0x45, 0x53, 0x54, 0x10, 0x01, 0x12, 0x13, 0x0a, 0x0f, 0x53, 0x54, 0x52, 0x41, + 0x54, 0x45, 0x47, 0x59, 0x5f, 0x52, 0x41, 0x4e, 0x44, 0x4f, 0x4d, 0x10, 0x02, 0x2a, 0xac, 0x01, + 0x0a, 0x0b, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x54, 0x79, 0x70, 0x65, 0x12, 0x17, 0x0a, + 0x13, 0x57, 0x49, 0x54, 0x4e, 0x45, 0x53, 0x53, 0x5f, 0x50, 0x55, 0x42, 0x4b, 0x45, 0x59, 0x5f, + 0x48, 0x41, 0x53, 0x48, 0x10, 0x00, 0x12, 0x16, 0x0a, 0x12, 0x4e, 0x45, 0x53, 0x54, 0x45, 0x44, + 0x5f, 0x50, 0x55, 0x42, 0x4b, 0x45, 0x59, 0x5f, 0x48, 0x41, 0x53, 0x48, 0x10, 0x01, 0x12, 0x1e, + 0x0a, 0x1a, 0x55, 0x4e, 0x55, 0x53, 0x45, 0x44, 0x5f, 0x57, 0x49, 0x54, 0x4e, 0x45, 0x53, 0x53, + 0x5f, 0x50, 0x55, 0x42, 0x4b, 0x45, 0x59, 0x5f, 0x48, 0x41, 0x53, 0x48, 0x10, 0x02, 0x12, 0x1d, + 0x0a, 0x19, 0x55, 0x4e, 0x55, 0x53, 0x45, 0x44, 0x5f, 0x4e, 0x45, 0x53, 0x54, 0x45, 0x44, 0x5f, + 0x50, 0x55, 0x42, 0x4b, 0x45, 0x59, 0x5f, 0x48, 0x41, 0x53, 0x48, 0x10, 0x03, 0x12, 0x12, 0x0a, + 0x0e, 0x54, 0x41, 0x50, 0x52, 0x4f, 0x4f, 0x54, 0x5f, 0x50, 0x55, 0x42, 0x4b, 0x45, 0x59, 0x10, + 0x04, 0x12, 0x19, 0x0a, 0x15, 0x55, 0x4e, 0x55, 0x53, 0x45, 0x44, 0x5f, 0x54, 0x41, 0x50, 0x52, + 0x4f, 0x4f, 0x54, 0x5f, 0x50, 0x55, 0x42, 0x4b, 0x45, 0x59, 0x10, 0x05, 0x2a, 0x8c, 0x01, 0x0a, + 0x0e, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x6d, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, + 0x1b, 0x0a, 0x17, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x5f, 0x43, 0x4f, 0x4d, 0x4d, 0x49, + 0x54, 0x4d, 0x45, 0x4e, 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, + 0x4c, 0x45, 0x47, 0x41, 0x43, 0x59, 0x10, 0x01, 0x12, 0x15, 0x0a, 0x11, 0x53, 0x54, 0x41, 0x54, + 0x49, 0x43, 0x5f, 0x52, 0x45, 0x4d, 0x4f, 0x54, 0x45, 0x5f, 0x4b, 0x45, 0x59, 0x10, 0x02, 0x12, + 0x0b, 0x0a, 0x07, 0x41, 0x4e, 0x43, 0x48, 0x4f, 0x52, 0x53, 0x10, 0x03, 0x12, 0x19, 0x0a, 0x15, + 0x53, 0x43, 0x52, 0x49, 0x50, 0x54, 0x5f, 0x45, 0x4e, 0x46, 0x4f, 0x52, 0x43, 0x45, 0x44, 0x5f, + 0x4c, 0x45, 0x41, 0x53, 0x45, 0x10, 0x04, 0x12, 0x12, 0x0a, 0x0e, 0x53, 0x49, 0x4d, 0x50, 0x4c, + 0x45, 0x5f, 0x54, 0x41, 0x50, 0x52, 0x4f, 0x4f, 0x54, 0x10, 0x05, 0x2a, 0x61, 0x0a, 0x09, 0x49, + 0x6e, 0x69, 0x74, 0x69, 0x61, 0x74, 0x6f, 0x72, 0x12, 0x15, 0x0a, 0x11, 0x49, 0x4e, 0x49, 0x54, + 0x49, 0x41, 0x54, 0x4f, 0x52, 0x5f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, + 0x13, 0x0a, 0x0f, 0x49, 0x4e, 0x49, 0x54, 0x49, 0x41, 0x54, 0x4f, 0x52, 0x5f, 0x4c, 0x4f, 0x43, + 0x41, 0x4c, 0x10, 0x01, 0x12, 0x14, 0x0a, 0x10, 0x49, 0x4e, 0x49, 0x54, 0x49, 0x41, 0x54, 0x4f, + 0x52, 0x5f, 0x52, 0x45, 0x4d, 0x4f, 0x54, 0x45, 0x10, 0x02, 0x12, 0x12, 0x0a, 0x0e, 0x49, 0x4e, + 0x49, 0x54, 0x49, 0x41, 0x54, 0x4f, 0x52, 0x5f, 0x42, 0x4f, 0x54, 0x48, 0x10, 0x03, 0x2a, 0x60, + 0x0a, 0x0e, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x79, 0x70, 0x65, + 0x12, 0x10, 0x0a, 0x0c, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, + 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x41, 0x4e, 0x43, 0x48, 0x4f, 0x52, 0x10, 0x01, 0x12, 0x11, + 0x0a, 0x0d, 0x49, 0x4e, 0x43, 0x4f, 0x4d, 0x49, 0x4e, 0x47, 0x5f, 0x48, 0x54, 0x4c, 0x43, 0x10, + 0x02, 0x12, 0x11, 0x0a, 0x0d, 0x4f, 0x55, 0x54, 0x47, 0x4f, 0x49, 0x4e, 0x47, 0x5f, 0x48, 0x54, + 0x4c, 0x43, 0x10, 0x03, 0x12, 0x0a, 0x0a, 0x06, 0x43, 0x4f, 0x4d, 0x4d, 0x49, 0x54, 0x10, 0x04, + 0x2a, 0x71, 0x0a, 0x11, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x4f, 0x75, + 0x74, 0x63, 0x6f, 0x6d, 0x65, 0x12, 0x13, 0x0a, 0x0f, 0x4f, 0x55, 0x54, 0x43, 0x4f, 0x4d, 0x45, + 0x5f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x43, 0x4c, + 0x41, 0x49, 0x4d, 0x45, 0x44, 0x10, 0x01, 0x12, 0x0d, 0x0a, 0x09, 0x55, 0x4e, 0x43, 0x4c, 0x41, + 0x49, 0x4d, 0x45, 0x44, 0x10, 0x02, 0x12, 0x0d, 0x0a, 0x09, 0x41, 0x42, 0x41, 0x4e, 0x44, 0x4f, + 0x4e, 0x45, 0x44, 0x10, 0x03, 0x12, 0x0f, 0x0a, 0x0b, 0x46, 0x49, 0x52, 0x53, 0x54, 0x5f, 0x53, + 0x54, 0x41, 0x47, 0x45, 0x10, 0x04, 0x12, 0x0b, 0x0a, 0x07, 0x54, 0x49, 0x4d, 0x45, 0x4f, 0x55, + 0x54, 0x10, 0x05, 0x2a, 0x39, 0x0a, 0x0e, 0x4e, 0x6f, 0x64, 0x65, 0x4d, 0x65, 0x74, 0x72, 0x69, + 0x63, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, + 0x10, 0x00, 0x12, 0x1a, 0x0a, 0x16, 0x42, 0x45, 0x54, 0x57, 0x45, 0x45, 0x4e, 0x4e, 0x45, 0x53, + 0x53, 0x5f, 0x43, 0x45, 0x4e, 0x54, 0x52, 0x41, 0x4c, 0x49, 0x54, 0x59, 0x10, 0x01, 0x2a, 0x3b, + 0x0a, 0x10, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x48, 0x54, 0x4c, 0x43, 0x53, 0x74, 0x61, + 0x74, 0x65, 0x12, 0x0c, 0x0a, 0x08, 0x41, 0x43, 0x43, 0x45, 0x50, 0x54, 0x45, 0x44, 0x10, 0x00, + 0x12, 0x0b, 0x0a, 0x07, 0x53, 0x45, 0x54, 0x54, 0x4c, 0x45, 0x44, 0x10, 0x01, 0x12, 0x0c, 0x0a, + 0x08, 0x43, 0x41, 0x4e, 0x43, 0x45, 0x4c, 0x45, 0x44, 0x10, 0x02, 0x2a, 0xf6, 0x01, 0x0a, 0x14, + 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x46, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x52, 0x65, + 0x61, 0x73, 0x6f, 0x6e, 0x12, 0x17, 0x0a, 0x13, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, + 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x4e, 0x4f, 0x4e, 0x45, 0x10, 0x00, 0x12, 0x1a, 0x0a, + 0x16, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, + 0x54, 0x49, 0x4d, 0x45, 0x4f, 0x55, 0x54, 0x10, 0x01, 0x12, 0x1b, 0x0a, 0x17, 0x46, 0x41, 0x49, + 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x4e, 0x4f, 0x5f, 0x52, + 0x4f, 0x55, 0x54, 0x45, 0x10, 0x02, 0x12, 0x18, 0x0a, 0x14, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, + 0x45, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, 0x03, + 0x12, 0x2c, 0x0a, 0x28, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x52, 0x45, 0x41, 0x53, + 0x4f, 0x4e, 0x5f, 0x49, 0x4e, 0x43, 0x4f, 0x52, 0x52, 0x45, 0x43, 0x54, 0x5f, 0x50, 0x41, 0x59, + 0x4d, 0x45, 0x4e, 0x54, 0x5f, 0x44, 0x45, 0x54, 0x41, 0x49, 0x4c, 0x53, 0x10, 0x04, 0x12, 0x27, + 0x0a, 0x23, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, + 0x5f, 0x49, 0x4e, 0x53, 0x55, 0x46, 0x46, 0x49, 0x43, 0x49, 0x45, 0x4e, 0x54, 0x5f, 0x42, 0x41, + 0x4c, 0x41, 0x4e, 0x43, 0x45, 0x10, 0x05, 0x12, 0x1b, 0x0a, 0x17, 0x46, 0x41, 0x49, 0x4c, 0x55, + 0x52, 0x45, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x43, 0x41, 0x4e, 0x43, 0x45, 0x4c, + 0x45, 0x44, 0x10, 0x06, 0x2a, 0x89, 0x05, 0x0a, 0x0a, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, + 0x42, 0x69, 0x74, 0x12, 0x18, 0x0a, 0x14, 0x44, 0x41, 0x54, 0x41, 0x4c, 0x4f, 0x53, 0x53, 0x5f, + 0x50, 0x52, 0x4f, 0x54, 0x45, 0x43, 0x54, 0x5f, 0x52, 0x45, 0x51, 0x10, 0x00, 0x12, 0x18, 0x0a, + 0x14, 0x44, 0x41, 0x54, 0x41, 0x4c, 0x4f, 0x53, 0x53, 0x5f, 0x50, 0x52, 0x4f, 0x54, 0x45, 0x43, + 0x54, 0x5f, 0x4f, 0x50, 0x54, 0x10, 0x01, 0x12, 0x17, 0x0a, 0x13, 0x49, 0x4e, 0x49, 0x54, 0x49, + 0x41, 0x4c, 0x5f, 0x52, 0x4f, 0x55, 0x49, 0x4e, 0x47, 0x5f, 0x53, 0x59, 0x4e, 0x43, 0x10, 0x03, + 0x12, 0x1f, 0x0a, 0x1b, 0x55, 0x50, 0x46, 0x52, 0x4f, 0x4e, 0x54, 0x5f, 0x53, 0x48, 0x55, 0x54, + 0x44, 0x4f, 0x57, 0x4e, 0x5f, 0x53, 0x43, 0x52, 0x49, 0x50, 0x54, 0x5f, 0x52, 0x45, 0x51, 0x10, + 0x04, 0x12, 0x1f, 0x0a, 0x1b, 0x55, 0x50, 0x46, 0x52, 0x4f, 0x4e, 0x54, 0x5f, 0x53, 0x48, 0x55, + 0x54, 0x44, 0x4f, 0x57, 0x4e, 0x5f, 0x53, 0x43, 0x52, 0x49, 0x50, 0x54, 0x5f, 0x4f, 0x50, 0x54, + 0x10, 0x05, 0x12, 0x16, 0x0a, 0x12, 0x47, 0x4f, 0x53, 0x53, 0x49, 0x50, 0x5f, 0x51, 0x55, 0x45, + 0x52, 0x49, 0x45, 0x53, 0x5f, 0x52, 0x45, 0x51, 0x10, 0x06, 0x12, 0x16, 0x0a, 0x12, 0x47, 0x4f, + 0x53, 0x53, 0x49, 0x50, 0x5f, 0x51, 0x55, 0x45, 0x52, 0x49, 0x45, 0x53, 0x5f, 0x4f, 0x50, 0x54, + 0x10, 0x07, 0x12, 0x11, 0x0a, 0x0d, 0x54, 0x4c, 0x56, 0x5f, 0x4f, 0x4e, 0x49, 0x4f, 0x4e, 0x5f, + 0x52, 0x45, 0x51, 0x10, 0x08, 0x12, 0x11, 0x0a, 0x0d, 0x54, 0x4c, 0x56, 0x5f, 0x4f, 0x4e, 0x49, + 0x4f, 0x4e, 0x5f, 0x4f, 0x50, 0x54, 0x10, 0x09, 0x12, 0x1a, 0x0a, 0x16, 0x45, 0x58, 0x54, 0x5f, + 0x47, 0x4f, 0x53, 0x53, 0x49, 0x50, 0x5f, 0x51, 0x55, 0x45, 0x52, 0x49, 0x45, 0x53, 0x5f, 0x52, + 0x45, 0x51, 0x10, 0x0a, 0x12, 0x1a, 0x0a, 0x16, 0x45, 0x58, 0x54, 0x5f, 0x47, 0x4f, 0x53, 0x53, + 0x49, 0x50, 0x5f, 0x51, 0x55, 0x45, 0x52, 0x49, 0x45, 0x53, 0x5f, 0x4f, 0x50, 0x54, 0x10, 0x0b, + 0x12, 0x19, 0x0a, 0x15, 0x53, 0x54, 0x41, 0x54, 0x49, 0x43, 0x5f, 0x52, 0x45, 0x4d, 0x4f, 0x54, + 0x45, 0x5f, 0x4b, 0x45, 0x59, 0x5f, 0x52, 0x45, 0x51, 0x10, 0x0c, 0x12, 0x19, 0x0a, 0x15, 0x53, + 0x54, 0x41, 0x54, 0x49, 0x43, 0x5f, 0x52, 0x45, 0x4d, 0x4f, 0x54, 0x45, 0x5f, 0x4b, 0x45, 0x59, + 0x5f, 0x4f, 0x50, 0x54, 0x10, 0x0d, 0x12, 0x14, 0x0a, 0x10, 0x50, 0x41, 0x59, 0x4d, 0x45, 0x4e, + 0x54, 0x5f, 0x41, 0x44, 0x44, 0x52, 0x5f, 0x52, 0x45, 0x51, 0x10, 0x0e, 0x12, 0x14, 0x0a, 0x10, + 0x50, 0x41, 0x59, 0x4d, 0x45, 0x4e, 0x54, 0x5f, 0x41, 0x44, 0x44, 0x52, 0x5f, 0x4f, 0x50, 0x54, + 0x10, 0x0f, 0x12, 0x0b, 0x0a, 0x07, 0x4d, 0x50, 0x50, 0x5f, 0x52, 0x45, 0x51, 0x10, 0x10, 0x12, + 0x0b, 0x0a, 0x07, 0x4d, 0x50, 0x50, 0x5f, 0x4f, 0x50, 0x54, 0x10, 0x11, 0x12, 0x16, 0x0a, 0x12, + 0x57, 0x55, 0x4d, 0x42, 0x4f, 0x5f, 0x43, 0x48, 0x41, 0x4e, 0x4e, 0x45, 0x4c, 0x53, 0x5f, 0x52, + 0x45, 0x51, 0x10, 0x12, 0x12, 0x16, 0x0a, 0x12, 0x57, 0x55, 0x4d, 0x42, 0x4f, 0x5f, 0x43, 0x48, + 0x41, 0x4e, 0x4e, 0x45, 0x4c, 0x53, 0x5f, 0x4f, 0x50, 0x54, 0x10, 0x13, 0x12, 0x0f, 0x0a, 0x0b, + 0x41, 0x4e, 0x43, 0x48, 0x4f, 0x52, 0x53, 0x5f, 0x52, 0x45, 0x51, 0x10, 0x14, 0x12, 0x0f, 0x0a, + 0x0b, 0x41, 0x4e, 0x43, 0x48, 0x4f, 0x52, 0x53, 0x5f, 0x4f, 0x50, 0x54, 0x10, 0x15, 0x12, 0x1d, + 0x0a, 0x19, 0x41, 0x4e, 0x43, 0x48, 0x4f, 0x52, 0x53, 0x5f, 0x5a, 0x45, 0x52, 0x4f, 0x5f, 0x46, + 0x45, 0x45, 0x5f, 0x48, 0x54, 0x4c, 0x43, 0x5f, 0x52, 0x45, 0x51, 0x10, 0x16, 0x12, 0x1d, 0x0a, + 0x19, 0x41, 0x4e, 0x43, 0x48, 0x4f, 0x52, 0x53, 0x5f, 0x5a, 0x45, 0x52, 0x4f, 0x5f, 0x46, 0x45, + 0x45, 0x5f, 0x48, 0x54, 0x4c, 0x43, 0x5f, 0x4f, 0x50, 0x54, 0x10, 0x17, 0x12, 0x1b, 0x0a, 0x17, + 0x52, 0x4f, 0x55, 0x54, 0x45, 0x5f, 0x42, 0x4c, 0x49, 0x4e, 0x44, 0x49, 0x4e, 0x47, 0x5f, 0x52, + 0x45, 0x51, 0x55, 0x49, 0x52, 0x45, 0x44, 0x10, 0x18, 0x12, 0x1b, 0x0a, 0x17, 0x52, 0x4f, 0x55, + 0x54, 0x45, 0x5f, 0x42, 0x4c, 0x49, 0x4e, 0x44, 0x49, 0x4e, 0x47, 0x5f, 0x4f, 0x50, 0x54, 0x49, + 0x4f, 0x4e, 0x41, 0x4c, 0x10, 0x19, 0x12, 0x0b, 0x0a, 0x07, 0x41, 0x4d, 0x50, 0x5f, 0x52, 0x45, + 0x51, 0x10, 0x1e, 0x12, 0x0b, 0x0a, 0x07, 0x41, 0x4d, 0x50, 0x5f, 0x4f, 0x50, 0x54, 0x10, 0x1f, + 0x2a, 0xac, 0x01, 0x0a, 0x0d, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x46, 0x61, 0x69, 0x6c, 0x75, + 0x72, 0x65, 0x12, 0x1a, 0x0a, 0x16, 0x55, 0x50, 0x44, 0x41, 0x54, 0x45, 0x5f, 0x46, 0x41, 0x49, + 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x1a, + 0x0a, 0x16, 0x55, 0x50, 0x44, 0x41, 0x54, 0x45, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, + 0x5f, 0x50, 0x45, 0x4e, 0x44, 0x49, 0x4e, 0x47, 0x10, 0x01, 0x12, 0x1c, 0x0a, 0x18, 0x55, 0x50, + 0x44, 0x41, 0x54, 0x45, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x4e, 0x4f, 0x54, + 0x5f, 0x46, 0x4f, 0x55, 0x4e, 0x44, 0x10, 0x02, 0x12, 0x1f, 0x0a, 0x1b, 0x55, 0x50, 0x44, 0x41, + 0x54, 0x45, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x49, 0x4e, 0x54, 0x45, 0x52, + 0x4e, 0x41, 0x4c, 0x5f, 0x45, 0x52, 0x52, 0x10, 0x03, 0x12, 0x24, 0x0a, 0x20, 0x55, 0x50, 0x44, + 0x41, 0x54, 0x45, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x49, 0x4e, 0x56, 0x41, + 0x4c, 0x49, 0x44, 0x5f, 0x50, 0x41, 0x52, 0x41, 0x4d, 0x45, 0x54, 0x45, 0x52, 0x10, 0x04, 0x32, + 0xb9, 0x27, 0x0a, 0x09, 0x4c, 0x69, 0x67, 0x68, 0x74, 0x6e, 0x69, 0x6e, 0x67, 0x12, 0x4a, 0x0a, + 0x0d, 0x57, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x12, 0x1b, + 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x42, 0x61, 0x6c, + 0x61, 0x6e, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, - 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, - 0x2e, 0x57, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4d, 0x0a, 0x0e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, - 0x6c, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x12, 0x1c, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, - 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, - 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4b, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x54, 0x72, 0x61, 0x6e, - 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x1d, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, - 0x2e, 0x47, 0x65, 0x74, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, - 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x65, 0x74, 0x61, 0x69, - 0x6c, 0x73, 0x12, 0x44, 0x0a, 0x0b, 0x45, 0x73, 0x74, 0x69, 0x6d, 0x61, 0x74, 0x65, 0x46, 0x65, - 0x65, 0x12, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x45, 0x73, 0x74, 0x69, 0x6d, 0x61, - 0x74, 0x65, 0x46, 0x65, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x6c, - 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x45, 0x73, 0x74, 0x69, 0x6d, 0x61, 0x74, 0x65, 0x46, 0x65, 0x65, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3e, 0x0a, 0x09, 0x53, 0x65, 0x6e, 0x64, - 0x43, 0x6f, 0x69, 0x6e, 0x73, 0x12, 0x17, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, - 0x6e, 0x64, 0x43, 0x6f, 0x69, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, - 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x43, 0x6f, 0x69, 0x6e, 0x73, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x44, 0x0a, 0x0b, 0x4c, 0x69, 0x73, 0x74, - 0x55, 0x6e, 0x73, 0x70, 0x65, 0x6e, 0x74, 0x12, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, - 0x4c, 0x69, 0x73, 0x74, 0x55, 0x6e, 0x73, 0x70, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x55, - 0x6e, 0x73, 0x70, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4c, - 0x0a, 0x15, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x54, 0x72, 0x61, 0x6e, 0x73, - 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x1d, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, - 0x47, 0x65, 0x74, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x12, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x54, - 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x30, 0x01, 0x12, 0x3b, 0x0a, 0x08, - 0x53, 0x65, 0x6e, 0x64, 0x4d, 0x61, 0x6e, 0x79, 0x12, 0x16, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, - 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x4d, 0x61, 0x6e, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x17, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x4d, 0x61, 0x6e, - 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x41, 0x0a, 0x0a, 0x4e, 0x65, 0x77, - 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x18, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, - 0x4e, 0x65, 0x77, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4e, 0x65, 0x77, 0x41, 0x64, 0x64, - 0x72, 0x65, 0x73, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x44, 0x0a, 0x0b, - 0x53, 0x69, 0x67, 0x6e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x19, 0x2e, 0x6c, 0x6e, - 0x72, 0x70, 0x63, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, - 0x69, 0x67, 0x6e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x4a, 0x0a, 0x0d, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x4d, 0x65, 0x73, 0x73, - 0x61, 0x67, 0x65, 0x12, 0x1b, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x56, 0x65, 0x72, 0x69, - 0x66, 0x79, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x1c, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x4d, - 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x44, - 0x0a, 0x0b, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x50, 0x65, 0x65, 0x72, 0x12, 0x19, 0x2e, - 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x50, 0x65, 0x65, - 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, - 0x2e, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x50, 0x65, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4d, 0x0a, 0x0e, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x6e, 0x6e, 0x65, - 0x63, 0x74, 0x50, 0x65, 0x65, 0x72, 0x12, 0x1c, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x44, - 0x69, 0x73, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x50, 0x65, 0x65, 0x72, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x44, 0x69, 0x73, - 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x50, 0x65, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x3e, 0x0a, 0x09, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x65, 0x65, 0x72, 0x73, - 0x12, 0x17, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x65, 0x65, - 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x6c, 0x6e, 0x72, 0x70, - 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x65, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x47, 0x0a, 0x13, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, - 0x50, 0x65, 0x65, 0x72, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x1c, 0x2e, 0x6c, 0x6e, 0x72, - 0x70, 0x63, 0x2e, 0x50, 0x65, 0x65, 0x72, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x53, 0x75, 0x62, 0x73, - 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x1a, 0x10, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, - 0x2e, 0x50, 0x65, 0x65, 0x72, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x30, 0x01, 0x12, 0x38, 0x0a, 0x07, - 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x15, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, - 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, - 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x47, 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x44, 0x65, 0x62, - 0x75, 0x67, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x47, - 0x65, 0x74, 0x44, 0x65, 0x62, 0x75, 0x67, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x44, 0x65, - 0x62, 0x75, 0x67, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, - 0x50, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x52, 0x65, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x49, 0x6e, - 0x66, 0x6f, 0x12, 0x1d, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x65, - 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x1e, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x65, 0x63, - 0x6f, 0x76, 0x65, 0x72, 0x79, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x50, 0x0a, 0x0f, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x43, 0x68, 0x61, 0x6e, - 0x6e, 0x65, 0x6c, 0x73, 0x12, 0x1d, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x65, 0x6e, - 0x64, 0x69, 0x6e, 0x67, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x65, 0x6e, 0x64, - 0x69, 0x6e, 0x67, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x47, 0x0a, 0x0c, 0x4c, 0x69, 0x73, 0x74, 0x43, 0x68, 0x61, 0x6e, 0x6e, - 0x65, 0x6c, 0x73, 0x12, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, - 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x1b, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x43, 0x68, 0x61, 0x6e, - 0x6e, 0x65, 0x6c, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x56, 0x0a, 0x16, - 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, - 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x1f, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, - 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x53, 0x75, 0x62, 0x73, 0x63, - 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x1a, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, - 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x55, 0x70, 0x64, 0x61, - 0x74, 0x65, 0x30, 0x01, 0x12, 0x4d, 0x0a, 0x0e, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x64, 0x43, 0x68, - 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x12, 0x1c, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, - 0x6c, 0x6f, 0x73, 0x65, 0x64, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x6c, 0x6f, - 0x73, 0x65, 0x64, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x41, 0x0a, 0x0f, 0x4f, 0x70, 0x65, 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x6e, - 0x65, 0x6c, 0x53, 0x79, 0x6e, 0x63, 0x12, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4f, - 0x70, 0x65, 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x13, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, - 0x6c, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x43, 0x0a, 0x0b, 0x4f, 0x70, 0x65, 0x6e, 0x43, 0x68, - 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x12, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4f, 0x70, - 0x65, 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x17, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x53, 0x74, 0x61, - 0x74, 0x75, 0x73, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x30, 0x01, 0x12, 0x53, 0x0a, 0x10, 0x42, - 0x61, 0x74, 0x63, 0x68, 0x4f, 0x70, 0x65, 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x12, - 0x1e, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x42, 0x61, 0x74, 0x63, 0x68, 0x4f, 0x70, 0x65, - 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x1f, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x42, 0x61, 0x74, 0x63, 0x68, 0x4f, 0x70, 0x65, - 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x12, 0x4c, 0x0a, 0x10, 0x46, 0x75, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x53, 0x74, 0x61, 0x74, 0x65, - 0x53, 0x74, 0x65, 0x70, 0x12, 0x1b, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x75, 0x6e, - 0x64, 0x69, 0x6e, 0x67, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x73, - 0x67, 0x1a, 0x1b, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x75, 0x6e, 0x64, 0x69, 0x6e, - 0x67, 0x53, 0x74, 0x61, 0x74, 0x65, 0x53, 0x74, 0x65, 0x70, 0x52, 0x65, 0x73, 0x70, 0x12, 0x50, - 0x0a, 0x0f, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x6f, - 0x72, 0x12, 0x1c, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, - 0x6c, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x1a, - 0x1b, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x41, - 0x63, 0x63, 0x65, 0x70, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x28, 0x01, 0x30, 0x01, - 0x12, 0x46, 0x0a, 0x0c, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, - 0x12, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x43, 0x68, - 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x6c, - 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, - 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x30, 0x01, 0x12, 0x4d, 0x0a, 0x0e, 0x41, 0x62, 0x61, 0x6e, - 0x64, 0x6f, 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x12, 0x1c, 0x2e, 0x6c, 0x6e, 0x72, - 0x70, 0x63, 0x2e, 0x41, 0x62, 0x61, 0x6e, 0x64, 0x6f, 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, - 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, - 0x2e, 0x41, 0x62, 0x61, 0x6e, 0x64, 0x6f, 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3f, 0x0a, 0x0b, 0x53, 0x65, 0x6e, 0x64, 0x50, - 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x12, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, - 0x65, 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x13, 0x2e, 0x6c, 0x6e, 0x72, - 0x70, 0x63, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0x03, 0x88, 0x02, 0x01, 0x28, 0x01, 0x30, 0x01, 0x12, 0x3a, 0x0a, 0x0f, 0x53, 0x65, 0x6e, 0x64, - 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x53, 0x79, 0x6e, 0x63, 0x12, 0x12, 0x2e, 0x6c, 0x6e, - 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x13, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x46, 0x0a, 0x0b, 0x53, 0x65, 0x6e, 0x64, 0x54, 0x6f, 0x52, 0x6f, - 0x75, 0x74, 0x65, 0x12, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x6e, 0x64, - 0x54, 0x6f, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x13, - 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x03, 0x88, 0x02, 0x01, 0x28, 0x01, 0x30, 0x01, 0x12, 0x41, 0x0a, 0x0f, - 0x53, 0x65, 0x6e, 0x64, 0x54, 0x6f, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x53, 0x79, 0x6e, 0x63, 0x12, - 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x54, 0x6f, 0x52, 0x6f, - 0x75, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x13, 0x2e, 0x6c, 0x6e, 0x72, - 0x70, 0x63, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, - 0x37, 0x0a, 0x0a, 0x41, 0x64, 0x64, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x12, 0x0e, 0x2e, - 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x1a, 0x19, 0x2e, - 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x64, 0x64, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x45, 0x0a, 0x0c, 0x4c, 0x69, 0x73, 0x74, - 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x73, 0x12, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, - 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, - 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, - 0x33, 0x0a, 0x0d, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, - 0x12, 0x12, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, - 0x48, 0x61, 0x73, 0x68, 0x1a, 0x0e, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x49, 0x6e, 0x76, - 0x6f, 0x69, 0x63, 0x65, 0x12, 0x41, 0x0a, 0x11, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, - 0x65, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x73, 0x12, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, - 0x63, 0x2e, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, - 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x1a, 0x0e, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x49, 0x6e, - 0x76, 0x6f, 0x69, 0x63, 0x65, 0x30, 0x01, 0x12, 0x32, 0x0a, 0x0c, 0x44, 0x65, 0x63, 0x6f, 0x64, - 0x65, 0x50, 0x61, 0x79, 0x52, 0x65, 0x71, 0x12, 0x13, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, - 0x50, 0x61, 0x79, 0x52, 0x65, 0x71, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x1a, 0x0d, 0x2e, 0x6c, - 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x61, 0x79, 0x52, 0x65, 0x71, 0x12, 0x47, 0x0a, 0x0c, 0x4c, - 0x69, 0x73, 0x74, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x1a, 0x2e, 0x6c, 0x6e, - 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x73, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, - 0x4c, 0x69, 0x73, 0x74, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4a, 0x0a, 0x0d, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x50, 0x61, - 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x1b, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x44, 0x65, - 0x6c, 0x65, 0x74, 0x65, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, - 0x65, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x12, 0x56, 0x0a, 0x11, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x41, 0x6c, 0x6c, 0x50, 0x61, 0x79, - 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x1f, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x44, 0x65, - 0x6c, 0x65, 0x74, 0x65, 0x41, 0x6c, 0x6c, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x44, - 0x65, 0x6c, 0x65, 0x74, 0x65, 0x41, 0x6c, 0x6c, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x73, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x40, 0x0a, 0x0d, 0x44, 0x65, 0x73, 0x63, - 0x72, 0x69, 0x62, 0x65, 0x47, 0x72, 0x61, 0x70, 0x68, 0x12, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, - 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x47, 0x72, 0x61, 0x70, 0x68, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x13, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, - 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x47, 0x72, 0x61, 0x70, 0x68, 0x12, 0x47, 0x0a, 0x0e, 0x47, 0x65, - 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x12, 0x19, 0x2e, 0x6c, - 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, - 0x4e, 0x6f, 0x64, 0x65, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x39, 0x0a, 0x0b, 0x47, 0x65, 0x74, 0x43, 0x68, 0x61, 0x6e, 0x49, 0x6e, - 0x66, 0x6f, 0x12, 0x16, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x49, - 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x12, 0x2e, 0x6c, 0x6e, 0x72, - 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x45, 0x64, 0x67, 0x65, 0x12, 0x36, - 0x0a, 0x0b, 0x47, 0x65, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x16, 0x2e, - 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x0f, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4e, 0x6f, - 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x44, 0x0a, 0x0b, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, - 0x6f, 0x75, 0x74, 0x65, 0x73, 0x12, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x51, 0x75, - 0x65, 0x72, 0x79, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x6f, - 0x75, 0x74, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3f, 0x0a, 0x0e, - 0x47, 0x65, 0x74, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x19, - 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x49, 0x6e, - 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x12, 0x2e, 0x6c, 0x6e, 0x72, 0x70, - 0x63, 0x2e, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x35, 0x0a, - 0x0a, 0x53, 0x74, 0x6f, 0x70, 0x44, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x12, 0x12, 0x2e, 0x6c, 0x6e, - 0x72, 0x70, 0x63, 0x2e, 0x53, 0x74, 0x6f, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x13, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x74, 0x6f, 0x70, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x57, 0x0a, 0x15, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, - 0x65, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x47, 0x72, 0x61, 0x70, 0x68, 0x12, 0x20, 0x2e, - 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x72, 0x61, 0x70, 0x68, 0x54, 0x6f, 0x70, 0x6f, 0x6c, - 0x6f, 0x67, 0x79, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x1a, - 0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x72, 0x61, 0x70, 0x68, 0x54, 0x6f, 0x70, - 0x6f, 0x6c, 0x6f, 0x67, 0x79, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x30, 0x01, 0x12, 0x41, 0x0a, - 0x0a, 0x44, 0x65, 0x62, 0x75, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x18, 0x2e, 0x6c, 0x6e, - 0x72, 0x70, 0x63, 0x2e, 0x44, 0x65, 0x62, 0x75, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x44, 0x65, - 0x62, 0x75, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x12, 0x3e, 0x0a, 0x09, 0x46, 0x65, 0x65, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x17, 0x2e, - 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x65, 0x65, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x46, - 0x65, 0x65, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x12, 0x4e, 0x0a, 0x13, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, - 0x6c, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, - 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x6f, 0x6c, 0x69, - 0x63, 0x79, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x12, 0x56, 0x0a, 0x11, 0x46, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x48, 0x69, - 0x73, 0x74, 0x6f, 0x72, 0x79, 0x12, 0x1f, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x6f, - 0x72, 0x77, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x46, - 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4e, 0x0a, 0x13, 0x45, 0x78, 0x70, 0x6f, - 0x72, 0x74, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x12, - 0x21, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x45, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x43, 0x68, - 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x14, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, - 0x65, 0x6c, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x12, 0x54, 0x0a, 0x17, 0x45, 0x78, 0x70, 0x6f, - 0x72, 0x74, 0x41, 0x6c, 0x6c, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x42, 0x61, 0x63, 0x6b, - 0x75, 0x70, 0x73, 0x12, 0x1e, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, - 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x45, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, - 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x12, 0x4e, - 0x0a, 0x10, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x43, 0x68, 0x61, 0x6e, 0x42, 0x61, 0x63, 0x6b, - 0x75, 0x70, 0x12, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x42, - 0x61, 0x63, 0x6b, 0x75, 0x70, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x1a, 0x1f, 0x2e, - 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x43, 0x68, 0x61, 0x6e, - 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x56, - 0x0a, 0x15, 0x52, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, - 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x73, 0x12, 0x1f, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, - 0x52, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x42, 0x61, 0x63, 0x6b, 0x75, - 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, - 0x2e, 0x52, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x58, 0x0a, 0x17, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, - 0x69, 0x62, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, - 0x73, 0x12, 0x20, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, - 0x6c, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, - 0x69, 0x6f, 0x6e, 0x1a, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, - 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x30, 0x01, - 0x12, 0x47, 0x0a, 0x0c, 0x42, 0x61, 0x6b, 0x65, 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, - 0x12, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x42, 0x61, 0x6b, 0x65, 0x4d, 0x61, 0x63, - 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x6c, - 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x42, 0x61, 0x6b, 0x65, 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, - 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x50, 0x0a, 0x0f, 0x4c, 0x69, 0x73, - 0x74, 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x49, 0x44, 0x73, 0x12, 0x1d, 0x2e, 0x6c, - 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, - 0x6e, 0x49, 0x44, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x6c, 0x6e, - 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, - 0x49, 0x44, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x53, 0x0a, 0x10, 0x44, - 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x49, 0x44, 0x12, - 0x1e, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4d, 0x61, - 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x49, 0x44, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x1f, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4d, 0x61, - 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x49, 0x44, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x12, 0x50, 0x0a, 0x0f, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, - 0x6f, 0x6e, 0x73, 0x12, 0x1d, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, - 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x50, - 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x53, 0x0a, 0x18, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x4d, 0x61, 0x63, 0x61, 0x72, - 0x6f, 0x6f, 0x6e, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x1a, - 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x4d, 0x61, 0x63, 0x50, - 0x65, 0x72, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x6c, 0x6e, 0x72, - 0x70, 0x63, 0x2e, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x4d, 0x61, 0x63, 0x50, 0x65, 0x72, 0x6d, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x56, 0x0a, 0x15, 0x52, 0x65, 0x67, 0x69, 0x73, - 0x74, 0x65, 0x72, 0x52, 0x50, 0x43, 0x4d, 0x69, 0x64, 0x64, 0x6c, 0x65, 0x77, 0x61, 0x72, 0x65, - 0x12, 0x1c, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x50, 0x43, 0x4d, 0x69, 0x64, 0x64, - 0x6c, 0x65, 0x77, 0x61, 0x72, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x1a, 0x1b, - 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x50, 0x43, 0x4d, 0x69, 0x64, 0x64, 0x6c, 0x65, - 0x77, 0x61, 0x72, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x28, 0x01, 0x30, 0x01, 0x12, - 0x56, 0x0a, 0x11, 0x53, 0x65, 0x6e, 0x64, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x4d, 0x65, 0x73, - 0x73, 0x61, 0x67, 0x65, 0x12, 0x1f, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x6e, - 0x64, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, - 0x6e, 0x64, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x58, 0x0a, 0x17, 0x53, 0x75, 0x62, 0x73, 0x63, - 0x72, 0x69, 0x62, 0x65, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, - 0x65, 0x73, 0x12, 0x25, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x75, 0x62, 0x73, 0x63, - 0x72, 0x69, 0x62, 0x65, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, - 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x14, 0x2e, 0x6c, 0x6e, 0x72, 0x70, - 0x63, 0x2e, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x30, - 0x01, 0x12, 0x44, 0x0a, 0x0b, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x65, 0x73, - 0x12, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x6c, 0x69, - 0x61, 0x73, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x6c, 0x6e, - 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x65, 0x73, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5f, 0x0a, 0x14, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, - 0x70, 0x48, 0x74, 0x6c, 0x63, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x12, - 0x22, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x48, 0x74, - 0x6c, 0x63, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x6f, 0x6f, 0x6b, - 0x75, 0x70, 0x48, 0x74, 0x6c, 0x63, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x27, 0x5a, 0x25, 0x67, 0x69, 0x74, 0x68, - 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x6e, 0x69, 0x6e, 0x67, - 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x2f, 0x6c, 0x6e, 0x64, 0x2f, 0x6c, 0x6e, 0x72, 0x70, - 0x63, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4d, 0x0a, 0x0e, 0x43, 0x68, 0x61, + 0x6e, 0x6e, 0x65, 0x6c, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x12, 0x1c, 0x2e, 0x6c, 0x6e, + 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x42, 0x61, 0x6c, 0x61, 0x6e, + 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x6c, 0x6e, 0x72, 0x70, + 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4b, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x54, + 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x1d, 0x2e, 0x6c, 0x6e, + 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, + 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x6c, 0x6e, 0x72, + 0x70, 0x63, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x65, + 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x44, 0x0a, 0x0b, 0x45, 0x73, 0x74, 0x69, 0x6d, 0x61, 0x74, + 0x65, 0x46, 0x65, 0x65, 0x12, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x45, 0x73, 0x74, + 0x69, 0x6d, 0x61, 0x74, 0x65, 0x46, 0x65, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x45, 0x73, 0x74, 0x69, 0x6d, 0x61, 0x74, 0x65, + 0x46, 0x65, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3e, 0x0a, 0x09, 0x53, + 0x65, 0x6e, 0x64, 0x43, 0x6f, 0x69, 0x6e, 0x73, 0x12, 0x17, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, + 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x43, 0x6f, 0x69, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x18, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x43, 0x6f, + 0x69, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x44, 0x0a, 0x0b, 0x4c, + 0x69, 0x73, 0x74, 0x55, 0x6e, 0x73, 0x70, 0x65, 0x6e, 0x74, 0x12, 0x19, 0x2e, 0x6c, 0x6e, 0x72, + 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x55, 0x6e, 0x73, 0x70, 0x65, 0x6e, 0x74, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, + 0x73, 0x74, 0x55, 0x6e, 0x73, 0x70, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x4c, 0x0a, 0x15, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x54, 0x72, + 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x1d, 0x2e, 0x6c, 0x6e, 0x72, + 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, + 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x12, 0x2e, 0x6c, 0x6e, 0x72, 0x70, + 0x63, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x30, 0x01, 0x12, + 0x3b, 0x0a, 0x08, 0x53, 0x65, 0x6e, 0x64, 0x4d, 0x61, 0x6e, 0x79, 0x12, 0x16, 0x2e, 0x6c, 0x6e, + 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x4d, 0x61, 0x6e, 0x79, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x6e, 0x64, + 0x4d, 0x61, 0x6e, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x41, 0x0a, 0x0a, + 0x4e, 0x65, 0x77, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x18, 0x2e, 0x6c, 0x6e, 0x72, + 0x70, 0x63, 0x2e, 0x4e, 0x65, 0x77, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4e, 0x65, 0x77, + 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x44, 0x0a, 0x0b, 0x53, 0x69, 0x67, 0x6e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x19, + 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x4d, 0x65, 0x73, 0x73, 0x61, + 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, + 0x63, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4a, 0x0a, 0x0d, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x4d, + 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x1b, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x56, + 0x65, 0x72, 0x69, 0x66, 0x79, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x56, 0x65, 0x72, 0x69, + 0x66, 0x79, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x44, 0x0a, 0x0b, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x50, 0x65, 0x65, 0x72, + 0x12, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, + 0x50, 0x65, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x6c, 0x6e, + 0x72, 0x70, 0x63, 0x2e, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x50, 0x65, 0x65, 0x72, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4d, 0x0a, 0x0e, 0x44, 0x69, 0x73, 0x63, 0x6f, + 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x50, 0x65, 0x65, 0x72, 0x12, 0x1c, 0x2e, 0x6c, 0x6e, 0x72, 0x70, + 0x63, 0x2e, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x50, 0x65, 0x65, 0x72, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, + 0x44, 0x69, 0x73, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x50, 0x65, 0x65, 0x72, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3e, 0x0a, 0x09, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x65, + 0x65, 0x72, 0x73, 0x12, 0x17, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, + 0x50, 0x65, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x6c, + 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x65, 0x65, 0x72, 0x73, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x47, 0x0a, 0x13, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, + 0x69, 0x62, 0x65, 0x50, 0x65, 0x65, 0x72, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x1c, 0x2e, + 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x65, 0x65, 0x72, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x53, + 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x1a, 0x10, 0x2e, 0x6c, 0x6e, + 0x72, 0x70, 0x63, 0x2e, 0x50, 0x65, 0x65, 0x72, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x30, 0x01, 0x12, + 0x38, 0x0a, 0x07, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x15, 0x2e, 0x6c, 0x6e, 0x72, + 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x16, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, + 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x47, 0x0a, 0x0c, 0x47, 0x65, 0x74, + 0x44, 0x65, 0x62, 0x75, 0x67, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, + 0x63, 0x2e, 0x47, 0x65, 0x74, 0x44, 0x65, 0x62, 0x75, 0x67, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, + 0x74, 0x44, 0x65, 0x62, 0x75, 0x67, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x50, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x52, 0x65, 0x63, 0x6f, 0x76, 0x65, 0x72, + 0x79, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1d, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, + 0x74, 0x52, 0x65, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, + 0x52, 0x65, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x50, 0x0a, 0x0f, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x43, + 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x12, 0x1d, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, + 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x50, + 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x47, 0x0a, 0x0c, 0x4c, 0x69, 0x73, 0x74, 0x43, 0x68, + 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x12, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4c, + 0x69, 0x73, 0x74, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x43, + 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x56, 0x0a, 0x16, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x43, 0x68, 0x61, 0x6e, + 0x6e, 0x65, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x1f, 0x2e, 0x6c, 0x6e, 0x72, 0x70, + 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x53, 0x75, + 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x1a, 0x19, 0x2e, 0x6c, 0x6e, 0x72, + 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x55, + 0x70, 0x64, 0x61, 0x74, 0x65, 0x30, 0x01, 0x12, 0x4d, 0x0a, 0x0e, 0x43, 0x6c, 0x6f, 0x73, 0x65, + 0x64, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x12, 0x1c, 0x2e, 0x6c, 0x6e, 0x72, 0x70, + 0x63, 0x2e, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x64, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, + 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x64, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x41, 0x0a, 0x0f, 0x4f, 0x70, 0x65, 0x6e, 0x43, 0x68, + 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x53, 0x79, 0x6e, 0x63, 0x12, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, + 0x63, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x13, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, + 0x6e, 0x6e, 0x65, 0x6c, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x43, 0x0a, 0x0b, 0x4f, 0x70, 0x65, + 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x12, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, + 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4f, 0x70, 0x65, 0x6e, + 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x30, 0x01, 0x12, 0x53, + 0x0a, 0x10, 0x42, 0x61, 0x74, 0x63, 0x68, 0x4f, 0x70, 0x65, 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x6e, + 0x65, 0x6c, 0x12, 0x1e, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x42, 0x61, 0x74, 0x63, 0x68, + 0x4f, 0x70, 0x65, 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x42, 0x61, 0x74, 0x63, 0x68, + 0x4f, 0x70, 0x65, 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x4c, 0x0a, 0x10, 0x46, 0x75, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x53, 0x74, + 0x61, 0x74, 0x65, 0x53, 0x74, 0x65, 0x70, 0x12, 0x1b, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, + 0x46, 0x75, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x6f, + 0x6e, 0x4d, 0x73, 0x67, 0x1a, 0x1b, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x75, 0x6e, + 0x64, 0x69, 0x6e, 0x67, 0x53, 0x74, 0x61, 0x74, 0x65, 0x53, 0x74, 0x65, 0x70, 0x52, 0x65, 0x73, + 0x70, 0x12, 0x50, 0x0a, 0x0f, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x41, 0x63, 0x63, 0x65, + 0x70, 0x74, 0x6f, 0x72, 0x12, 0x1c, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, + 0x6e, 0x6e, 0x65, 0x6c, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x1a, 0x1b, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, + 0x65, 0x6c, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x28, + 0x01, 0x30, 0x01, 0x12, 0x46, 0x0a, 0x0c, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x43, 0x68, 0x61, 0x6e, + 0x6e, 0x65, 0x6c, 0x12, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x6c, 0x6f, 0x73, + 0x65, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x18, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x53, 0x74, 0x61, + 0x74, 0x75, 0x73, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x30, 0x01, 0x12, 0x4d, 0x0a, 0x0e, 0x41, + 0x62, 0x61, 0x6e, 0x64, 0x6f, 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x12, 0x1c, 0x2e, + 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x62, 0x61, 0x6e, 0x64, 0x6f, 0x6e, 0x43, 0x68, 0x61, + 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x6c, 0x6e, + 0x72, 0x70, 0x63, 0x2e, 0x41, 0x62, 0x61, 0x6e, 0x64, 0x6f, 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x6e, + 0x65, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3f, 0x0a, 0x0b, 0x53, 0x65, + 0x6e, 0x64, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x12, 0x2e, 0x6c, 0x6e, 0x72, 0x70, + 0x63, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x13, 0x2e, + 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x22, 0x03, 0x88, 0x02, 0x01, 0x28, 0x01, 0x30, 0x01, 0x12, 0x3a, 0x0a, 0x0f, 0x53, + 0x65, 0x6e, 0x64, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x53, 0x79, 0x6e, 0x63, 0x12, 0x12, + 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x13, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x46, 0x0a, 0x0b, 0x53, 0x65, 0x6e, 0x64, 0x54, + 0x6f, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x12, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, + 0x65, 0x6e, 0x64, 0x54, 0x6f, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x13, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x03, 0x88, 0x02, 0x01, 0x28, 0x01, 0x30, 0x01, 0x12, + 0x41, 0x0a, 0x0f, 0x53, 0x65, 0x6e, 0x64, 0x54, 0x6f, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x53, 0x79, + 0x6e, 0x63, 0x12, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x54, + 0x6f, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x13, 0x2e, + 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x37, 0x0a, 0x0a, 0x41, 0x64, 0x64, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, + 0x12, 0x0e, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, + 0x1a, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x64, 0x64, 0x49, 0x6e, 0x76, 0x6f, + 0x69, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x45, 0x0a, 0x0c, 0x4c, + 0x69, 0x73, 0x74, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x73, 0x12, 0x19, 0x2e, 0x6c, 0x6e, + 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4c, + 0x69, 0x73, 0x74, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x33, 0x0a, 0x0d, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x49, 0x6e, 0x76, 0x6f, + 0x69, 0x63, 0x65, 0x12, 0x12, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x61, 0x79, 0x6d, + 0x65, 0x6e, 0x74, 0x48, 0x61, 0x73, 0x68, 0x1a, 0x0e, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, + 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x12, 0x41, 0x0a, 0x11, 0x53, 0x75, 0x62, 0x73, 0x63, + 0x72, 0x69, 0x62, 0x65, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x73, 0x12, 0x1a, 0x2e, 0x6c, + 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x53, 0x75, 0x62, 0x73, + 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x1a, 0x0e, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, + 0x2e, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x30, 0x01, 0x12, 0x32, 0x0a, 0x0c, 0x44, 0x65, + 0x63, 0x6f, 0x64, 0x65, 0x50, 0x61, 0x79, 0x52, 0x65, 0x71, 0x12, 0x13, 0x2e, 0x6c, 0x6e, 0x72, + 0x70, 0x63, 0x2e, 0x50, 0x61, 0x79, 0x52, 0x65, 0x71, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x1a, + 0x0d, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x61, 0x79, 0x52, 0x65, 0x71, 0x12, 0x47, + 0x0a, 0x0c, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x1a, + 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x61, 0x79, 0x6d, 0x65, + 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x6c, 0x6e, 0x72, + 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4a, 0x0a, 0x0d, 0x44, 0x65, 0x6c, 0x65, 0x74, + 0x65, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x1b, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, + 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x44, 0x65, + 0x6c, 0x65, 0x74, 0x65, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x56, 0x0a, 0x11, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x41, 0x6c, 0x6c, + 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x1f, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, + 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x41, 0x6c, 0x6c, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, + 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x6c, 0x6e, 0x72, 0x70, + 0x63, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x41, 0x6c, 0x6c, 0x50, 0x61, 0x79, 0x6d, 0x65, + 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x40, 0x0a, 0x0d, 0x44, + 0x65, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x47, 0x72, 0x61, 0x70, 0x68, 0x12, 0x1a, 0x2e, 0x6c, + 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x47, 0x72, 0x61, 0x70, + 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x13, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, + 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x47, 0x72, 0x61, 0x70, 0x68, 0x12, 0x47, 0x0a, + 0x0e, 0x47, 0x65, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x12, + 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x4d, 0x65, 0x74, 0x72, + 0x69, 0x63, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, + 0x70, 0x63, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x39, 0x0a, 0x0b, 0x47, 0x65, 0x74, 0x43, 0x68, 0x61, + 0x6e, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x16, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, + 0x61, 0x6e, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x12, 0x2e, + 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x45, 0x64, 0x67, + 0x65, 0x12, 0x36, 0x0a, 0x0b, 0x47, 0x65, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, + 0x12, 0x16, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, + 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x0f, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, + 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x44, 0x0a, 0x0b, 0x51, 0x75, 0x65, + 0x72, 0x79, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x12, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, + 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x51, 0x75, 0x65, 0x72, + 0x79, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x3f, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x49, 0x6e, 0x66, + 0x6f, 0x12, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, + 0x6b, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x12, 0x2e, 0x6c, + 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x49, 0x6e, 0x66, 0x6f, + 0x12, 0x35, 0x0a, 0x0a, 0x53, 0x74, 0x6f, 0x70, 0x44, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x12, 0x12, + 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x74, 0x6f, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x13, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x74, 0x6f, 0x70, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x57, 0x0a, 0x15, 0x53, 0x75, 0x62, 0x73, 0x63, + 0x72, 0x69, 0x62, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x47, 0x72, 0x61, 0x70, 0x68, + 0x12, 0x20, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x72, 0x61, 0x70, 0x68, 0x54, 0x6f, + 0x70, 0x6f, 0x6c, 0x6f, 0x67, 0x79, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, + 0x6f, 0x6e, 0x1a, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x72, 0x61, 0x70, 0x68, + 0x54, 0x6f, 0x70, 0x6f, 0x6c, 0x6f, 0x67, 0x79, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x30, 0x01, + 0x12, 0x41, 0x0a, 0x0a, 0x44, 0x65, 0x62, 0x75, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x18, + 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x44, 0x65, 0x62, 0x75, 0x67, 0x4c, 0x65, 0x76, 0x65, + 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, + 0x2e, 0x44, 0x65, 0x62, 0x75, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x3e, 0x0a, 0x09, 0x46, 0x65, 0x65, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, + 0x12, 0x17, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x65, 0x65, 0x52, 0x65, 0x70, 0x6f, + 0x72, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x6c, 0x6e, 0x72, 0x70, + 0x63, 0x2e, 0x46, 0x65, 0x65, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x4e, 0x0a, 0x13, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x43, 0x68, 0x61, + 0x6e, 0x6e, 0x65, 0x6c, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, + 0x70, 0x63, 0x2e, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x50, + 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x56, 0x0a, 0x11, 0x46, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x69, 0x6e, + 0x67, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x12, 0x1f, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, + 0x2e, 0x46, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x48, 0x69, 0x73, 0x74, 0x6f, + 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x6c, 0x6e, 0x72, 0x70, + 0x63, 0x2e, 0x46, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x48, 0x69, 0x73, 0x74, + 0x6f, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4e, 0x0a, 0x13, 0x45, + 0x78, 0x70, 0x6f, 0x72, 0x74, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x42, 0x61, 0x63, 0x6b, + 0x75, 0x70, 0x12, 0x21, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x45, 0x78, 0x70, 0x6f, 0x72, + 0x74, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x14, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, + 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x12, 0x54, 0x0a, 0x17, 0x45, + 0x78, 0x70, 0x6f, 0x72, 0x74, 0x41, 0x6c, 0x6c, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x42, + 0x61, 0x63, 0x6b, 0x75, 0x70, 0x73, 0x12, 0x1e, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, + 0x68, 0x61, 0x6e, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x45, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, + 0x68, 0x61, 0x6e, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, + 0x74, 0x12, 0x4e, 0x0a, 0x10, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x43, 0x68, 0x61, 0x6e, 0x42, + 0x61, 0x63, 0x6b, 0x75, 0x70, 0x12, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, + 0x61, 0x6e, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, + 0x1a, 0x1f, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x43, + 0x68, 0x61, 0x6e, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x56, 0x0a, 0x15, 0x52, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x43, 0x68, 0x61, 0x6e, + 0x6e, 0x65, 0x6c, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x73, 0x12, 0x1f, 0x2e, 0x6c, 0x6e, 0x72, + 0x70, 0x63, 0x2e, 0x52, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x42, 0x61, + 0x63, 0x6b, 0x75, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x6c, 0x6e, + 0x72, 0x70, 0x63, 0x2e, 0x52, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x42, 0x61, 0x63, 0x6b, 0x75, + 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x58, 0x0a, 0x17, 0x53, 0x75, 0x62, + 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x42, 0x61, 0x63, + 0x6b, 0x75, 0x70, 0x73, 0x12, 0x20, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, + 0x6e, 0x6e, 0x65, 0x6c, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, + 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x1a, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, + 0x68, 0x61, 0x6e, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, + 0x74, 0x30, 0x01, 0x12, 0x47, 0x0a, 0x0c, 0x42, 0x61, 0x6b, 0x65, 0x4d, 0x61, 0x63, 0x61, 0x72, + 0x6f, 0x6f, 0x6e, 0x12, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x42, 0x61, 0x6b, 0x65, + 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x1b, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x42, 0x61, 0x6b, 0x65, 0x4d, 0x61, 0x63, 0x61, + 0x72, 0x6f, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x50, 0x0a, 0x0f, + 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x49, 0x44, 0x73, 0x12, + 0x1d, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x61, 0x63, 0x61, + 0x72, 0x6f, 0x6f, 0x6e, 0x49, 0x44, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, + 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x61, 0x63, 0x61, 0x72, + 0x6f, 0x6f, 0x6e, 0x49, 0x44, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x53, + 0x0a, 0x10, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, + 0x49, 0x44, 0x12, 0x1e, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, + 0x65, 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x49, 0x44, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, + 0x65, 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x49, 0x44, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x50, 0x0a, 0x0f, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x65, 0x72, 0x6d, 0x69, + 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x1d, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4c, + 0x69, 0x73, 0x74, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, + 0x73, 0x74, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x53, 0x0a, 0x18, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x4d, 0x61, + 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, + 0x73, 0x12, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x4d, + 0x61, 0x63, 0x50, 0x65, 0x72, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, + 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x4d, 0x61, 0x63, 0x50, 0x65, + 0x72, 0x6d, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x56, 0x0a, 0x15, 0x52, 0x65, + 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x52, 0x50, 0x43, 0x4d, 0x69, 0x64, 0x64, 0x6c, 0x65, 0x77, + 0x61, 0x72, 0x65, 0x12, 0x1c, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x50, 0x43, 0x4d, + 0x69, 0x64, 0x64, 0x6c, 0x65, 0x77, 0x61, 0x72, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x1a, 0x1b, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x50, 0x43, 0x4d, 0x69, 0x64, + 0x64, 0x6c, 0x65, 0x77, 0x61, 0x72, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x28, 0x01, + 0x30, 0x01, 0x12, 0x56, 0x0a, 0x11, 0x53, 0x65, 0x6e, 0x64, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, + 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x1f, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, + 0x53, 0x65, 0x6e, 0x64, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, + 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, + 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x4d, 0x65, 0x73, 0x73, 0x61, + 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x58, 0x0a, 0x17, 0x53, 0x75, + 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x4d, 0x65, 0x73, + 0x73, 0x61, 0x67, 0x65, 0x73, 0x12, 0x25, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x75, + 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x4d, 0x65, 0x73, + 0x73, 0x61, 0x67, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x14, 0x2e, 0x6c, + 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x4d, 0x65, 0x73, 0x73, 0x61, + 0x67, 0x65, 0x30, 0x01, 0x12, 0x44, 0x0a, 0x0b, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x6c, 0x69, 0x61, + 0x73, 0x65, 0x73, 0x12, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, + 0x41, 0x6c, 0x69, 0x61, 0x73, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, + 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x6c, 0x69, 0x61, 0x73, + 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5f, 0x0a, 0x14, 0x4c, 0x6f, + 0x6f, 0x6b, 0x75, 0x70, 0x48, 0x74, 0x6c, 0x63, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, + 0x6f, 0x6e, 0x12, 0x22, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, + 0x70, 0x48, 0x74, 0x6c, 0x63, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4c, + 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x48, 0x74, 0x6c, 0x63, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, + 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x27, 0x5a, 0x25, 0x67, + 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x6e, + 0x69, 0x6e, 0x67, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x2f, 0x6c, 0x6e, 0x64, 0x2f, 0x6c, + 0x6e, 0x72, 0x70, 0x63, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/lnrpc/lightning.proto b/lnrpc/lightning.proto index 0bdee236c0..6d975c8c3e 100644 --- a/lnrpc/lightning.proto +++ b/lnrpc/lightning.proto @@ -3293,6 +3293,20 @@ message Route { The total amount in millisatoshis. */ int64 total_amt_msat = 6; + + /* + The actual on-chain amount that was sent out to the first hop. This value is + only different from the total_amt_msat field if this is a custom channel + payment and the value transported in the HTLC is different from the BTC + amount in the HTLC. If this value is zero, then this is an old payment that + didn't have this value yet and can be ignored. + */ + int64 first_hop_amount_msat = 7; + + /* + Custom channel data that might be populated in custom channels. + */ + bytes custom_channel_data = 8; } message NodeInfoRequest { diff --git a/lnrpc/lightning.swagger.json b/lnrpc/lightning.swagger.json index 0db9ace9b1..fe204e12c2 100644 --- a/lnrpc/lightning.swagger.json +++ b/lnrpc/lightning.swagger.json @@ -6912,6 +6912,16 @@ "type": "string", "format": "int64", "description": "The total amount in millisatoshis." + }, + "first_hop_amount_msat": { + "type": "string", + "format": "int64", + "description": "The actual on-chain amount that was sent out to the first hop. This value is\nonly different from the total_amt_msat field if this is a custom channel\npayment and the value transported in the HTLC is different from the BTC\namount in the HTLC. If this value is zero, then this is an old payment that\ndidn't have this value yet and can be ignored." + }, + "custom_channel_data": { + "type": "string", + "format": "byte", + "description": "Custom channel data that might be populated in custom channels." } }, "description": "A path through the channel graph which runs over one or more channels in\nsuccession. This struct carries all the information required to craft the\nSphinx onion packet, and send the payment along the first hop in the path. A\nroute is only selected as valid if all the channels have sufficient capacity to\ncarry the initial payment amount after fees are accounted for." diff --git a/lnrpc/routerrpc/router.swagger.json b/lnrpc/routerrpc/router.swagger.json index 3469fd34c6..b838f475d1 100644 --- a/lnrpc/routerrpc/router.swagger.json +++ b/lnrpc/routerrpc/router.swagger.json @@ -1166,6 +1166,16 @@ "type": "string", "format": "int64", "description": "The total amount in millisatoshis." + }, + "first_hop_amount_msat": { + "type": "string", + "format": "int64", + "description": "The actual on-chain amount that was sent out to the first hop. This value is\nonly different from the total_amt_msat field if this is a custom channel\npayment and the value transported in the HTLC is different from the BTC\namount in the HTLC. If this value is zero, then this is an old payment that\ndidn't have this value yet and can be ignored." + }, + "custom_channel_data": { + "type": "string", + "format": "byte", + "description": "Custom channel data that might be populated in custom channels." } }, "description": "A path through the channel graph which runs over one or more channels in\nsuccession. This struct carries all the information required to craft the\nSphinx onion packet, and send the payment along the first hop in the path. A\nroute is only selected as valid if all the channels have sufficient capacity to\ncarry the initial payment amount after fees are accounted for." diff --git a/lnrpc/routerrpc/router_backend.go b/lnrpc/routerrpc/router_backend.go index 4eab3bb1a4..6694e8b4f6 100644 --- a/lnrpc/routerrpc/router_backend.go +++ b/lnrpc/routerrpc/router_backend.go @@ -578,13 +578,28 @@ func (r *RouterBackend) rpcEdgeToPair(e *lnrpc.EdgeLocator) ( // MarshallRoute marshalls an internal route to an rpc route struct. func (r *RouterBackend) MarshallRoute(route *route.Route) (*lnrpc.Route, error) { resp := &lnrpc.Route{ - TotalTimeLock: route.TotalTimeLock, - TotalFees: int64(route.TotalFees().ToSatoshis()), - TotalFeesMsat: int64(route.TotalFees()), - TotalAmt: int64(route.TotalAmount.ToSatoshis()), - TotalAmtMsat: int64(route.TotalAmount), - Hops: make([]*lnrpc.Hop, len(route.Hops)), + TotalTimeLock: route.TotalTimeLock, + TotalFees: int64(route.TotalFees().ToSatoshis()), + TotalFeesMsat: int64(route.TotalFees()), + TotalAmt: int64(route.TotalAmount.ToSatoshis()), + TotalAmtMsat: int64(route.TotalAmount), + Hops: make([]*lnrpc.Hop, len(route.Hops)), + FirstHopAmountMsat: int64(route.FirstHopAmount.Val.Int()), + } + + // Encode the route's custom channel data (if available). + if len(route.FirstHopWireCustomRecords) > 0 { + customData, err := route.FirstHopWireCustomRecords.Serialize() + if err != nil { + return nil, err + } + + resp.CustomChannelData = customData + + // TODO(guggero): Feed the route into the custom data parser + // (part 3 of the mega PR series). } + incomingAmt := route.TotalAmount for i, hop := range route.Hops { fee := route.HopFee(i) From b0be0adcefbd6efdfce79f95535487ac009388c2 Mon Sep 17 00:00:00 2001 From: George Tsagkarelis Date: Tue, 16 Apr 2024 12:29:39 +0200 Subject: [PATCH 078/218] itest: add interceptor and first hop data tests --- itest/list_on_test.go | 8 + itest/lnd_forward_interceptor_test.go | 286 +++++++++++++++++++++++++- lntest/harness_assertion.go | 12 +- 3 files changed, 301 insertions(+), 5 deletions(-) diff --git a/itest/list_on_test.go b/itest/list_on_test.go index 9b385ebed2..76d4e9132d 100644 --- a/itest/list_on_test.go +++ b/itest/list_on_test.go @@ -458,6 +458,14 @@ var allTestCases = []*lntest.TestCase{ Name: "forward interceptor modified htlc", TestFunc: testForwardInterceptorModifiedHtlc, }, + { + Name: "forward interceptor wire records", + TestFunc: testForwardInterceptorWireRecords, + }, + { + Name: "forward interceptor restart", + TestFunc: testForwardInterceptorRestart, + }, { Name: "zero conf channel open", TestFunc: testZeroConfChannelOpen, diff --git a/itest/lnd_forward_interceptor_test.go b/itest/lnd_forward_interceptor_test.go index ecbfc523ca..78cd4ff657 100644 --- a/itest/lnd_forward_interceptor_test.go +++ b/itest/lnd_forward_interceptor_test.go @@ -1,7 +1,9 @@ package itest import ( + "bytes" "fmt" + "reflect" "strings" "time" @@ -13,6 +15,7 @@ import ( "github.com/lightningnetwork/lnd/lntest/node" "github.com/lightningnetwork/lnd/lntest/wait" "github.com/lightningnetwork/lnd/lntypes" + "github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/routing/route" "github.com/stretchr/testify/require" "google.golang.org/grpc/codes" @@ -24,6 +27,7 @@ var ( customTestValue = []byte{1, 3, 5} actionResumeModify = routerrpc.ResolveHoldForwardAction_RESUME_MODIFIED + actionResume = routerrpc.ResolveHoldForwardAction_RESUME ) type interceptorTestCase struct { @@ -436,17 +440,287 @@ func testForwardInterceptorModifiedHtlc(ht *lntest.HarnessTest) { ht.CloseChannel(bob, cpBC) } +// testForwardInterceptorWireRecords tests that the interceptor can read any +// wire custom records provided by the sender of a payment as part of the +// update_add_htlc message. +func testForwardInterceptorWireRecords(ht *lntest.HarnessTest) { + // Initialize the test context with 3 connected nodes. + ts := newInterceptorTestScenario(ht) + + alice, bob, carol, dave := ts.alice, ts.bob, ts.carol, ts.dave + + // Open and wait for channels. + const chanAmt = btcutil.Amount(300000) + p := lntest.OpenChannelParams{Amt: chanAmt} + reqs := []*lntest.OpenChannelRequest{ + {Local: alice, Remote: bob, Param: p}, + {Local: bob, Remote: carol, Param: p}, + {Local: carol, Remote: dave, Param: p}, + } + resp := ht.OpenMultiChannelsAsync(reqs) + cpAB, cpBC, cpCD := resp[0], resp[1], resp[2] + + // Make sure Alice is aware of channel Bob=>Carol. + ht.AssertTopologyChannelOpen(alice, cpBC) + + // Connect an interceptor to Bob's node. + bobInterceptor, cancelBobInterceptor := bob.RPC.HtlcInterceptor() + defer cancelBobInterceptor() + + // Also connect an interceptor on Carol's node to check whether we're + // relaying the TLVs send in update_add_htlc over Alice -> Bob on the + // Bob -> Carol link. + carolInterceptor, cancelCarolInterceptor := carol.RPC.HtlcInterceptor() + defer cancelCarolInterceptor() + + req := &lnrpc.Invoice{ValueMsat: 1000} + addResponse := dave.RPC.AddInvoice(req) + invoice := dave.RPC.LookupInvoice(addResponse.RHash) + + sendReq := &routerrpc.SendPaymentRequest{ + PaymentRequest: invoice.PaymentRequest, + TimeoutSeconds: int32(wait.PaymentTimeout.Seconds()), + FeeLimitMsat: noFeeLimitMsat, + FirstHopCustomRecords: map[uint64][]byte{ + 65537: []byte("test"), + }, + } + + _ = alice.RPC.SendPayment(sendReq) + + // We start the htlc interceptor with a simple implementation that saves + // all intercepted packets. These packets are held to simulate a + // pending payment. + packet := ht.ReceiveHtlcInterceptor(bobInterceptor) + + require.Len(ht, packet.InWireCustomRecords, 1) + + val, ok := packet.InWireCustomRecords[65537] + require.True(ht, ok, "expected custom record") + require.Equal(ht, []byte("test"), val) + + // TODO(guggero): Actually modify the amount once we have the invoice + // interceptor and can accept a lower amount. + newOutAmountMsat := packet.OutgoingAmountMsat + + err := bobInterceptor.Send(&routerrpc.ForwardHtlcInterceptResponse{ + IncomingCircuitKey: packet.IncomingCircuitKey, + OutAmountMsat: newOutAmountMsat, + Action: actionResumeModify, + }) + require.NoError(ht, err, "failed to send request") + + // Assert that the Alice -> Bob custom records in update_add_htlc are + // not propagated on the Bob -> Carol link. + packet = ht.ReceiveHtlcInterceptor(carolInterceptor) + require.Len(ht, packet.InWireCustomRecords, 0) + + // Just resume the payment on Carol. + err = carolInterceptor.Send(&routerrpc.ForwardHtlcInterceptResponse{ + IncomingCircuitKey: packet.IncomingCircuitKey, + Action: actionResume, + }) + require.NoError(ht, err, "carol interceptor response") + + // Assert that the payment was successful. + var preimage lntypes.Preimage + copy(preimage[:], invoice.RPreimage) + ht.AssertPaymentStatus( + alice, preimage, lnrpc.Payment_SUCCEEDED, + func(p *lnrpc.Payment) error { + recordsEqual := reflect.DeepEqual( + p.FirstHopCustomRecords, + sendReq.FirstHopCustomRecords, + ) + if !recordsEqual { + return fmt.Errorf("expected custom records to "+ + "be equal, got %v expected %v", + p.FirstHopCustomRecords, + sendReq.FirstHopCustomRecords) + } + + return nil + }, + ) + + // Finally, close channels. + ht.CloseChannel(alice, cpAB) + ht.CloseChannel(bob, cpBC) + ht.CloseChannel(carol, cpCD) +} + +// testForwardInterceptorRestart tests that the interceptor can read any wire +// custom records provided by the sender of a payment as part of the +// update_add_htlc message and that those records are persisted correctly and +// re-sent on node restart. +func testForwardInterceptorRestart(ht *lntest.HarnessTest) { + // Initialize the test context with 3 connected nodes. + ts := newInterceptorTestScenario(ht) + + alice, bob, carol, dave := ts.alice, ts.bob, ts.carol, ts.dave + + // Open and wait for channels. + const chanAmt = btcutil.Amount(300000) + p := lntest.OpenChannelParams{Amt: chanAmt} + reqs := []*lntest.OpenChannelRequest{ + {Local: alice, Remote: bob, Param: p}, + {Local: bob, Remote: carol, Param: p}, + {Local: carol, Remote: dave, Param: p}, + } + resp := ht.OpenMultiChannelsAsync(reqs) + cpAB, cpBC, cpCD := resp[0], resp[1], resp[2] + + // Make sure Alice is aware of channels Bob=>Carol and Carol=>Dave. + ht.AssertTopologyChannelOpen(alice, cpBC) + ht.AssertTopologyChannelOpen(alice, cpCD) + + // Connect an interceptor to Bob's node. + bobInterceptor, cancelBobInterceptor := bob.RPC.HtlcInterceptor() + + // Also connect an interceptor on Carol's node to check whether we're + // relaying the TLVs send in update_add_htlc over Alice -> Bob on the + // Bob -> Carol link. + carolInterceptor, cancelCarolInterceptor := carol.RPC.HtlcInterceptor() + defer cancelCarolInterceptor() + + req := &lnrpc.Invoice{ValueMsat: 50_000_000} + addResponse := dave.RPC.AddInvoice(req) + invoice := dave.RPC.LookupInvoice(addResponse.RHash) + + customRecords := map[uint64][]byte{ + 65537: []byte("test"), + } + + sendReq := &routerrpc.SendPaymentRequest{ + PaymentRequest: invoice.PaymentRequest, + TimeoutSeconds: int32(wait.PaymentTimeout.Seconds()), + FeeLimitMsat: noFeeLimitMsat, + FirstHopCustomRecords: customRecords, + } + + _ = alice.RPC.SendPayment(sendReq) + + // We start the htlc interceptor with a simple implementation that saves + // all intercepted packets. These packets are held to simulate a + // pending payment. + packet := ht.ReceiveHtlcInterceptor(bobInterceptor) + + require.Len(ht, packet.InWireCustomRecords, 1) + require.Equal(ht, customRecords, packet.InWireCustomRecords) + + // We accept the payment at Bob and resume it, so it gets to Carol. + // This means the HTLC should now be fully locked in on Alice's side and + // any restart of the node should cause the payment to be resumed and + // the data to be persisted across restarts. + err := bobInterceptor.Send(&routerrpc.ForwardHtlcInterceptResponse{ + IncomingCircuitKey: packet.IncomingCircuitKey, + Action: actionResume, + }) + require.NoError(ht, err, "failed to send request") + + // We don't resume the payment on Carol, so it should be held there. + + // The payment should now be in flight. + var preimage lntypes.Preimage + copy(preimage[:], invoice.RPreimage) + ht.AssertPaymentStatus(alice, preimage, lnrpc.Payment_IN_FLIGHT) + + // We don't resume the payment on Carol, so it should be held there. + // We now restart first Bob, then Alice, so we can make sure we've + // started the interceptor again on Bob before Alice resumes the + // payment. + cancelBobInterceptor() + restartBob := ht.SuspendNode(bob) + restartAlice := ht.SuspendNode(alice) + + require.NoError(ht, restartBob(), "failed to restart bob") + bobInterceptor, cancelBobInterceptor = bob.RPC.HtlcInterceptor() + defer cancelBobInterceptor() + + require.NoError(ht, restartAlice(), "failed to restart alice") + + // We should get another notification about the held HTLC. + packet = ht.ReceiveHtlcInterceptor(bobInterceptor) + + require.Len(ht, packet.InWireCustomRecords, 1) + require.Equal(ht, customRecords, packet.InWireCustomRecords) + + err = carolInterceptor.Send(&routerrpc.ForwardHtlcInterceptResponse{ + IncomingCircuitKey: packet.IncomingCircuitKey, + Action: actionResume, + }) + require.NoError(ht, err, "failed to send request") + + // And now we forward the payment at Carol. + packet = ht.ReceiveHtlcInterceptor(carolInterceptor) + require.Len(ht, packet.InWireCustomRecords, 0) + err = carolInterceptor.Send(&routerrpc.ForwardHtlcInterceptResponse{ + IncomingCircuitKey: packet.IncomingCircuitKey, + Action: actionResume, + }) + require.NoError(ht, err, "failed to send request") + + // Assert that the payment was successful. + ht.AssertPaymentStatus( + alice, preimage, lnrpc.Payment_SUCCEEDED, + func(p *lnrpc.Payment) error { + recordsEqual := reflect.DeepEqual( + p.FirstHopCustomRecords, + sendReq.FirstHopCustomRecords, + ) + if !recordsEqual { + return fmt.Errorf("expected custom records to "+ + "be equal, got %v expected %v", + p.FirstHopCustomRecords, + sendReq.FirstHopCustomRecords) + } + + if len(p.Htlcs) != 1 { + return fmt.Errorf("expected 1 htlc, got %d", + len(p.Htlcs)) + } + + htlc := p.Htlcs[0] + rt := htlc.Route + if rt.FirstHopAmountMsat != rt.TotalAmtMsat { + return fmt.Errorf("expected first hop amount "+ + "to be %d, got %d", rt.TotalAmtMsat, + rt.FirstHopAmountMsat) + } + + cr := lnwire.CustomRecords(p.FirstHopCustomRecords) + recordData, err := cr.Serialize() + if err != nil { + return err + } + + if !bytes.Equal(rt.CustomChannelData, recordData) { + return fmt.Errorf("expected custom records to "+ + "be equal, got %x expected %x", + rt.CustomChannelData, recordData) + } + + return nil + }, + ) + + // Finally, close channels. + ht.CloseChannel(alice, cpAB) + ht.CloseChannel(bob, cpBC) + ht.CloseChannel(carol, cpCD) +} + // interceptorTestScenario is a helper struct to hold the test context and // provide the needed functionality. type interceptorTestScenario struct { - ht *lntest.HarnessTest - alice, bob, carol *node.HarnessNode + ht *lntest.HarnessTest + alice, bob, carol, dave *node.HarnessNode } // newInterceptorTestScenario initializes a new test scenario with three nodes // and connects them to have the following topology, // -// Alice --> Bob --> Carol +// Alice --> Bob --> Carol --> Dave // // Among them, Alice and Bob are standby nodes and Carol is a new node. func newInterceptorTestScenario( @@ -454,15 +728,21 @@ func newInterceptorTestScenario( alice, bob := ht.Alice, ht.Bob carol := ht.NewNode("carol", nil) + dave := ht.NewNode("dave", nil) ht.EnsureConnected(alice, bob) ht.EnsureConnected(bob, carol) + ht.EnsureConnected(carol, dave) + + // So that carol can open channels. + ht.FundCoins(btcutil.SatoshiPerBitcoin, carol) return &interceptorTestScenario{ ht: ht, alice: alice, bob: bob, carol: carol, + dave: dave, } } diff --git a/lntest/harness_assertion.go b/lntest/harness_assertion.go index 615263373c..9538c48e71 100644 --- a/lntest/harness_assertion.go +++ b/lntest/harness_assertion.go @@ -1600,13 +1600,16 @@ func (h *HarnessTest) findPayment(hn *node.HarnessNode, return nil } +// PaymentCheck is a function that checks a payment for a specific condition. +type PaymentCheck func(*lnrpc.Payment) error + // AssertPaymentStatus asserts that the given node list a payment with the // given preimage has the expected status. It also checks that the payment has // the expected preimage, which is empty when it's not settled and matches the // given preimage when it's succeeded. func (h *HarnessTest) AssertPaymentStatus(hn *node.HarnessNode, - preimage lntypes.Preimage, - status lnrpc.Payment_PaymentStatus) *lnrpc.Payment { + preimage lntypes.Preimage, status lnrpc.Payment_PaymentStatus, + checks ...PaymentCheck) *lnrpc.Payment { var target *lnrpc.Payment payHash := preimage.Hash() @@ -1636,6 +1639,11 @@ func (h *HarnessTest) AssertPaymentStatus(hn *node.HarnessNode, target.PaymentPreimage, "expected zero preimage") } + // Perform any additional checks on the payment. + for _, check := range checks { + require.NoError(h, check(target)) + } + return target } From a40d3639367bb10e3eb050f97a5052632509763b Mon Sep 17 00:00:00 2001 From: Keagan McClelland Date: Thu, 6 Jun 2024 11:35:52 -0700 Subject: [PATCH 079/218] lnwallet: add ChannelID method to LightningChannel In this commit we introduce a convenience method to LightningChannel to allow us to quickly grab the channel id. This will be important in upcoming commits where we need to remember the ChannelID to reconstruct update messages. --- lnwallet/channel.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lnwallet/channel.go b/lnwallet/channel.go index 49d8df566c..a9d4a39a9f 100644 --- a/lnwallet/channel.go +++ b/lnwallet/channel.go @@ -6327,6 +6327,12 @@ func (lc *LightningChannel) ChannelPoint() wire.OutPoint { return lc.channelState.FundingOutpoint } +// ChannelID returns the ChannelID of this LightningChannel. This is the same +// ChannelID that is used in update messages for this channel. +func (lc *LightningChannel) ChannelID() lnwire.ChannelID { + return lnwire.NewChanIDFromOutPoint(lc.ChannelPoint()) +} + // ShortChanID returns the short channel ID for the channel. The short channel // ID encodes the exact location in the main chain that the original // funding output can be found. From 391370de2040db055b962928c8bc33cf7b6a594b Mon Sep 17 00:00:00 2001 From: Keagan McClelland Date: Thu, 6 Jun 2024 11:44:44 -0700 Subject: [PATCH 080/218] lnwallet: track ChannelID on PaymentDescriptor In this commit we track the ChannelID on the PaymentDescriptor. This will be useful in upcoming commits that need to be able to reconstruct lnwire.Message values from PaymentDescriptors as the Messages that are exchanged to update channel state all include the ChannelID. --- lnwallet/channel.go | 28 ++++++++++++++++++++++++++++ lnwallet/payment_descriptor.go | 5 +++++ 2 files changed, 33 insertions(+) diff --git a/lnwallet/channel.go b/lnwallet/channel.go index a9d4a39a9f..3073cef782 100644 --- a/lnwallet/channel.go +++ b/lnwallet/channel.go @@ -196,6 +196,7 @@ func PayDescsFromRemoteLogUpdates(chanID lnwire.ShortChannelID, height uint64, case *lnwire.UpdateAddHTLC: pd = PaymentDescriptor{ + ChanID: wireMsg.ChanID, RHash: wireMsg.PaymentHash, Timeout: wireMsg.Expiry, Amount: wireMsg.Amount, @@ -214,6 +215,7 @@ func PayDescsFromRemoteLogUpdates(chanID lnwire.ShortChannelID, height uint64, case *lnwire.UpdateFulfillHTLC: pd = PaymentDescriptor{ + ChanID: wireMsg.ChanID, RPreimage: wireMsg.PaymentPreimage, ParentIndex: wireMsg.ID, EntryType: Settle, @@ -226,6 +228,7 @@ func PayDescsFromRemoteLogUpdates(chanID lnwire.ShortChannelID, height uint64, case *lnwire.UpdateFailHTLC: pd = PaymentDescriptor{ + ChanID: wireMsg.ChanID, ParentIndex: wireMsg.ID, EntryType: Fail, FailReason: wireMsg.Reason[:], @@ -238,6 +241,7 @@ func PayDescsFromRemoteLogUpdates(chanID lnwire.ShortChannelID, height uint64, case *lnwire.UpdateFailMalformedHTLC: pd = PaymentDescriptor{ + ChanID: wireMsg.ChanID, ParentIndex: wireMsg.ID, EntryType: MalformedFail, FailCode: wireMsg.FailureCode, @@ -646,6 +650,7 @@ func (lc *LightningChannel) diskHtlcToPayDesc(feeRate chainfee.SatPerKWeight, // vs theirs or a pending commit for the remote party), we can now // re-create the original payment descriptor. return PaymentDescriptor{ + ChanID: lc.ChannelID(), RHash: htlc.RHash, Timeout: htlc.RefundTimeout, Amount: htlc.Amt, @@ -1147,6 +1152,7 @@ func (lc *LightningChannel) logUpdateToPayDesc(logUpdate *channeldb.LogUpdate, // as we've included this HTLC in our local commitment chain // for the remote party. pd = &PaymentDescriptor{ + ChanID: wireMsg.ChanID, RHash: wireMsg.PaymentHash, Timeout: wireMsg.Expiry, Amount: wireMsg.Amount, @@ -1192,6 +1198,7 @@ func (lc *LightningChannel) logUpdateToPayDesc(logUpdate *channeldb.LogUpdate, ogHTLC := remoteUpdateLog.lookupHtlc(wireMsg.ID) pd = &PaymentDescriptor{ + ChanID: wireMsg.ChanID, Amount: ogHTLC.Amount, RHash: ogHTLC.RHash, RPreimage: wireMsg.PaymentPreimage, @@ -1209,6 +1216,7 @@ func (lc *LightningChannel) logUpdateToPayDesc(logUpdate *channeldb.LogUpdate, ogHTLC := remoteUpdateLog.lookupHtlc(wireMsg.ID) pd = &PaymentDescriptor{ + ChanID: wireMsg.ChanID, Amount: ogHTLC.Amount, RHash: ogHTLC.RHash, ParentIndex: ogHTLC.HtlcIndex, @@ -1225,6 +1233,7 @@ func (lc *LightningChannel) logUpdateToPayDesc(logUpdate *channeldb.LogUpdate, // TODO(roasbeef): err if nil? pd = &PaymentDescriptor{ + ChanID: wireMsg.ChanID, Amount: ogHTLC.Amount, RHash: ogHTLC.RHash, ParentIndex: ogHTLC.HtlcIndex, @@ -1243,6 +1252,7 @@ func (lc *LightningChannel) logUpdateToPayDesc(logUpdate *channeldb.LogUpdate, // adding and removing it at the same height. case *lnwire.UpdateFee: pd = &PaymentDescriptor{ + ChanID: wireMsg.ChanID, LogIndex: logUpdate.LogIndex, Amount: lnwire.NewMSatFromSatoshis( btcutil.Amount(wireMsg.FeePerKw), @@ -1279,6 +1289,7 @@ func (lc *LightningChannel) localLogUpdateToPayDesc(logUpdate *channeldb.LogUpda ogHTLC := remoteUpdateLog.lookupHtlc(wireMsg.ID) return &PaymentDescriptor{ + ChanID: wireMsg.ChanID, Amount: ogHTLC.Amount, RHash: ogHTLC.RHash, RPreimage: wireMsg.PaymentPreimage, @@ -1295,6 +1306,7 @@ func (lc *LightningChannel) localLogUpdateToPayDesc(logUpdate *channeldb.LogUpda ogHTLC := remoteUpdateLog.lookupHtlc(wireMsg.ID) return &PaymentDescriptor{ + ChanID: wireMsg.ChanID, Amount: ogHTLC.Amount, RHash: ogHTLC.RHash, ParentIndex: ogHTLC.HtlcIndex, @@ -1310,6 +1322,7 @@ func (lc *LightningChannel) localLogUpdateToPayDesc(logUpdate *channeldb.LogUpda ogHTLC := remoteUpdateLog.lookupHtlc(wireMsg.ID) return &PaymentDescriptor{ + ChanID: wireMsg.ChanID, Amount: ogHTLC.Amount, RHash: ogHTLC.RHash, ParentIndex: ogHTLC.HtlcIndex, @@ -1322,6 +1335,7 @@ func (lc *LightningChannel) localLogUpdateToPayDesc(logUpdate *channeldb.LogUpda case *lnwire.UpdateFee: return &PaymentDescriptor{ + ChanID: wireMsg.ChanID, LogIndex: logUpdate.LogIndex, Amount: lnwire.NewMSatFromSatoshis( btcutil.Amount(wireMsg.FeePerKw), @@ -1353,6 +1367,7 @@ func (lc *LightningChannel) remoteLogUpdateToPayDesc(logUpdate *channeldb.LogUpd switch wireMsg := logUpdate.UpdateMsg.(type) { case *lnwire.UpdateAddHTLC: pd := &PaymentDescriptor{ + ChanID: wireMsg.ChanID, RHash: wireMsg.PaymentHash, Timeout: wireMsg.Expiry, Amount: wireMsg.Amount, @@ -1378,6 +1393,7 @@ func (lc *LightningChannel) remoteLogUpdateToPayDesc(logUpdate *channeldb.LogUpd ogHTLC := localUpdateLog.lookupHtlc(wireMsg.ID) return &PaymentDescriptor{ + ChanID: wireMsg.ChanID, Amount: ogHTLC.Amount, RHash: ogHTLC.RHash, RPreimage: wireMsg.PaymentPreimage, @@ -1394,6 +1410,7 @@ func (lc *LightningChannel) remoteLogUpdateToPayDesc(logUpdate *channeldb.LogUpd ogHTLC := localUpdateLog.lookupHtlc(wireMsg.ID) return &PaymentDescriptor{ + ChanID: wireMsg.ChanID, Amount: ogHTLC.Amount, RHash: ogHTLC.RHash, ParentIndex: ogHTLC.HtlcIndex, @@ -1409,6 +1426,7 @@ func (lc *LightningChannel) remoteLogUpdateToPayDesc(logUpdate *channeldb.LogUpd ogHTLC := localUpdateLog.lookupHtlc(wireMsg.ID) return &PaymentDescriptor{ + ChanID: wireMsg.ChanID, Amount: ogHTLC.Amount, RHash: ogHTLC.RHash, ParentIndex: ogHTLC.HtlcIndex, @@ -1427,6 +1445,7 @@ func (lc *LightningChannel) remoteLogUpdateToPayDesc(logUpdate *channeldb.LogUpd // adding and removing it at the same height. case *lnwire.UpdateFee: return &PaymentDescriptor{ + ChanID: wireMsg.ChanID, LogIndex: logUpdate.LogIndex, Amount: lnwire.NewMSatFromSatoshis( btcutil.Amount(wireMsg.FeePerKw), @@ -5962,6 +5981,7 @@ func (lc *LightningChannel) htlcAddDescriptor(htlc *lnwire.UpdateAddHTLC, openKey *models.CircuitKey) *PaymentDescriptor { return &PaymentDescriptor{ + ChanID: htlc.ChanID, EntryType: Add, RHash: PaymentHash(htlc.PaymentHash), Timeout: htlc.Expiry, @@ -6024,6 +6044,7 @@ func (lc *LightningChannel) ReceiveHTLC(htlc *lnwire.UpdateAddHTLC) (uint64, } pd := &PaymentDescriptor{ + ChanID: htlc.ChanID, EntryType: Add, RHash: PaymentHash(htlc.PaymentHash), Timeout: htlc.Expiry, @@ -6104,6 +6125,7 @@ func (lc *LightningChannel) SettleHTLC(preimage [32]byte, } pd := &PaymentDescriptor{ + ChanID: lc.ChannelID(), Amount: htlc.Amount, RPreimage: preimage, LogIndex: lc.updateLogs.Local.logIndex, @@ -6149,6 +6171,7 @@ func (lc *LightningChannel) ReceiveHTLCSettle(preimage [32]byte, htlcIndex uint6 } pd := &PaymentDescriptor{ + ChanID: lc.ChannelID(), Amount: htlc.Amount, RPreimage: preimage, ParentIndex: htlc.HtlcIndex, @@ -6210,6 +6233,7 @@ func (lc *LightningChannel) FailHTLC(htlcIndex uint64, reason []byte, } pd := &PaymentDescriptor{ + ChanID: lc.ChannelID(), Amount: htlc.Amount, RHash: htlc.RHash, ParentIndex: htlcIndex, @@ -6260,6 +6284,7 @@ func (lc *LightningChannel) MalformedFailHTLC(htlcIndex uint64, } pd := &PaymentDescriptor{ + ChanID: lc.ChannelID(), Amount: htlc.Amount, RHash: htlc.RHash, ParentIndex: htlcIndex, @@ -6302,6 +6327,7 @@ func (lc *LightningChannel) ReceiveFailHTLC(htlcIndex uint64, reason []byte, } pd := &PaymentDescriptor{ + ChanID: lc.ChannelID(), Amount: htlc.Amount, RHash: htlc.RHash, ParentIndex: htlc.HtlcIndex, @@ -8421,6 +8447,7 @@ func (lc *LightningChannel) UpdateFee(feePerKw chainfee.SatPerKWeight) error { } pd := &PaymentDescriptor{ + ChanID: lc.ChannelID(), LogIndex: lc.updateLogs.Local.logIndex, Amount: lnwire.NewMSatFromSatoshis(btcutil.Amount(feePerKw)), EntryType: FeeUpdate, @@ -8493,6 +8520,7 @@ func (lc *LightningChannel) ReceiveUpdateFee(feePerKw chainfee.SatPerKWeight) er // TODO(roasbeef): or just modify to use the other balance? pd := &PaymentDescriptor{ + ChanID: lc.ChannelID(), LogIndex: lc.updateLogs.Remote.logIndex, Amount: lnwire.NewMSatFromSatoshis(btcutil.Amount(feePerKw)), EntryType: FeeUpdate, diff --git a/lnwallet/payment_descriptor.go b/lnwallet/payment_descriptor.go index 6fe74bb6fb..bedc182cf8 100644 --- a/lnwallet/payment_descriptor.go +++ b/lnwallet/payment_descriptor.go @@ -70,6 +70,11 @@ func (u updateType) String() string { // TODO(roasbeef): LogEntry interface?? // - need to separate attrs for cancel/add/settle/feeupdate type PaymentDescriptor struct { + // ChanID is the ChannelID of the LightningChannel that this + // PaymentDescriptor belongs to. We track this here so we can + // reconstruct the Messages that this PaymentDescriptor is built from. + ChanID lnwire.ChannelID + // RHash is the payment hash for this HTLC. The HTLC can be settled iff // the preimage to this hash is presented. RHash PaymentHash From fb841b6436a1669ce8ada8d6c55835e41286d9f2 Mon Sep 17 00:00:00 2001 From: Keagan McClelland Date: Fri, 14 Jun 2024 17:14:25 -0700 Subject: [PATCH 081/218] htlcswitch+lnwallet: fix OnionBlob to 1366 bytes --- htlcswitch/link.go | 4 ++-- lnwallet/channel.go | 26 +++++++++++--------------- lnwallet/payment_descriptor.go | 2 +- 3 files changed, 14 insertions(+), 18 deletions(-) diff --git a/htlcswitch/link.go b/htlcswitch/link.go index a8e35afe68..51e7290acb 100644 --- a/htlcswitch/link.go +++ b/htlcswitch/link.go @@ -3419,7 +3419,7 @@ func (l *channelLink) processRemoteAdds(fwdPkg *channeldb.FwdPkg, // parse the onion object in order to obtain the // routing information with DecodeHopIterator function // which process the Sphinx packet. - onionReader := bytes.NewReader(pd.OnionBlob) + onionReader := bytes.NewReader(pd.OnionBlob[:]) req := hop.DecodeHopIteratorRequest{ OnionReader: onionReader, @@ -3471,7 +3471,7 @@ func (l *channelLink) processRemoteAdds(fwdPkg *channeldb.FwdPkg, // Fetch the onion blob that was included within this processed // payment descriptor. var onionBlob [lnwire.OnionPacketSize]byte - copy(onionBlob[:], pd.OnionBlob) + copy(onionBlob[:], pd.OnionBlob[:]) // Before adding the new htlc to the state machine, parse the // onion object in order to obtain the routing information with diff --git a/lnwallet/channel.go b/lnwallet/channel.go index 3073cef782..9d34bd395d 100644 --- a/lnwallet/channel.go +++ b/lnwallet/channel.go @@ -210,8 +210,7 @@ func PayDescsFromRemoteLogUpdates(chanID lnwire.ShortChannelID, height uint64, BlindingPoint: wireMsg.BlindingPoint, CustomRecords: wireMsg.CustomRecords.Copy(), } - pd.OnionBlob = make([]byte, len(wireMsg.OnionBlob)) - copy(pd.OnionBlob[:], wireMsg.OnionBlob[:]) + pd.OnionBlob = wireMsg.OnionBlob case *lnwire.UpdateFulfillHTLC: pd = PaymentDescriptor{ @@ -532,10 +531,10 @@ func (c *commitment) toDiskCommit( HtlcIndex: htlc.HtlcIndex, LogIndex: htlc.LogIndex, Incoming: false, + OnionBlob: htlc.OnionBlob, BlindingPoint: htlc.BlindingPoint, CustomRecords: htlc.CustomRecords.Copy(), } - copy(h.OnionBlob[:], htlc.OnionBlob) if whoseCommit.IsLocal() && htlc.sig != nil { h.Signature = htlc.sig.Serialize() @@ -558,11 +557,10 @@ func (c *commitment) toDiskCommit( HtlcIndex: htlc.HtlcIndex, LogIndex: htlc.LogIndex, Incoming: true, + OnionBlob: htlc.OnionBlob, BlindingPoint: htlc.BlindingPoint, CustomRecords: htlc.CustomRecords.Copy(), } - copy(h.OnionBlob[:], htlc.OnionBlob) - if whoseCommit.IsLocal() && htlc.sig != nil { h.Signature = htlc.sig.Serialize() } @@ -657,7 +655,7 @@ func (lc *LightningChannel) diskHtlcToPayDesc(feeRate chainfee.SatPerKWeight, EntryType: Add, HtlcIndex: htlc.HtlcIndex, LogIndex: htlc.LogIndex, - OnionBlob: htlc.OnionBlob[:], + OnionBlob: htlc.OnionBlob, localOutputIndex: localOutputIndex, remoteOutputIndex: remoteOutputIndex, ourPkScript: ourP2WSH, @@ -1160,11 +1158,10 @@ func (lc *LightningChannel) logUpdateToPayDesc(logUpdate *channeldb.LogUpdate, HtlcIndex: wireMsg.ID, LogIndex: logUpdate.LogIndex, addCommitHeightRemote: commitHeight, + OnionBlob: wireMsg.OnionBlob, BlindingPoint: wireMsg.BlindingPoint, CustomRecords: wireMsg.CustomRecords.Copy(), } - pd.OnionBlob = make([]byte, len(wireMsg.OnionBlob)) - copy(pd.OnionBlob[:], wireMsg.OnionBlob[:]) isDustRemote := HtlcIsDust( lc.channelState.ChanType, false, lntypes.Remote, @@ -1375,11 +1372,10 @@ func (lc *LightningChannel) remoteLogUpdateToPayDesc(logUpdate *channeldb.LogUpd HtlcIndex: wireMsg.ID, LogIndex: logUpdate.LogIndex, addCommitHeightLocal: commitHeight, + OnionBlob: wireMsg.OnionBlob, BlindingPoint: wireMsg.BlindingPoint, CustomRecords: wireMsg.CustomRecords.Copy(), } - pd.OnionBlob = make([]byte, len(wireMsg.OnionBlob)) - copy(pd.OnionBlob, wireMsg.OnionBlob[:]) // We don't need to generate an htlc script yet. This will be // done once we sign our remote commitment. @@ -3424,10 +3420,10 @@ func (lc *LightningChannel) createCommitDiff(newCommit *commitment, Amount: pd.Amount, Expiry: pd.Timeout, PaymentHash: pd.RHash, + OnionBlob: pd.OnionBlob, BlindingPoint: pd.BlindingPoint, CustomRecords: pd.CustomRecords.Copy(), } - copy(htlc.OnionBlob[:], pd.OnionBlob) logUpdate.UpdateMsg = htlc // Gather any references for circuits opened by this Add @@ -3565,10 +3561,10 @@ func (lc *LightningChannel) getUnsignedAckedUpdates() []channeldb.LogUpdate { Amount: pd.Amount, Expiry: pd.Timeout, PaymentHash: pd.RHash, + OnionBlob: pd.OnionBlob, BlindingPoint: pd.BlindingPoint, CustomRecords: pd.CustomRecords.Copy(), } - copy(htlc.OnionBlob[:], pd.OnionBlob) logUpdate.UpdateMsg = htlc case Settle: @@ -5643,10 +5639,10 @@ func (lc *LightningChannel) ReceiveRevocation(revMsg *lnwire.RevokeAndAck) ( Amount: pd.Amount, Expiry: pd.Timeout, PaymentHash: pd.RHash, + OnionBlob: pd.OnionBlob, BlindingPoint: pd.BlindingPoint, CustomRecords: pd.CustomRecords.Copy(), } - copy(htlc.OnionBlob[:], pd.OnionBlob) logUpdate.UpdateMsg = htlc addUpdates = append(addUpdates, logUpdate) @@ -5988,7 +5984,7 @@ func (lc *LightningChannel) htlcAddDescriptor(htlc *lnwire.UpdateAddHTLC, Amount: htlc.Amount, LogIndex: lc.updateLogs.Local.logIndex, HtlcIndex: lc.updateLogs.Local.htlcCounter, - OnionBlob: htlc.OnionBlob[:], + OnionBlob: htlc.OnionBlob, OpenCircuitKey: openKey, BlindingPoint: htlc.BlindingPoint, CustomRecords: htlc.CustomRecords.Copy(), @@ -6051,7 +6047,7 @@ func (lc *LightningChannel) ReceiveHTLC(htlc *lnwire.UpdateAddHTLC) (uint64, Amount: htlc.Amount, LogIndex: lc.updateLogs.Remote.logIndex, HtlcIndex: lc.updateLogs.Remote.htlcCounter, - OnionBlob: htlc.OnionBlob[:], + OnionBlob: htlc.OnionBlob, BlindingPoint: htlc.BlindingPoint, CustomRecords: htlc.CustomRecords.Copy(), } diff --git a/lnwallet/payment_descriptor.go b/lnwallet/payment_descriptor.go index bedc182cf8..e5091fc8fe 100644 --- a/lnwallet/payment_descriptor.go +++ b/lnwallet/payment_descriptor.go @@ -180,7 +180,7 @@ type PaymentDescriptor struct { // routing. // // NOTE: Populated only on add payment descriptor entry types. - OnionBlob []byte + OnionBlob [lnwire.OnionPacketSize]byte // ShaOnionBlob is a sha of the onion blob. // From e4497b4f4566decc6be75256d095532ea64fa5f4 Mon Sep 17 00:00:00 2001 From: Keagan McClelland Date: Tue, 20 Aug 2024 11:01:34 -0600 Subject: [PATCH 082/218] htlcswitch: change sendMalformedHTLCError to take array instead of slice --- htlcswitch/link.go | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/htlcswitch/link.go b/htlcswitch/link.go index 51e7290acb..4467940be7 100644 --- a/htlcswitch/link.go +++ b/htlcswitch/link.go @@ -3482,7 +3482,7 @@ func (l *channelLink) processRemoteAdds(fwdPkg *channeldb.FwdPkg, // should send the malformed htlc error to payment // sender. l.sendMalformedHTLCError(pd.HtlcIndex, failureCode, - onionBlob[:], pd.SourceRef) + pd.OnionBlob, pd.SourceRef) l.log.Errorf("unable to decode onion hop "+ "iterator: %v", failureCode) @@ -3527,7 +3527,7 @@ func (l *channelLink) processRemoteAdds(fwdPkg *channeldb.FwdPkg, // malformed. l.sendMalformedHTLCError( pd.HtlcIndex, failureCode, - onionBlob[:], pd.SourceRef, + pd.OnionBlob, pd.SourceRef, ) continue @@ -3561,7 +3561,7 @@ func (l *channelLink) processRemoteAdds(fwdPkg *channeldb.FwdPkg, // should send the malformed htlc error to payment // sender. l.sendMalformedHTLCError( - pd.HtlcIndex, failureCode, onionBlob[:], + pd.HtlcIndex, failureCode, pd.OnionBlob, pd.SourceRef, ) @@ -4065,9 +4065,10 @@ func (l *channelLink) sendIncomingHTLCFailureMsg(htlcIndex uint64, // sendMalformedHTLCError helper function which sends the malformed HTLC update // to the payment sender. func (l *channelLink) sendMalformedHTLCError(htlcIndex uint64, - code lnwire.FailCode, onionBlob []byte, sourceRef *channeldb.AddRef) { + code lnwire.FailCode, onionBlob [lnwire.OnionPacketSize]byte, + sourceRef *channeldb.AddRef) { - shaOnionBlob := sha256.Sum256(onionBlob) + shaOnionBlob := sha256.Sum256(onionBlob[:]) err := l.channel.MalformedFailHTLC(htlcIndex, code, shaOnionBlob, sourceRef) if err != nil { l.log.Errorf("unable cancel htlc: %v", err) From 9904af5d963bbf733a0842659df160e41f652a93 Mon Sep 17 00:00:00 2001 From: Keagan McClelland Date: Tue, 20 Aug 2024 11:28:37 -0600 Subject: [PATCH 083/218] lnwire+htlcswitch: change NewInvalidBlinding to use array instead of slice --- htlcswitch/link.go | 11 ++++------- lnwire/onion_error.go | 12 +++++++++--- lnwire/onion_error_test.go | 11 ++++++----- 3 files changed, 19 insertions(+), 15 deletions(-) diff --git a/htlcswitch/link.go b/htlcswitch/link.go index 4467940be7..80362161d1 100644 --- a/htlcswitch/link.go +++ b/htlcswitch/link.go @@ -3468,11 +3468,6 @@ func (l *channelLink) processRemoteAdds(fwdPkg *channeldb.FwdPkg, // or are able to settle it (and it adheres to our fee related // constraints). - // Fetch the onion blob that was included within this processed - // payment descriptor. - var onionBlob [lnwire.OnionPacketSize]byte - copy(onionBlob[:], pd.OnionBlob[:]) - // Before adding the new htlc to the state machine, parse the // onion object in order to obtain the routing information with // DecodeHopIterator function which process the Sphinx packet. @@ -3581,7 +3576,7 @@ func (l *channelLink) processRemoteAdds(fwdPkg *channeldb.FwdPkg, l.cfg.DisallowRouteBlinding { failure := lnwire.NewInvalidBlinding( - onionBlob[:], + fn.Some(pd.OnionBlob), ) l.sendHTLCError( pd, NewLinkError(failure), obfuscator, false, @@ -4027,7 +4022,9 @@ func (l *channelLink) sendIncomingHTLCFailureMsg(htlcIndex uint64, // The specification does not require that we set the onion // blob. - failureMsg := lnwire.NewInvalidBlinding(nil) + failureMsg := lnwire.NewInvalidBlinding( + fn.None[[lnwire.OnionPacketSize]byte](), + ) reason, err := e.EncryptFirstHop(failureMsg) if err != nil { return err diff --git a/lnwire/onion_error.go b/lnwire/onion_error.go index 66db6a9f58..f8a8bf0698 100644 --- a/lnwire/onion_error.go +++ b/lnwire/onion_error.go @@ -10,6 +10,7 @@ import ( "github.com/davecgh/go-spew/spew" "github.com/go-errors/errors" + "github.com/lightningnetwork/lnd/fn" "github.com/lightningnetwork/lnd/tlv" ) @@ -1271,14 +1272,19 @@ func (f *FailInvalidBlinding) Encode(w *bytes.Buffer, _ uint32) error { } // NewInvalidBlinding creates new instance of FailInvalidBlinding. -func NewInvalidBlinding(onion []byte) *FailInvalidBlinding { +func NewInvalidBlinding( + onion fn.Option[[OnionPacketSize]byte]) *FailInvalidBlinding { // The spec allows empty onion hashes for invalid blinding, so we only // include our onion hash if it's provided. - if onion == nil { + if onion.IsNone() { return &FailInvalidBlinding{} } - return &FailInvalidBlinding{OnionSHA256: sha256.Sum256(onion)} + shaSum := fn.MapOptionZ(onion, func(o [OnionPacketSize]byte) [32]byte { + return sha256.Sum256(o[:]) + }) + + return &FailInvalidBlinding{OnionSHA256: shaSum} } // DecodeFailure decodes, validates, and parses the lnwire onion failure, for diff --git a/lnwire/onion_error_test.go b/lnwire/onion_error_test.go index a06c572dc4..bc14d5d420 100644 --- a/lnwire/onion_error_test.go +++ b/lnwire/onion_error_test.go @@ -9,11 +9,12 @@ import ( "testing" "github.com/davecgh/go-spew/spew" + "github.com/lightningnetwork/lnd/fn" "github.com/stretchr/testify/require" ) var ( - testOnionHash = []byte{} + testOnionHash = [OnionPacketSize]byte{} testAmount = MilliSatoshi(1) testCtlvExpiry = uint32(2) testFlags = uint16(2) @@ -43,9 +44,9 @@ var onionFailures = []FailureMessage{ &FailMPPTimeout{}, NewFailIncorrectDetails(99, 100), - NewInvalidOnionVersion(testOnionHash), - NewInvalidOnionHmac(testOnionHash), - NewInvalidOnionKey(testOnionHash), + NewInvalidOnionVersion(testOnionHash[:]), + NewInvalidOnionHmac(testOnionHash[:]), + NewInvalidOnionKey(testOnionHash[:]), NewTemporaryChannelFailure(&testChannelUpdate), NewTemporaryChannelFailure(nil), NewAmountBelowMinimum(testAmount, testChannelUpdate), @@ -56,7 +57,7 @@ var onionFailures = []FailureMessage{ NewFinalIncorrectCltvExpiry(testCtlvExpiry), NewFinalIncorrectHtlcAmount(testAmount), NewInvalidOnionPayload(testType, testOffset), - NewInvalidBlinding(testOnionHash), + NewInvalidBlinding(fn.Some(testOnionHash)), } // TestEncodeDecodeCode tests the ability of onion errors to be properly encoded From d364b9b9f8b8b31651164f4075973913e9bd2c60 Mon Sep 17 00:00:00 2001 From: Keagan McClelland Date: Fri, 14 Jun 2024 17:38:52 -0700 Subject: [PATCH 084/218] lnwallet: add function to convert paymentDescriptor to LogUpdate Here we add a function that is capable of recovering LogUpdates from paymentDescriptors and we refactor the lnwallet code to use this rather than doing JIT inline construction of the LogUpdates. --- lnwallet/channel.go | 235 +++++---------------------------- lnwallet/payment_descriptor.go | 53 ++++++++ 2 files changed, 83 insertions(+), 205 deletions(-) diff --git a/lnwallet/channel.go b/lnwallet/channel.go index 9d34bd395d..205b339a2a 100644 --- a/lnwallet/channel.go +++ b/lnwallet/channel.go @@ -3371,12 +3371,6 @@ func genRemoteHtlcSigJobs(keyRing *CommitmentKeyRing, func (lc *LightningChannel) createCommitDiff(newCommit *commitment, commitSig lnwire.Sig, htlcSigs []lnwire.Sig) *channeldb.CommitDiff { - // First, we need to convert the funding outpoint into the ID that's - // used on the wire to identify this channel. We'll use this shortly - // when recording the exact CommitSig message that we'll be sending - // out. - chanID := lnwire.NewChanIDFromOutPoint(lc.channelState.FundingOutpoint) - var ( logUpdates []channeldb.LogUpdate ackAddRefs []channeldb.AddRef @@ -3401,91 +3395,42 @@ func (lc *LightningChannel) createCommitDiff(newCommit *commitment, continue } - // Knowing that this update is a part of this new commitment, - // we'll create a log update and not its index in the log so - // we can later restore it properly if a restart occurs. - logUpdate := channeldb.LogUpdate{ - LogIndex: pd.LogIndex, - } - // We'll map the type of the PaymentDescriptor to one of the // four messages that it corresponds to. With this set of // messages obtained, we can simply read from disk and re-send // them in the case of a needed channel sync. switch pd.EntryType { case Add: - htlc := &lnwire.UpdateAddHTLC{ - ChanID: chanID, - ID: pd.HtlcIndex, - Amount: pd.Amount, - Expiry: pd.Timeout, - PaymentHash: pd.RHash, - OnionBlob: pd.OnionBlob, - BlindingPoint: pd.BlindingPoint, - CustomRecords: pd.CustomRecords.Copy(), - } - logUpdate.UpdateMsg = htlc - // Gather any references for circuits opened by this Add // HTLC. if pd.OpenCircuitKey != nil { - openCircuitKeys = append(openCircuitKeys, - *pd.OpenCircuitKey) + openCircuitKeys = append( + openCircuitKeys, *pd.OpenCircuitKey, + ) } - logUpdates = append(logUpdates, logUpdate) - - // Short circuit here since an add should not have any - // of the references gathered in the case of settles, - // fails or malformed fails. - continue - - case Settle: - logUpdate.UpdateMsg = &lnwire.UpdateFulfillHTLC{ - ChanID: chanID, - ID: pd.ParentIndex, - PaymentPreimage: pd.RPreimage, + case Settle, Fail, MalformedFail: + // Gather the fwd pkg references from any settle or fail + // packets, if they exist. + if pd.SourceRef != nil { + ackAddRefs = append(ackAddRefs, *pd.SourceRef) } - - case Fail: - logUpdate.UpdateMsg = &lnwire.UpdateFailHTLC{ - ChanID: chanID, - ID: pd.ParentIndex, - Reason: pd.FailReason, + if pd.DestRef != nil { + settleFailRefs = append( + settleFailRefs, *pd.DestRef, + ) } - - case MalformedFail: - logUpdate.UpdateMsg = &lnwire.UpdateFailMalformedHTLC{ - ChanID: chanID, - ID: pd.ParentIndex, - ShaOnionBlob: pd.ShaOnionBlob, - FailureCode: pd.FailCode, + if pd.ClosedCircuitKey != nil { + closedCircuitKeys = append( + closedCircuitKeys, *pd.ClosedCircuitKey, + ) } case FeeUpdate: - // The Amount field holds the feerate denominated in - // msat. Since feerates are only denominated in sat/kw, - // we can convert it without loss of precision. - logUpdate.UpdateMsg = &lnwire.UpdateFee{ - ChanID: chanID, - FeePerKw: uint32(pd.Amount.ToSatoshis()), - } + // Nothing special to do. } - // Gather the fwd pkg references from any settle or fail - // packets, if they exist. - if pd.SourceRef != nil { - ackAddRefs = append(ackAddRefs, *pd.SourceRef) - } - if pd.DestRef != nil { - settleFailRefs = append(settleFailRefs, *pd.DestRef) - } - if pd.ClosedCircuitKey != nil { - closedCircuitKeys = append(closedCircuitKeys, - *pd.ClosedCircuitKey) - } - - logUpdates = append(logUpdates, logUpdate) + logUpdates = append(logUpdates, pd.ToLogUpdate()) } // With the set of log updates mapped into wire messages, we'll now @@ -3513,10 +3458,6 @@ func (lc *LightningChannel) createCommitDiff(newCommit *commitment, // getUnsignedAckedUpdates returns all remote log updates that we haven't // signed for yet ourselves. func (lc *LightningChannel) getUnsignedAckedUpdates() []channeldb.LogUpdate { - // First, we need to convert the funding outpoint into the ID that's - // used on the wire to identify this channel. - chanID := lnwire.NewChanIDFromOutPoint(lc.channelState.FundingOutpoint) - // Fetch the last remote update that we have signed for. lastRemoteCommitted := lc.commitChains.Remote.tail().messageIndices.Remote @@ -3547,60 +3488,9 @@ func (lc *LightningChannel) getUnsignedAckedUpdates() []channeldb.LogUpdate { continue } - logUpdate := channeldb.LogUpdate{ - LogIndex: pd.LogIndex, - } - - // We'll map the type of the PaymentDescriptor to one of the - // four messages that it corresponds to. - switch pd.EntryType { - case Add: - htlc := &lnwire.UpdateAddHTLC{ - ChanID: chanID, - ID: pd.HtlcIndex, - Amount: pd.Amount, - Expiry: pd.Timeout, - PaymentHash: pd.RHash, - OnionBlob: pd.OnionBlob, - BlindingPoint: pd.BlindingPoint, - CustomRecords: pd.CustomRecords.Copy(), - } - logUpdate.UpdateMsg = htlc - - case Settle: - logUpdate.UpdateMsg = &lnwire.UpdateFulfillHTLC{ - ChanID: chanID, - ID: pd.ParentIndex, - PaymentPreimage: pd.RPreimage, - } - - case Fail: - logUpdate.UpdateMsg = &lnwire.UpdateFailHTLC{ - ChanID: chanID, - ID: pd.ParentIndex, - Reason: pd.FailReason, - } - - case MalformedFail: - logUpdate.UpdateMsg = &lnwire.UpdateFailMalformedHTLC{ - ChanID: chanID, - ID: pd.ParentIndex, - ShaOnionBlob: pd.ShaOnionBlob, - FailureCode: pd.FailCode, - } - - case FeeUpdate: - // The Amount field holds the feerate denominated in - // msat. Since feerates are only denominated in sat/kw, - // we can convert it without loss of precision. - logUpdate.UpdateMsg = &lnwire.UpdateFee{ - ChanID: chanID, - FeePerKw: uint32(pd.Amount.ToSatoshis()), - } - } - - logUpdates = append(logUpdates, logUpdate) + logUpdates = append(logUpdates, pd.ToLogUpdate()) } + return logUpdates } @@ -5621,55 +5511,19 @@ func (lc *LightningChannel) ReceiveRevocation(revMsg *lnwire.RevokeAndAck) ( // If we've reached this point, this HTLC will be added to the // forwarding package at the height of the remote commitment. - // All types of HTLCs will record their assigned log index. - logUpdate := channeldb.LogUpdate{ - LogIndex: pd.LogIndex, - } - - // Next, we'll map the type of the PaymentDescriptor to one of - // the four messages that it corresponds to and separate the + // We'll map the type of the PaymentDescriptor to one of the + // four messages that it corresponds to and separate the // updates into Adds and Settle/Fail/MalformedFail such that // they can be written in the forwarding package. Adds are // aggregated separately from the other types of HTLCs. switch pd.EntryType { case Add: - htlc := &lnwire.UpdateAddHTLC{ - ChanID: chanID, - ID: pd.HtlcIndex, - Amount: pd.Amount, - Expiry: pd.Timeout, - PaymentHash: pd.RHash, - OnionBlob: pd.OnionBlob, - BlindingPoint: pd.BlindingPoint, - CustomRecords: pd.CustomRecords.Copy(), - } - logUpdate.UpdateMsg = htlc - addUpdates = append(addUpdates, logUpdate) - - case Settle: - logUpdate.UpdateMsg = &lnwire.UpdateFulfillHTLC{ - ChanID: chanID, - ID: pd.ParentIndex, - PaymentPreimage: pd.RPreimage, - } - settleFailUpdates = append(settleFailUpdates, logUpdate) + addUpdates = append(addUpdates, pd.ToLogUpdate()) - case Fail: - logUpdate.UpdateMsg = &lnwire.UpdateFailHTLC{ - ChanID: chanID, - ID: pd.ParentIndex, - Reason: pd.FailReason, - } - settleFailUpdates = append(settleFailUpdates, logUpdate) - - case MalformedFail: - logUpdate.UpdateMsg = &lnwire.UpdateFailMalformedHTLC{ - ChanID: chanID, - ID: pd.ParentIndex, - ShaOnionBlob: pd.ShaOnionBlob, - FailureCode: pd.FailCode, - } - settleFailUpdates = append(settleFailUpdates, logUpdate) + case Settle, Fail, MalformedFail: + settleFailUpdates = append( + settleFailUpdates, pd.ToLogUpdate(), + ) } } @@ -9005,38 +8859,9 @@ func (lc *LightningChannel) unsignedLocalUpdates(remoteMessageIndex, // covered in the next commitment signature that the remote // sends. if pd.LogIndex < remoteMessageIndex && pd.LogIndex >= localMessageIndex { - logUpdate := channeldb.LogUpdate{ - LogIndex: pd.LogIndex, - } - - switch pd.EntryType { - case FeeUpdate: - logUpdate.UpdateMsg = &lnwire.UpdateFee{ - ChanID: chanID, - FeePerKw: uint32(pd.Amount.ToSatoshis()), - } - case Settle: - logUpdate.UpdateMsg = &lnwire.UpdateFulfillHTLC{ - ChanID: chanID, - ID: pd.ParentIndex, - PaymentPreimage: pd.RPreimage, - } - case Fail: - logUpdate.UpdateMsg = &lnwire.UpdateFailHTLC{ - ChanID: chanID, - ID: pd.ParentIndex, - Reason: pd.FailReason, - } - case MalformedFail: - logUpdate.UpdateMsg = &lnwire.UpdateFailMalformedHTLC{ - ChanID: chanID, - ID: pd.ParentIndex, - ShaOnionBlob: pd.ShaOnionBlob, - FailureCode: pd.FailCode, - } - } - - localPeerUpdates = append(localPeerUpdates, logUpdate) + localPeerUpdates = append( + localPeerUpdates, pd.ToLogUpdate(), + ) } } diff --git a/lnwallet/payment_descriptor.go b/lnwallet/payment_descriptor.go index e5091fc8fe..2d6c158cd9 100644 --- a/lnwallet/payment_descriptor.go +++ b/lnwallet/payment_descriptor.go @@ -231,3 +231,56 @@ type PaymentDescriptor struct { // may have been attached to a sent HTLC. CustomRecords lnwire.CustomRecords } + +// ToLogUpdate recovers the underlying LogUpdate from the paymentDescriptor. +// This operation is lossy and will forget some extra information tracked by the +// paymentDescriptor but the function is total in that all paymentDescriptors +// can be converted back to LogUpdates. +func (pd *PaymentDescriptor) ToLogUpdate() channeldb.LogUpdate { + var msg lnwire.Message + switch pd.EntryType { + case Add: + msg = &lnwire.UpdateAddHTLC{ + ChanID: pd.ChanID, + ID: pd.HtlcIndex, + Amount: pd.Amount, + PaymentHash: pd.RHash, + Expiry: pd.Timeout, + OnionBlob: pd.OnionBlob, + BlindingPoint: pd.BlindingPoint, + CustomRecords: pd.CustomRecords.Copy(), + } + case Settle: + msg = &lnwire.UpdateFulfillHTLC{ + ChanID: pd.ChanID, + ID: pd.ParentIndex, + PaymentPreimage: pd.RPreimage, + } + case Fail: + msg = &lnwire.UpdateFailHTLC{ + ChanID: pd.ChanID, + ID: pd.ParentIndex, + Reason: pd.FailReason, + } + case MalformedFail: + msg = &lnwire.UpdateFailMalformedHTLC{ + ChanID: pd.ChanID, + ID: pd.ParentIndex, + ShaOnionBlob: pd.ShaOnionBlob, + FailureCode: pd.FailCode, + } + case FeeUpdate: + // The Amount field holds the feerate denominated in + // msat. Since feerates are only denominated in sat/kw, + // we can convert it without loss of precision. + msg = &lnwire.UpdateFee{ + ChanID: pd.ChanID, + FeePerKw: uint32(pd.Amount.ToSatoshis()), + } + } + + return channeldb.LogUpdate{ + LogIndex: pd.LogIndex, + UpdateMsg: msg, + } +} From 7f2fe2e5ffdbb6c453959b39f448fd800cc0516f Mon Sep 17 00:00:00 2001 From: Keagan McClelland Date: Fri, 16 Aug 2024 15:55:18 -0700 Subject: [PATCH 085/218] channeldb: add convenience functions for generating Source/Dest Refs --- channeldb/forwarding_package.go | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/channeldb/forwarding_package.go b/channeldb/forwarding_package.go index 4c447fc035..11c0099e75 100644 --- a/channeldb/forwarding_package.go +++ b/channeldb/forwarding_package.go @@ -286,6 +286,27 @@ func NewFwdPkg(source lnwire.ShortChannelID, height uint64, } } +// SourceRef is a convenience method that returns an AddRef to this forwarding +// package for the index in the argument. It is the caller's responsibility +// to ensure that the index is in bounds. +func (f *FwdPkg) SourceRef(i uint16) AddRef { + return AddRef{ + Height: f.Height, + Index: i, + } +} + +// DestRef is a convenience method that returns a SettleFailRef to this +// forwarding package for the index in the argument. It is the caller's +// responsibility to ensure that the index is in bounds. +func (f *FwdPkg) DestRef(i uint16) SettleFailRef { + return SettleFailRef{ + Source: f.Source, + Height: f.Height, + Index: i, + } +} + // ID returns an unique identifier for this package, used to ensure that sphinx // replay processing of this batch is idempotent. func (f *FwdPkg) ID() []byte { From 22b2568f227c6417a28a3b22eca7359bc8e672ac Mon Sep 17 00:00:00 2001 From: Keagan McClelland Date: Fri, 16 Aug 2024 12:45:35 -0700 Subject: [PATCH 086/218] htlcswitch: remove PaymentDescriptor from sendHTLCError's call signature This is done as part of a systematic removal of PaymentDescriptor from the mechanics of the htlcswitch package. --- htlcswitch/link.go | 53 ++++++++++++++++++++++++++++++++++------------ 1 file changed, 40 insertions(+), 13 deletions(-) diff --git a/htlcswitch/link.go b/htlcswitch/link.go index 80362161d1..65c609f177 100644 --- a/htlcswitch/link.go +++ b/htlcswitch/link.go @@ -1532,8 +1532,11 @@ func (l *channelLink) processHtlcResolution(resolution invoices.HtlcResolution, // result. failure := getResolutionFailure(res, htlc.pd.Amount) + //nolint:forcetypeassert + add := htlc.pd.ToLogUpdate().UpdateMsg.(*lnwire.UpdateAddHTLC) l.sendHTLCError( - htlc.pd, failure, htlc.obfuscator, true, + *add, *htlc.pd.SourceRef, failure, htlc.obfuscator, + true, ) return nil @@ -3535,8 +3538,13 @@ func (l *channelLink) processRemoteAdds(fwdPkg *channeldb.FwdPkg, // payloads. Deferring this non-trival effort till a // later date failure := lnwire.NewInvalidOnionPayload(failedType, 0) + + addMsg := pd.ToLogUpdate().UpdateMsg + //nolint:forcetypeassert + add := *addMsg.(*lnwire.UpdateAddHTLC) l.sendHTLCError( - pd, NewLinkError(failure), obfuscator, false, + add, *pd.SourceRef, NewLinkError(failure), + obfuscator, false, ) l.log.Errorf("unable to decode forwarding "+ @@ -3578,8 +3586,13 @@ func (l *channelLink) processRemoteAdds(fwdPkg *channeldb.FwdPkg, failure := lnwire.NewInvalidBlinding( fn.Some(pd.OnionBlob), ) + + addMsg := pd.ToLogUpdate().UpdateMsg + //nolint:forcetypeassert + add := *addMsg.(*lnwire.UpdateAddHTLC) l.sendHTLCError( - pd, NewLinkError(failure), obfuscator, false, + add, *pd.SourceRef, NewLinkError(failure), + obfuscator, false, ) l.log.Error("rejected htlc that uses use as an " + @@ -3699,8 +3712,13 @@ func (l *channelLink) processRemoteAdds(fwdPkg *channeldb.FwdPkg, true, hop.Source, cb, ) + addMsg := pd.ToLogUpdate().UpdateMsg + //nolint:forcetypeassert + add := *addMsg.(*lnwire.UpdateAddHTLC) l.sendHTLCError( - pd, NewLinkError(failure), obfuscator, false, + add, *pd.SourceRef, + NewLinkError(failure), obfuscator, + false, ) continue } @@ -3794,7 +3812,11 @@ func (l *channelLink) processExitHop(pd *lnwallet.PaymentDescriptor, failure := NewLinkError( lnwire.NewFinalIncorrectHtlcAmount(pd.Amount), ) - l.sendHTLCError(pd, failure, obfuscator, true) + + addMsg := pd.ToLogUpdate().UpdateMsg + //nolint:forcetypeassert + add := *addMsg.(*lnwire.UpdateAddHTLC) + l.sendHTLCError(add, *pd.SourceRef, failure, obfuscator, true) return nil } @@ -3809,7 +3831,11 @@ func (l *channelLink) processExitHop(pd *lnwallet.PaymentDescriptor, failure := NewLinkError( lnwire.NewFinalIncorrectCltvExpiry(pd.Timeout), ) - l.sendHTLCError(pd, failure, obfuscator, true) + + addMsg := pd.ToLogUpdate().UpdateMsg + //nolint:forcetypeassert + add := *addMsg.(*lnwire.UpdateAddHTLC) + l.sendHTLCError(add, *pd.SourceRef, failure, obfuscator, true) return nil } @@ -3920,8 +3946,9 @@ func (l *channelLink) forwardBatch(replay bool, packets ...*htlcPacket) { // sendHTLCError functions cancels HTLC and send cancel message back to the // peer from which HTLC was received. -func (l *channelLink) sendHTLCError(pd *lnwallet.PaymentDescriptor, - failure *LinkError, e hop.ErrorEncrypter, isReceive bool) { +func (l *channelLink) sendHTLCError(add lnwire.UpdateAddHTLC, + sourceRef channeldb.AddRef, failure *LinkError, + e hop.ErrorEncrypter, isReceive bool) { reason, err := e.EncryptFirstHop(failure.WireMessage()) if err != nil { @@ -3929,7 +3956,7 @@ func (l *channelLink) sendHTLCError(pd *lnwallet.PaymentDescriptor, return } - err = l.channel.FailHTLC(pd.HtlcIndex, reason, pd.SourceRef, nil, nil) + err = l.channel.FailHTLC(add.ID, reason, &sourceRef, nil, nil) if err != nil { l.log.Errorf("unable cancel htlc: %v", err) return @@ -3938,7 +3965,7 @@ func (l *channelLink) sendHTLCError(pd *lnwallet.PaymentDescriptor, // Send the appropriate failure message depending on whether we're // in a blinded route or not. if err := l.sendIncomingHTLCFailureMsg( - pd.HtlcIndex, e, reason, + add.ID, e, reason, ); err != nil { l.log.Errorf("unable to send HTLC failure: %v", err) return @@ -3958,12 +3985,12 @@ func (l *channelLink) sendHTLCError(pd *lnwallet.PaymentDescriptor, HtlcKey{ IncomingCircuit: models.CircuitKey{ ChanID: l.ShortChanID(), - HtlcID: pd.HtlcIndex, + HtlcID: add.ID, }, }, HtlcInfo{ - IncomingTimeLock: pd.Timeout, - IncomingAmt: pd.Amount, + IncomingTimeLock: add.Expiry, + IncomingAmt: add.Amount, }, eventType, failure, From 160747cf5ee45d86f4c68e74cc86fff28f7ea319 Mon Sep 17 00:00:00 2001 From: Keagan McClelland Date: Fri, 16 Aug 2024 12:48:04 -0700 Subject: [PATCH 087/218] htlcswitch: remove PaymentDescriptor from settleHTLC's call signature This is done as part of a systematic removal of PaymentDescriptor from the mechanics of the htlcswitch package. --- htlcswitch/link.go | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/htlcswitch/link.go b/htlcswitch/link.go index 65c609f177..389d652619 100644 --- a/htlcswitch/link.go +++ b/htlcswitch/link.go @@ -1520,7 +1520,9 @@ func (l *channelLink) processHtlcResolution(resolution invoices.HtlcResolution, l.log.Debugf("received settle resolution for %v "+ "with outcome: %v", circuitKey, res.Outcome) - return l.settleHTLC(res.Preimage, htlc.pd) + return l.settleHTLC( + res.Preimage, htlc.pd.HtlcIndex, *htlc.pd.SourceRef, + ) // For htlc failures, we get the relevant failure message based // on the failure resolution and then fail the htlc. @@ -3877,14 +3879,14 @@ func (l *channelLink) processExitHop(pd *lnwallet.PaymentDescriptor, // settleHTLC settles the HTLC on the channel. func (l *channelLink) settleHTLC(preimage lntypes.Preimage, - pd *lnwallet.PaymentDescriptor) error { + htlcIndex uint64, sourceRef channeldb.AddRef) error { hash := preimage.Hash() l.log.Infof("settling htlc %v as exit hop", hash) err := l.channel.SettleHTLC( - preimage, pd.HtlcIndex, pd.SourceRef, nil, nil, + preimage, htlcIndex, &sourceRef, nil, nil, ) if err != nil { return fmt.Errorf("unable to settle htlc: %w", err) @@ -3902,7 +3904,7 @@ func (l *channelLink) settleHTLC(preimage lntypes.Preimage, // remote peer. l.cfg.Peer.SendMessage(false, &lnwire.UpdateFulfillHTLC{ ChanID: l.ChanID(), - ID: pd.HtlcIndex, + ID: htlcIndex, PaymentPreimage: preimage, }) @@ -3911,7 +3913,7 @@ func (l *channelLink) settleHTLC(preimage lntypes.Preimage, HtlcKey{ IncomingCircuit: models.CircuitKey{ ChanID: l.ShortChanID(), - HtlcID: pd.HtlcIndex, + HtlcID: htlcIndex, }, }, preimage, From e7d545fb0ac0b8bcca03834063d76695a120d80e Mon Sep 17 00:00:00 2001 From: Keagan McClelland Date: Fri, 16 Aug 2024 13:02:51 -0700 Subject: [PATCH 088/218] htlcswitch: remove PaymentDescriptor from the processExitHop's call signature This is part of a systematic removal of PaymentDescriptor from the mechanics of the htlcswitch package. --- htlcswitch/link.go | 55 ++++++++++++++++++++++++++-------------------- 1 file changed, 31 insertions(+), 24 deletions(-) diff --git a/htlcswitch/link.go b/htlcswitch/link.go index 389d652619..b00f7762aa 100644 --- a/htlcswitch/link.go +++ b/htlcswitch/link.go @@ -3606,8 +3606,12 @@ func (l *channelLink) processRemoteAdds(fwdPkg *channeldb.FwdPkg, switch fwdInfo.NextHop { case hop.Exit: + addMsg := pd.ToLogUpdate().UpdateMsg + //nolint:forcetypeassert + add := *addMsg.(*lnwire.UpdateAddHTLC) err := l.processExitHop( - pd, obfuscator, fwdInfo, heightNow, pld, + add, *pd.SourceRef, obfuscator, fwdInfo, + heightNow, pld, ) if err != nil { l.failf(LinkFailureError{ @@ -3790,9 +3794,10 @@ func (l *channelLink) processRemoteAdds(fwdPkg *channeldb.FwdPkg, // processExitHop handles an htlc for which this link is the exit hop. It // returns a boolean indicating whether the commitment tx needs an update. -func (l *channelLink) processExitHop(pd *lnwallet.PaymentDescriptor, - obfuscator hop.ErrorEncrypter, fwdInfo hop.ForwardingInfo, - heightNow uint32, payload invoices.Payload) error { +func (l *channelLink) processExitHop(add lnwire.UpdateAddHTLC, + sourceRef channeldb.AddRef, obfuscator hop.ErrorEncrypter, + fwdInfo hop.ForwardingInfo, heightNow uint32, + payload invoices.Payload) error { // If hodl.ExitSettle is requested, we will not validate the final hop's // ADD, nor will we settle the corresponding invoice or respond with the @@ -3806,38 +3811,31 @@ func (l *channelLink) processExitHop(pd *lnwallet.PaymentDescriptor, // As we're the exit hop, we'll double check the hop-payload included in // the HTLC to ensure that it was crafted correctly by the sender and // is compatible with the HTLC we were extended. - if pd.Amount < fwdInfo.AmountToForward { + if add.Amount < fwdInfo.AmountToForward { l.log.Errorf("onion payload of incoming htlc(%x) has "+ - "incompatible value: expected <=%v, got %v", pd.RHash, - pd.Amount, fwdInfo.AmountToForward) + "incompatible value: expected <=%v, got %v", + add.PaymentHash, add.Amount, fwdInfo.AmountToForward) failure := NewLinkError( - lnwire.NewFinalIncorrectHtlcAmount(pd.Amount), + lnwire.NewFinalIncorrectHtlcAmount(add.Amount), ) - - addMsg := pd.ToLogUpdate().UpdateMsg - //nolint:forcetypeassert - add := *addMsg.(*lnwire.UpdateAddHTLC) - l.sendHTLCError(add, *pd.SourceRef, failure, obfuscator, true) + l.sendHTLCError(add, sourceRef, failure, obfuscator, true) return nil } // We'll also ensure that our time-lock value has been computed // correctly. - if pd.Timeout < fwdInfo.OutgoingCTLV { + if add.Expiry < fwdInfo.OutgoingCTLV { l.log.Errorf("onion payload of incoming htlc(%x) has "+ "incompatible time-lock: expected <=%v, got %v", - pd.RHash[:], pd.Timeout, fwdInfo.OutgoingCTLV) + add.PaymentHash, add.Expiry, fwdInfo.OutgoingCTLV) failure := NewLinkError( - lnwire.NewFinalIncorrectCltvExpiry(pd.Timeout), + lnwire.NewFinalIncorrectCltvExpiry(add.Expiry), ) - addMsg := pd.ToLogUpdate().UpdateMsg - //nolint:forcetypeassert - add := *addMsg.(*lnwire.UpdateAddHTLC) - l.sendHTLCError(add, *pd.SourceRef, failure, obfuscator, true) + l.sendHTLCError(add, sourceRef, failure, obfuscator, true) return nil } @@ -3845,15 +3843,15 @@ func (l *channelLink) processExitHop(pd *lnwallet.PaymentDescriptor, // Notify the invoiceRegistry of the exit hop htlc. If we crash right // after this, this code will be re-executed after restart. We will // receive back a resolution event. - invoiceHash := lntypes.Hash(pd.RHash) + invoiceHash := lntypes.Hash(add.PaymentHash) circuitKey := models.CircuitKey{ ChanID: l.ShortChanID(), - HtlcID: pd.HtlcIndex, + HtlcID: add.ID, } event, err := l.cfg.Registry.NotifyExitHopHtlc( - invoiceHash, pd.Amount, pd.Timeout, int32(heightNow), + invoiceHash, add.Amount, add.Expiry, int32(heightNow), circuitKey, l.hodlQueue.ChanIn(), payload, ) if err != nil { @@ -3862,7 +3860,16 @@ func (l *channelLink) processExitHop(pd *lnwallet.PaymentDescriptor, // Create a hodlHtlc struct and decide either resolved now or later. htlc := hodlHtlc{ - pd: pd, + pd: &lnwallet.PaymentDescriptor{ + EntryType: lnwallet.Add, + ChanID: add.ChanID, + RHash: add.PaymentHash, + Timeout: add.Expiry, + Amount: add.Amount, + HtlcIndex: add.ID, + SourceRef: &sourceRef, + BlindingPoint: add.BlindingPoint, + }, obfuscator: obfuscator, } From dd9568b648df5281f1763697e44a80183ddba9e4 Mon Sep 17 00:00:00 2001 From: Keagan McClelland Date: Fri, 16 Aug 2024 13:05:54 -0700 Subject: [PATCH 089/218] htlcswitch: remove PaymentDescriptor from hodlHtlc This is part of a systematic removal of PaymentDescriptor from the mechanics of the htlcswitch package. --- htlcswitch/link.go | 23 +++++++---------------- 1 file changed, 7 insertions(+), 16 deletions(-) diff --git a/htlcswitch/link.go b/htlcswitch/link.go index b00f7762aa..6d243db0af 100644 --- a/htlcswitch/link.go +++ b/htlcswitch/link.go @@ -442,7 +442,8 @@ func (m *hookMap) invoke() { // hodlHtlc contains htlc data that is required for resolution. type hodlHtlc struct { - pd *lnwallet.PaymentDescriptor + add lnwire.UpdateAddHTLC + sourceRef channeldb.AddRef obfuscator hop.ErrorEncrypter } @@ -1521,7 +1522,7 @@ func (l *channelLink) processHtlcResolution(resolution invoices.HtlcResolution, "with outcome: %v", circuitKey, res.Outcome) return l.settleHTLC( - res.Preimage, htlc.pd.HtlcIndex, *htlc.pd.SourceRef, + res.Preimage, htlc.add.ID, htlc.sourceRef, ) // For htlc failures, we get the relevant failure message based @@ -1532,12 +1533,10 @@ func (l *channelLink) processHtlcResolution(resolution invoices.HtlcResolution, // Get the lnwire failure message based on the resolution // result. - failure := getResolutionFailure(res, htlc.pd.Amount) + failure := getResolutionFailure(res, htlc.add.Amount) - //nolint:forcetypeassert - add := htlc.pd.ToLogUpdate().UpdateMsg.(*lnwire.UpdateAddHTLC) l.sendHTLCError( - *add, *htlc.pd.SourceRef, failure, htlc.obfuscator, + htlc.add, htlc.sourceRef, failure, htlc.obfuscator, true, ) return nil @@ -3860,16 +3859,8 @@ func (l *channelLink) processExitHop(add lnwire.UpdateAddHTLC, // Create a hodlHtlc struct and decide either resolved now or later. htlc := hodlHtlc{ - pd: &lnwallet.PaymentDescriptor{ - EntryType: lnwallet.Add, - ChanID: add.ChanID, - RHash: add.PaymentHash, - Timeout: add.Expiry, - Amount: add.Amount, - HtlcIndex: add.ID, - SourceRef: &sourceRef, - BlindingPoint: add.BlindingPoint, - }, + add: add, + sourceRef: sourceRef, obfuscator: obfuscator, } From 875d2e46649a4ac1b95916c8f802d26f6d6851b2 Mon Sep 17 00:00:00 2001 From: Keagan McClelland Date: Fri, 16 Aug 2024 13:19:36 -0700 Subject: [PATCH 090/218] htlcswitch: remove PaymentDescriptor from processRemoteAdds call signature This is part of a systematic removal of PaymentDescriptor from the mechanics of the htlcswitch package. --- htlcswitch/link.go | 121 +++++++++++++++++++-------------------------- 1 file changed, 52 insertions(+), 69 deletions(-) diff --git a/htlcswitch/link.go b/htlcswitch/link.go index 6d243db0af..551158d850 100644 --- a/htlcswitch/link.go +++ b/htlcswitch/link.go @@ -1009,15 +1009,7 @@ func (l *channelLink) resolveFwdPkg(fwdPkg *channeldb.FwdPkg) error { // shove the entire, original set of adds down the pipeline so that the // batch of adds presented to the sphinx router does not ever change. if !fwdPkg.AckFilter.IsFull() { - adds, err := lnwallet.PayDescsFromRemoteLogUpdates( - fwdPkg.Source, fwdPkg.Height, fwdPkg.Adds, - ) - if err != nil { - l.log.Errorf("unable to process remote log updates: %v", - err) - return err - } - l.processRemoteAdds(fwdPkg, adds) + l.processRemoteAdds(fwdPkg) // If the link failed during processing the adds, we must // return to ensure we won't attempted to update the state @@ -2337,7 +2329,7 @@ func (l *channelLink) handleUpstreamMsg(msg lnwire.Message) { // We now process the message and advance our remote commit // chain. - fwdPkg, adds, settleFails, remoteHTLCs, err := l.channel. + fwdPkg, _, settleFails, remoteHTLCs, err := l.channel. ReceiveRevocation(msg) if err != nil { // TODO(halseth): force close? @@ -2389,7 +2381,7 @@ func (l *channelLink) handleUpstreamMsg(msg lnwire.Message) { } l.processRemoteSettleFails(fwdPkg, settleFails) - l.processRemoteAdds(fwdPkg, adds) + l.processRemoteAdds(fwdPkg) // If the link failed during processing the adds, we must // return to ensure we won't attempted to update the state @@ -3405,32 +3397,29 @@ func (l *channelLink) processRemoteSettleFails(fwdPkg *channeldb.FwdPkg, // indicating whether this is the first time these Adds are being processed, or // whether we are reprocessing as a result of a failure or restart. Adds that // have already been acknowledged in the forwarding package will be ignored. -func (l *channelLink) processRemoteAdds(fwdPkg *channeldb.FwdPkg, - lockedInHtlcs []*lnwallet.PaymentDescriptor) { - +// +//nolint:funlen +func (l *channelLink) processRemoteAdds(fwdPkg *channeldb.FwdPkg) { l.log.Tracef("processing %d remote adds for height %d", - len(lockedInHtlcs), fwdPkg.Height) + len(fwdPkg.Adds), fwdPkg.Height) decodeReqs := make( - []hop.DecodeHopIteratorRequest, 0, len(lockedInHtlcs), + []hop.DecodeHopIteratorRequest, 0, len(fwdPkg.Adds), ) - for _, pd := range lockedInHtlcs { - switch pd.EntryType { - - // TODO(conner): remove type switch? - case lnwallet.Add: + for _, update := range fwdPkg.Adds { + if msg, ok := update.UpdateMsg.(*lnwire.UpdateAddHTLC); ok { // Before adding the new htlc to the state machine, // parse the onion object in order to obtain the // routing information with DecodeHopIterator function // which process the Sphinx packet. - onionReader := bytes.NewReader(pd.OnionBlob[:]) + onionReader := bytes.NewReader(msg.OnionBlob[:]) req := hop.DecodeHopIteratorRequest{ OnionReader: onionReader, - RHash: pd.RHash[:], - IncomingCltv: pd.Timeout, - IncomingAmount: pd.Amount, - BlindingPoint: pd.BlindingPoint, + RHash: msg.PaymentHash[:], + IncomingCltv: msg.Expiry, + IncomingAmount: msg.Amount, + BlindingPoint: msg.BlindingPoint, } decodeReqs = append(decodeReqs, req) @@ -3452,9 +3441,13 @@ func (l *channelLink) processRemoteAdds(fwdPkg *channeldb.FwdPkg, var switchPackets []*htlcPacket - for i, pd := range lockedInHtlcs { + for i, update := range fwdPkg.Adds { idx := uint16(i) + //nolint:forcetypeassert + add := *update.UpdateMsg.(*lnwire.UpdateAddHTLC) + sourceRef := fwdPkg.SourceRef(idx) + if fwdPkg.State == channeldb.FwdStateProcessed && fwdPkg.AckFilter.Contains(idx) { @@ -3480,8 +3473,9 @@ func (l *channelLink) processRemoteAdds(fwdPkg *channeldb.FwdPkg, // If we're unable to process the onion blob then we // should send the malformed htlc error to payment // sender. - l.sendMalformedHTLCError(pd.HtlcIndex, failureCode, - pd.OnionBlob, pd.SourceRef) + l.sendMalformedHTLCError( + add.ID, failureCode, add.OnionBlob, &sourceRef, + ) l.log.Errorf("unable to decode onion hop "+ "iterator: %v", failureCode) @@ -3525,8 +3519,8 @@ func (l *channelLink) processRemoteAdds(fwdPkg *channeldb.FwdPkg, // We can't process this htlc, send back // malformed. l.sendMalformedHTLCError( - pd.HtlcIndex, failureCode, - pd.OnionBlob, pd.SourceRef, + add.ID, failureCode, add.OnionBlob, + &sourceRef, ) continue @@ -3540,11 +3534,8 @@ func (l *channelLink) processRemoteAdds(fwdPkg *channeldb.FwdPkg, // later date failure := lnwire.NewInvalidOnionPayload(failedType, 0) - addMsg := pd.ToLogUpdate().UpdateMsg - //nolint:forcetypeassert - add := *addMsg.(*lnwire.UpdateAddHTLC) l.sendHTLCError( - add, *pd.SourceRef, NewLinkError(failure), + add, sourceRef, NewLinkError(failure), obfuscator, false, ) @@ -3565,8 +3556,8 @@ func (l *channelLink) processRemoteAdds(fwdPkg *channeldb.FwdPkg, // should send the malformed htlc error to payment // sender. l.sendMalformedHTLCError( - pd.HtlcIndex, failureCode, pd.OnionBlob, - pd.SourceRef, + add.ID, failureCode, add.OnionBlob, + &sourceRef, ) l.log.Errorf("unable to decode onion "+ @@ -3585,14 +3576,11 @@ func (l *channelLink) processRemoteAdds(fwdPkg *channeldb.FwdPkg, l.cfg.DisallowRouteBlinding { failure := lnwire.NewInvalidBlinding( - fn.Some(pd.OnionBlob), + fn.Some(add.OnionBlob), ) - addMsg := pd.ToLogUpdate().UpdateMsg - //nolint:forcetypeassert - add := *addMsg.(*lnwire.UpdateAddHTLC) l.sendHTLCError( - add, *pd.SourceRef, NewLinkError(failure), + add, sourceRef, NewLinkError(failure), obfuscator, false, ) @@ -3605,11 +3593,8 @@ func (l *channelLink) processRemoteAdds(fwdPkg *channeldb.FwdPkg, switch fwdInfo.NextHop { case hop.Exit: - addMsg := pd.ToLogUpdate().UpdateMsg - //nolint:forcetypeassert - add := *addMsg.(*lnwire.UpdateAddHTLC) err := l.processExitHop( - add, *pd.SourceRef, obfuscator, fwdInfo, + add, sourceRef, obfuscator, fwdInfo, heightNow, pld, ) if err != nil { @@ -3644,18 +3629,20 @@ func (l *channelLink) processRemoteAdds(fwdPkg *channeldb.FwdPkg, } // Otherwise, it was already processed, we can - // collect it and continue. - addMsg := &lnwire.UpdateAddHTLC{ + // can collect it and continue. + outgoingAdd := &lnwire.UpdateAddHTLC{ Expiry: fwdInfo.OutgoingCTLV, Amount: fwdInfo.AmountToForward, - PaymentHash: pd.RHash, + PaymentHash: add.PaymentHash, BlindingPoint: fwdInfo.NextBlinding, } // Finally, we'll encode the onion packet for // the _next_ hop using the hop iterator // decoded for the current hop. - buf := bytes.NewBuffer(addMsg.OnionBlob[0:0]) + buf := bytes.NewBuffer( + outgoingAdd.OnionBlob[0:0], + ) // We know this cannot fail, as this ADD // was marked forwarded in a previous @@ -3667,18 +3654,18 @@ func (l *channelLink) processRemoteAdds(fwdPkg *channeldb.FwdPkg, //nolint:lll updatePacket := &htlcPacket{ incomingChanID: l.ShortChanID(), - incomingHTLCID: pd.HtlcIndex, + incomingHTLCID: add.ID, outgoingChanID: fwdInfo.NextHop, - sourceRef: pd.SourceRef, - incomingAmount: pd.Amount, - amount: addMsg.Amount, - htlc: addMsg, + sourceRef: &sourceRef, + incomingAmount: add.Amount, + amount: outgoingAdd.Amount, + htlc: outgoingAdd, obfuscator: obfuscator, - incomingTimeout: pd.Timeout, + incomingTimeout: add.Expiry, outgoingTimeout: fwdInfo.OutgoingCTLV, inOnionCustomRecords: pld.CustomRecords(), inboundFee: inboundFee, - inWireCustomRecords: pd.CustomRecords.Copy(), + inWireCustomRecords: add.CustomRecords.Copy(), } switchPackets = append( switchPackets, updatePacket, @@ -3696,7 +3683,7 @@ func (l *channelLink) processRemoteAdds(fwdPkg *channeldb.FwdPkg, addMsg := &lnwire.UpdateAddHTLC{ Expiry: fwdInfo.OutgoingCTLV, Amount: fwdInfo.AmountToForward, - PaymentHash: pd.RHash, + PaymentHash: add.PaymentHash, BlindingPoint: fwdInfo.NextBlinding, } @@ -3717,13 +3704,9 @@ func (l *channelLink) processRemoteAdds(fwdPkg *channeldb.FwdPkg, true, hop.Source, cb, ) - addMsg := pd.ToLogUpdate().UpdateMsg - //nolint:forcetypeassert - add := *addMsg.(*lnwire.UpdateAddHTLC) l.sendHTLCError( - add, *pd.SourceRef, - NewLinkError(failure), obfuscator, - false, + add, sourceRef, NewLinkError(failure), + obfuscator, false, ) continue } @@ -3742,18 +3725,18 @@ func (l *channelLink) processRemoteAdds(fwdPkg *channeldb.FwdPkg, //nolint:lll updatePacket := &htlcPacket{ incomingChanID: l.ShortChanID(), - incomingHTLCID: pd.HtlcIndex, + incomingHTLCID: add.ID, outgoingChanID: fwdInfo.NextHop, - sourceRef: pd.SourceRef, - incomingAmount: pd.Amount, + sourceRef: &sourceRef, + incomingAmount: add.Amount, amount: addMsg.Amount, htlc: addMsg, obfuscator: obfuscator, - incomingTimeout: pd.Timeout, + incomingTimeout: add.Expiry, outgoingTimeout: fwdInfo.OutgoingCTLV, inOnionCustomRecords: pld.CustomRecords(), inboundFee: inboundFee, - inWireCustomRecords: pd.CustomRecords.Copy(), + inWireCustomRecords: add.CustomRecords.Copy(), } fwdPkg.FwdFilter.Set(idx) From a3e127d1d6d99149fcc16c10c7ee48203362de25 Mon Sep 17 00:00:00 2001 From: Keagan McClelland Date: Fri, 16 Aug 2024 13:28:44 -0700 Subject: [PATCH 091/218] htlcswitch: remove PaymentDescriptor from processRemoteSettleFails call signature This is part of a systematic removal of PaymentDescriptor from the mechanics of the htlcswitch package. --- htlcswitch/link.go | 53 +++++++++++++++++----------------------------- 1 file changed, 19 insertions(+), 34 deletions(-) diff --git a/htlcswitch/link.go b/htlcswitch/link.go index 551158d850..b8e4038e8d 100644 --- a/htlcswitch/link.go +++ b/htlcswitch/link.go @@ -993,15 +993,7 @@ func (l *channelLink) resolveFwdPkg(fwdPkg *channeldb.FwdPkg) error { // If the package is fully acked but not completed, it must still have // settles and fails to propagate. if !fwdPkg.SettleFailFilter.IsFull() { - settleFails, err := lnwallet.PayDescsFromRemoteLogUpdates( - fwdPkg.Source, fwdPkg.Height, fwdPkg.SettleFails, - ) - if err != nil { - l.log.Errorf("unable to process remote log updates: %v", - err) - return err - } - l.processRemoteSettleFails(fwdPkg, settleFails) + l.processRemoteSettleFails(fwdPkg) } // Finally, replay *ALL ADDS* in this forwarding package. The @@ -2329,7 +2321,7 @@ func (l *channelLink) handleUpstreamMsg(msg lnwire.Message) { // We now process the message and advance our remote commit // chain. - fwdPkg, _, settleFails, remoteHTLCs, err := l.channel. + fwdPkg, _, _, remoteHTLCs, err := l.channel. ReceiveRevocation(msg) if err != nil { // TODO(halseth): force close? @@ -2380,7 +2372,7 @@ func (l *channelLink) handleUpstreamMsg(msg lnwire.Message) { } } - l.processRemoteSettleFails(fwdPkg, settleFails) + l.processRemoteSettleFails(fwdPkg) l.processRemoteAdds(fwdPkg) // If the link failed during processing the adds, we must @@ -3287,17 +3279,17 @@ func (l *channelLink) updateChannelFee(feePerKw chainfee.SatPerKWeight) error { // the context of the provided forwarding package. Any settles or fails that // have already been acknowledged in the forwarding package will not be sent to // the switch. -func (l *channelLink) processRemoteSettleFails(fwdPkg *channeldb.FwdPkg, - settleFails []*lnwallet.PaymentDescriptor) { - - if len(settleFails) == 0 { +func (l *channelLink) processRemoteSettleFails(fwdPkg *channeldb.FwdPkg) { + if len(fwdPkg.SettleFails) == 0 { return } l.log.Debugf("settle-fail-filter: %v", fwdPkg.SettleFailFilter) var switchPackets []*htlcPacket - for i, pd := range settleFails { + for i, update := range fwdPkg.SettleFails { + destRef := fwdPkg.DestRef(uint16(i)) + // Skip any settles or fails that have already been // acknowledged by the incoming link that originated the // forwarded Add. @@ -3308,12 +3300,11 @@ func (l *channelLink) processRemoteSettleFails(fwdPkg *channeldb.FwdPkg, // TODO(roasbeef): rework log entries to a shared // interface. - switch pd.EntryType { - + switch msg := update.UpdateMsg.(type) { // A settle for an HTLC we previously forwarded HTLC has been // received. So we'll forward the HTLC to the switch which will // handle propagating the settle to the prior hop. - case lnwallet.Settle: + case *lnwire.UpdateFulfillHTLC: // If hodl.SettleIncoming is requested, we will not // forward the SETTLE to the switch and will not signal // a free slot on the commitment transaction. @@ -3324,11 +3315,9 @@ func (l *channelLink) processRemoteSettleFails(fwdPkg *channeldb.FwdPkg, settlePacket := &htlcPacket{ outgoingChanID: l.ShortChanID(), - outgoingHTLCID: pd.ParentIndex, - destRef: pd.DestRef, - htlc: &lnwire.UpdateFulfillHTLC{ - PaymentPreimage: pd.RPreimage, - }, + outgoingHTLCID: msg.ID, + destRef: &destRef, + htlc: msg, } // Add the packet to the batch to be forwarded, and @@ -3340,7 +3329,7 @@ func (l *channelLink) processRemoteSettleFails(fwdPkg *channeldb.FwdPkg, // been received. As a result a new slot will be freed up in // our commitment state, so we'll forward this to the switch so // the backwards undo can continue. - case lnwallet.Fail: + case *lnwire.UpdateFailHTLC: // If hodl.SettleIncoming is requested, we will not // forward the FAIL to the switch and will not signal a // free slot on the commitment transaction. @@ -3355,16 +3344,12 @@ func (l *channelLink) processRemoteSettleFails(fwdPkg *channeldb.FwdPkg, // set on the packet. failPacket := &htlcPacket{ outgoingChanID: l.ShortChanID(), - outgoingHTLCID: pd.ParentIndex, - destRef: pd.DestRef, - htlc: &lnwire.UpdateFailHTLC{ - Reason: lnwire.OpaqueReason( - pd.FailReason, - ), - }, + outgoingHTLCID: msg.ID, + destRef: &destRef, + htlc: msg, } - l.log.Debugf("Failed to send %s", pd.Amount) + l.log.Debugf("Failed to send HTLC with ID=%d", msg.ID) // If the failure message lacks an HMAC (but includes // the 4 bytes for encoding the message and padding @@ -3374,7 +3359,7 @@ func (l *channelLink) processRemoteSettleFails(fwdPkg *channeldb.FwdPkg, // to an actual error, by encrypting it as if we were // the originating hop. convertedErrorSize := lnwire.FailureMessageLength + 4 - if len(pd.FailReason) == convertedErrorSize { + if len(msg.Reason) == convertedErrorSize { failPacket.convertedError = true } From 870800b81a500c094a37c3ba4f0f99ceecda23c0 Mon Sep 17 00:00:00 2001 From: Keagan McClelland Date: Fri, 16 Aug 2024 14:35:49 -0700 Subject: [PATCH 092/218] htlcswitch+lnwallet: remove PaymentDescriptor from ReceiveRevocation returns This is part of a systematic removal of PaymentDescriptor from the public API of the lnwallet package. This marks the last change needed before we make the PaymentDescriptor structure private. --- htlcswitch/link.go | 3 +- htlcswitch/link_isolated_test.go | 2 +- htlcswitch/link_test.go | 6 +- lnwallet/channel.go | 67 ++++++-------- lnwallet/channel_test.go | 150 +++++++++++++++++-------------- lnwallet/test_utils.go | 4 +- lnwallet/transactions_test.go | 2 +- 7 files changed, 120 insertions(+), 114 deletions(-) diff --git a/htlcswitch/link.go b/htlcswitch/link.go index b8e4038e8d..83495f357b 100644 --- a/htlcswitch/link.go +++ b/htlcswitch/link.go @@ -2321,8 +2321,7 @@ func (l *channelLink) handleUpstreamMsg(msg lnwire.Message) { // We now process the message and advance our remote commit // chain. - fwdPkg, _, _, remoteHTLCs, err := l.channel. - ReceiveRevocation(msg) + fwdPkg, remoteHTLCs, err := l.channel.ReceiveRevocation(msg) if err != nil { // TODO(halseth): force close? l.failf( diff --git a/htlcswitch/link_isolated_test.go b/htlcswitch/link_isolated_test.go index 89d0ef3193..5d769d9201 100644 --- a/htlcswitch/link_isolated_test.go +++ b/htlcswitch/link_isolated_test.go @@ -129,7 +129,7 @@ func (l *linkTestContext) receiveRevAndAckAliceToBob() { l.t.Fatalf("expected RevokeAndAck, got %T", msg) } - _, _, _, _, err := l.bobChannel.ReceiveRevocation(rev) + _, _, err := l.bobChannel.ReceiveRevocation(rev) if err != nil { l.t.Fatalf("bob failed receiving revocation: %v", err) } diff --git a/htlcswitch/link_test.go b/htlcswitch/link_test.go index 60010d6fff..53a084209e 100644 --- a/htlcswitch/link_test.go +++ b/htlcswitch/link_test.go @@ -2335,7 +2335,7 @@ func handleStateUpdate(link *channelLink, if !ok { return fmt.Errorf("expected RevokeAndAck got %T", msg) } - _, _, _, _, err = remoteChannel.ReceiveRevocation(revoke) + _, _, err = remoteChannel.ReceiveRevocation(revoke) if err != nil { return fmt.Errorf("unable to receive "+ "revocation: %v", err) @@ -2389,7 +2389,7 @@ func updateState(batchTick chan time.Time, link *channelLink, return fmt.Errorf("expected RevokeAndAck got %T", msg) } - _, _, _, _, err = remoteChannel.ReceiveRevocation(revoke) + _, _, err = remoteChannel.ReceiveRevocation(revoke) if err != nil { return fmt.Errorf("unable to receive "+ "revocation: %v", err) @@ -3643,7 +3643,7 @@ func TestChannelLinkTrimCircuitsRemoteCommit(t *testing.T) { rev, _, _, err := harness.bobChannel.RevokeCurrentCommitment() require.NoError(t, err, "unable to revoke current commitment") - _, _, _, _, err = alice.channel.ReceiveRevocation(rev) + _, _, err = alice.channel.ReceiveRevocation(rev) require.NoError(t, err, "unable to receive revocation") // Restart Alice's link, which simulates a disconnection with the remote diff --git a/lnwallet/channel.go b/lnwallet/channel.go index 205b339a2a..6d98372299 100644 --- a/lnwallet/channel.go +++ b/lnwallet/channel.go @@ -5375,15 +5375,10 @@ func (lc *LightningChannel) RevokeCurrentCommitment() (*lnwire.RevokeAndAck, // The returned values correspond to: // 1. The forwarding package corresponding to the remote commitment height // that was revoked. -// 2. The PaymentDescriptor of any Add HTLCs that were locked in by this -// revocation. -// 3. The PaymentDescriptor of any Settle/Fail HTLCs that were locked in by -// this revocation. -// 4. The set of HTLCs present on the current valid commitment transaction +// 2. The set of HTLCs present on the current valid commitment transaction // for the remote party. func (lc *LightningChannel) ReceiveRevocation(revMsg *lnwire.RevokeAndAck) ( - *channeldb.FwdPkg, []*PaymentDescriptor, []*PaymentDescriptor, - []channeldb.HTLC, error) { + *channeldb.FwdPkg, []channeldb.HTLC, error) { lc.Lock() defer lc.Unlock() @@ -5392,10 +5387,10 @@ func (lc *LightningChannel) ReceiveRevocation(revMsg *lnwire.RevokeAndAck) ( store := lc.channelState.RevocationStore revocation, err := chainhash.NewHash(revMsg.Revocation[:]) if err != nil { - return nil, nil, nil, nil, err + return nil, nil, err } if err := store.AddNextEntry(revocation); err != nil { - return nil, nil, nil, nil, err + return nil, nil, err } // Verify that if we use the commitment point computed based off of the @@ -5404,7 +5399,7 @@ func (lc *LightningChannel) ReceiveRevocation(revMsg *lnwire.RevokeAndAck) ( currentCommitPoint := lc.channelState.RemoteCurrentRevocation derivedCommitPoint := input.ComputeCommitmentPoint(revMsg.Revocation[:]) if !derivedCommitPoint.IsEqual(currentCommitPoint) { - return nil, nil, nil, nil, fmt.Errorf("revocation key mismatch") + return nil, nil, fmt.Errorf("revocation key mismatch") } // Now that we've verified that the prior commitment has been properly @@ -5434,10 +5429,8 @@ func (lc *LightningChannel) ReceiveRevocation(revMsg *lnwire.RevokeAndAck) ( // updates to disk and optimistically buffer the forwarding package in // memory. var ( - addsToForward []*PaymentDescriptor - addUpdates []channeldb.LogUpdate - settleFailsToForward []*PaymentDescriptor - settleFailUpdates []channeldb.LogUpdate + addUpdatesToForward []channeldb.LogUpdate + settleFailUpdatesToForward []channeldb.LogUpdate ) var addIndex, settleFailIndex uint16 @@ -5489,7 +5482,13 @@ func (lc *LightningChannel) ReceiveRevocation(revMsg *lnwire.RevokeAndAck) ( addIndex++ pd.isForwarded = true - addsToForward = append(addsToForward, pd) + + // At this point we put the update into our list of + // updates that we will eventually put into the + // FwdPkg at this height. + addUpdatesToForward = append( + addUpdatesToForward, pd.ToLogUpdate(), + ) case pd.EntryType != Add && committedRmv && shouldFwdRmv: // Construct a reference specifying the location that @@ -5503,28 +5502,17 @@ func (lc *LightningChannel) ReceiveRevocation(revMsg *lnwire.RevokeAndAck) ( settleFailIndex++ pd.isForwarded = true - settleFailsToForward = append(settleFailsToForward, pd) + + // At this point we put the update into our list of + // updates that we will eventually put into the + // FwdPkg at this height. + settleFailUpdatesToForward = append( + settleFailUpdatesToForward, pd.ToLogUpdate(), + ) default: continue } - - // If we've reached this point, this HTLC will be added to the - // forwarding package at the height of the remote commitment. - // We'll map the type of the PaymentDescriptor to one of the - // four messages that it corresponds to and separate the - // updates into Adds and Settle/Fail/MalformedFail such that - // they can be written in the forwarding package. Adds are - // aggregated separately from the other types of HTLCs. - switch pd.EntryType { - case Add: - addUpdates = append(addUpdates, pd.ToLogUpdate()) - - case Settle, Fail, MalformedFail: - settleFailUpdates = append( - settleFailUpdates, pd.ToLogUpdate(), - ) - } } // We use the remote commitment chain's tip as it will soon become the tail @@ -5540,7 +5528,8 @@ func (lc *LightningChannel) ReceiveRevocation(revMsg *lnwire.RevokeAndAck) ( // type, construct a forwarding package using the height that the remote // commitment chain will be extended after persisting the revocation. fwdPkg := channeldb.NewFwdPkg( - source, remoteChainTail, addUpdates, settleFailUpdates, + source, remoteChainTail, addUpdatesToForward, + settleFailUpdatesToForward, ) // We will soon be saving the current remote commitment to revocation @@ -5553,7 +5542,7 @@ func (lc *LightningChannel) ReceiveRevocation(revMsg *lnwire.RevokeAndAck) ( revocation, lc.channelState, lc.leafStore, ) if err != nil { - return nil, nil, nil, nil, err + return nil, nil, err } // Now that we have a new verification nonce from them, we can refresh @@ -5561,7 +5550,7 @@ func (lc *LightningChannel) ReceiveRevocation(revMsg *lnwire.RevokeAndAck) ( if lc.channelState.ChanType.IsTaproot() { localNonce, err := revMsg.LocalNonce.UnwrapOrErrV(errNoNonce) if err != nil { - return nil, nil, nil, nil, err + return nil, nil, err } session, err := lc.musigSessions.RemoteSession.Refresh( @@ -5570,7 +5559,7 @@ func (lc *LightningChannel) ReceiveRevocation(revMsg *lnwire.RevokeAndAck) ( }, ) if err != nil { - return nil, nil, nil, nil, err + return nil, nil, err } lc.musigSessions.RemoteSession = session @@ -5586,7 +5575,7 @@ func (lc *LightningChannel) ReceiveRevocation(revMsg *lnwire.RevokeAndAck) ( ourOutputIndex, theirOutputIndex, ) if err != nil { - return nil, nil, nil, nil, err + return nil, nil, err } // Since they revoked the current lowest height in their commitment @@ -5603,7 +5592,7 @@ func (lc *LightningChannel) ReceiveRevocation(revMsg *lnwire.RevokeAndAck) ( remoteHTLCs := lc.channelState.RemoteCommitment.Htlcs - return fwdPkg, addsToForward, settleFailsToForward, remoteHTLCs, nil + return fwdPkg, remoteHTLCs, nil } // LoadFwdPkgs loads any pending log updates from disk and returns the payment diff --git a/lnwallet/channel_test.go b/lnwallet/channel_test.go index 532d7b9a6e..a10872a1fd 100644 --- a/lnwallet/channel_test.go +++ b/lnwallet/channel_test.go @@ -132,7 +132,7 @@ func testAddSettleWorkflow(t *testing.T, tweakless bool, // Alice then processes this revocation, sending her own revocation for // her prior commitment transaction. Alice shouldn't have any HTLCs to // forward since she's sending an outgoing HTLC. - fwdPkg, _, _, _, err := aliceChannel.ReceiveRevocation(bobRevocation) + fwdPkg, _, err := aliceChannel.ReceiveRevocation(bobRevocation) require.NoError(t, err, "alice unable to process bob's revocation") if len(fwdPkg.Adds) != 0 { t.Fatalf("alice forwards %v add htlcs, should forward none", @@ -157,7 +157,7 @@ func testAddSettleWorkflow(t *testing.T, tweakless bool, // is fully locked in within both commitment transactions. Bob should // also be able to forward an HTLC now that the HTLC has been locked // into both commitment transactions. - fwdPkg, _, _, _, err = bobChannel.ReceiveRevocation(aliceRevocation) + fwdPkg, _, err = bobChannel.ReceiveRevocation(aliceRevocation) require.NoError(t, err, "bob unable to process alice's revocation") if len(fwdPkg.Adds) != 1 { t.Fatalf("bob forwards %v add htlcs, should only forward one", @@ -249,7 +249,7 @@ func testAddSettleWorkflow(t *testing.T, tweakless bool, aliceNewCommit, err = aliceChannel.SignNextCommitment() require.NoError(t, err, "alice unable to sign new commitment") - fwdPkg, _, _, _, err = bobChannel.ReceiveRevocation(aliceRevocation2) + fwdPkg, _, err = bobChannel.ReceiveRevocation(aliceRevocation2) require.NoError(t, err, "bob unable to process alice's revocation") if len(fwdPkg.Adds) != 0 { t.Fatalf("bob forwards %v add htlcs, should forward none", @@ -286,7 +286,7 @@ func testAddSettleWorkflow(t *testing.T, tweakless bool, } } - fwdPkg, _, _, _, err = aliceChannel.ReceiveRevocation(bobRevocation2) + fwdPkg, _, err = aliceChannel.ReceiveRevocation(bobRevocation2) require.NoError(t, err, "alice unable to process bob's revocation") if len(fwdPkg.Adds) != 0 { // Alice should now be able to forward the settlement HTLC to @@ -468,7 +468,7 @@ func TestChannelZeroAddLocalHeight(t *testing.T) { aliceRevocation, _, _, err := aliceChannel.RevokeCurrentCommitment() require.NoError(t, err) - _, _, _, _, err = bobChannel.ReceiveRevocation(aliceRevocation) + _, _, err = bobChannel.ReceiveRevocation(aliceRevocation) require.NoError(t, err) // We now restore Alice's channel as this was the point at which @@ -643,7 +643,7 @@ func testCommitHTLCSigTieBreak(t *testing.T, restart bool) { bobRevocation, _, _, err := bobChannel.RevokeCurrentCommitment() require.NoError(t, err, "unable to revoke bob's commitment") - _, _, _, _, err = aliceChannel.ReceiveRevocation(bobRevocation) + _, _, err = aliceChannel.ReceiveRevocation(bobRevocation) require.NoError(t, err, "unable to receive bob's revocation") // Now have Bob initiate the second half of the commitment dance. Here @@ -2554,7 +2554,7 @@ func TestUpdateFeeSenderCommits(t *testing.T) { // Alice receives the revocation of the old one, and can now assume // that Bob's received everything up to the signature she sent, // including the HTLC and fee update. - _, _, _, _, err = aliceChannel.ReceiveRevocation(bobRevocation) + _, _, err = aliceChannel.ReceiveRevocation(bobRevocation) require.NoError(t, err, "alice unable to process bob's revocation") // Alice receives new signature from Bob, and assumes this covers the @@ -2582,7 +2582,7 @@ func TestUpdateFeeSenderCommits(t *testing.T) { } // Bob receives revocation from Alice. - _, _, _, _, err = bobChannel.ReceiveRevocation(aliceRevocation) + _, _, err = bobChannel.ReceiveRevocation(aliceRevocation) require.NoError(t, err, "bob unable to process alice's revocation") } @@ -2641,7 +2641,7 @@ func TestUpdateFeeReceiverCommits(t *testing.T) { require.NoError(t, err, "unable to generate bob revocation") // Bob receives the revocation of the old commitment - _, _, _, _, err = bobChannel.ReceiveRevocation(aliceRevocation) + _, _, err = bobChannel.ReceiveRevocation(aliceRevocation) require.NoError(t, err, "alice unable to process bob's revocation") // Alice will sign next commitment. Since she sent the revocation, she @@ -2682,7 +2682,7 @@ func TestUpdateFeeReceiverCommits(t *testing.T) { // Alice receives revocation from Bob, and can now be sure that Bob // received the two updates, and they are considered locked in. - _, _, _, _, err = aliceChannel.ReceiveRevocation(bobRevocation) + _, _, err = aliceChannel.ReceiveRevocation(bobRevocation) require.NoError(t, err, "bob unable to process alice's revocation") // Alice will receive the signature from Bob, which will cover what was @@ -2710,7 +2710,7 @@ func TestUpdateFeeReceiverCommits(t *testing.T) { } // Bob receives revocation from Alice. - _, _, _, _, err = bobChannel.ReceiveRevocation(aliceRevocation) + _, _, err = bobChannel.ReceiveRevocation(aliceRevocation) require.NoError(t, err, "bob unable to process alice's revocation") } @@ -2819,7 +2819,7 @@ func TestUpdateFeeMultipleUpdates(t *testing.T) { // Alice receives the revocation of the old one, and can now assume that // Bob's received everything up to the signature she sent, including the // HTLC and fee update. - _, _, _, _, err = aliceChannel.ReceiveRevocation(bobRevocation) + _, _, err = aliceChannel.ReceiveRevocation(bobRevocation) require.NoError(t, err, "alice unable to process bob's revocation") // Alice receives new signature from Bob, and assumes this covers the @@ -2849,7 +2849,7 @@ func TestUpdateFeeMultipleUpdates(t *testing.T) { } // Bob receives revocation from Alice. - _, _, _, _, err = bobChannel.ReceiveRevocation(aliceRevocation) + _, _, err = bobChannel.ReceiveRevocation(aliceRevocation) require.NoError(t, err, "bob unable to process alice's revocation") } @@ -3264,13 +3264,13 @@ func testChanSyncOweCommitment(t *testing.T, chanType channeldb.ChannelType) { require.NoError(t, err, "unable to revoke bob commitment") bobNewCommit, err := bobChannel.SignNextCommitment() require.NoError(t, err, "bob unable to sign commitment") - _, _, _, _, err = aliceChannel.ReceiveRevocation(bobRevocation) + _, _, err = aliceChannel.ReceiveRevocation(bobRevocation) require.NoError(t, err, "alice unable to recv revocation") err = aliceChannel.ReceiveNewCommitment(bobNewCommit.CommitSigs) require.NoError(t, err, "alice unable to rev bob's commitment") aliceRevocation, _, _, err := aliceChannel.RevokeCurrentCommitment() require.NoError(t, err, "alice unable to revoke commitment") - _, _, _, _, err = bobChannel.ReceiveRevocation(aliceRevocation) + _, _, err = bobChannel.ReceiveRevocation(aliceRevocation) require.NoError(t, err, "bob unable to recv revocation") // At this point, we'll now assert that their log states are what we @@ -3456,7 +3456,7 @@ func testChanSyncOweCommitmentPendingRemote(t *testing.T, t.Fatalf("unable to revoke commitment: %v", err) } - _, _, _, _, err = aliceChannel.ReceiveRevocation(bobRevoke) + _, _, err = aliceChannel.ReceiveRevocation(bobRevoke) if err != nil { t.Fatalf("unable to revoke commitment: %v", err) } @@ -3492,7 +3492,7 @@ func testChanSyncOweCommitmentPendingRemote(t *testing.T, if err != nil { t.Fatal(err) } - _, _, _, _, err = bobChannel.ReceiveRevocation(aliceRevoke) + _, _, err = bobChannel.ReceiveRevocation(aliceRevoke) if err != nil { t.Fatal(err) } @@ -3586,7 +3586,7 @@ func testChanSyncOweRevocation(t *testing.T, chanType channeldb.ChannelType) { bobNewCommit, err := bobChannel.SignNextCommitment() require.NoError(t, err, "bob unable to sign commitment") - _, _, _, _, err = aliceChannel.ReceiveRevocation(bobRevocation) + _, _, err = aliceChannel.ReceiveRevocation(bobRevocation) require.NoError(t, err, "alice unable to recv revocation") err = aliceChannel.ReceiveNewCommitment(bobNewCommit.CommitSigs) require.NoError(t, err, "alice unable to rev bob's commitment") @@ -3681,7 +3681,7 @@ func testChanSyncOweRevocation(t *testing.T, chanType channeldb.ChannelType) { // We'll continue by then allowing bob to process Alice's revocation // message. - _, _, _, _, err = bobChannel.ReceiveRevocation(aliceRevocation) + _, _, err = bobChannel.ReceiveRevocation(aliceRevocation) require.NoError(t, err, "bob unable to recv revocation") // Finally, Alice will add an HTLC over her own such that we assert the @@ -3898,13 +3898,13 @@ func testChanSyncOweRevocationAndCommit(t *testing.T, // We'll now finish the state transition by having Alice process both // messages, and send her final revocation. - _, _, _, _, err = aliceChannel.ReceiveRevocation(bobRevocation) + _, _, err = aliceChannel.ReceiveRevocation(bobRevocation) require.NoError(t, err, "alice unable to recv revocation") err = aliceChannel.ReceiveNewCommitment(bobNewCommit.CommitSigs) require.NoError(t, err, "alice unable to recv bob's commitment") aliceRevocation, _, _, err := aliceChannel.RevokeCurrentCommitment() require.NoError(t, err, "alice unable to revoke commitment") - _, _, _, _, err = bobChannel.ReceiveRevocation(aliceRevocation) + _, _, err = bobChannel.ReceiveRevocation(aliceRevocation) require.NoError(t, err, "bob unable to recv revocation") } @@ -3989,7 +3989,7 @@ func testChanSyncOweRevocationAndCommitForceTransition(t *testing.T, // local commit chain getting height > remote commit chain. aliceRevocation, _, _, err := aliceChannel.RevokeCurrentCommitment() require.NoError(t, err, "alice unable to revoke commitment") - _, _, _, _, err = bobChannel.ReceiveRevocation(aliceRevocation) + _, _, err = bobChannel.ReceiveRevocation(aliceRevocation) require.NoError(t, err, "bob unable to recv revocation") // Next, Alice will settle that incoming HTLC, then we'll start the @@ -4121,7 +4121,7 @@ func testChanSyncOweRevocationAndCommitForceTransition(t *testing.T, // Now, we'll continue the exchange, sending Bob's revocation and // signature message to Alice, ending with Alice sending her revocation // message to Bob. - _, _, _, _, err = aliceChannel.ReceiveRevocation(bobRevocation) + _, _, err = aliceChannel.ReceiveRevocation(bobRevocation) require.NoError(t, err, "alice unable to recv revocation") err = aliceChannel.ReceiveNewCommitment(&CommitSigs{ CommitSig: bobSigMsg.CommitSig, @@ -4131,7 +4131,7 @@ func testChanSyncOweRevocationAndCommitForceTransition(t *testing.T, require.NoError(t, err, "alice unable to rev bob's commitment") aliceRevocation, _, _, err = aliceChannel.RevokeCurrentCommitment() require.NoError(t, err, "alice unable to revoke commitment") - _, _, _, _, err = bobChannel.ReceiveRevocation(aliceRevocation) + _, _, err = bobChannel.ReceiveRevocation(aliceRevocation) require.NoError(t, err, "bob unable to recv revocation") } @@ -4546,13 +4546,13 @@ func TestChannelRetransmissionFeeUpdate(t *testing.T) { require.NoError(t, err, "unable to revoke bob commitment") bobNewCommit, err := bobChannel.SignNextCommitment() require.NoError(t, err, "bob unable to sign commitment") - _, _, _, _, err = aliceChannel.ReceiveRevocation(bobRevocation) + _, _, err = aliceChannel.ReceiveRevocation(bobRevocation) require.NoError(t, err, "alice unable to recv revocation") err = aliceChannel.ReceiveNewCommitment(bobNewCommit.CommitSigs) require.NoError(t, err, "alice unable to rev bob's commitment") aliceRevocation, _, _, err := aliceChannel.RevokeCurrentCommitment() require.NoError(t, err, "alice unable to revoke commitment") - _, _, _, _, err = bobChannel.ReceiveRevocation(aliceRevocation) + _, _, err = bobChannel.ReceiveRevocation(aliceRevocation) require.NoError(t, err, "bob unable to recv revocation") // Both parties should now have the latest fee rate locked-in. @@ -4744,13 +4744,13 @@ func TestFeeUpdateOldDiskFormat(t *testing.T) { require.NoError(t, err, "unable to revoke bob commitment") bobNewCommitSigs, err := bobChannel.SignNextCommitment() require.NoError(t, err, "bob unable to sign commitment") - _, _, _, _, err = aliceChannel.ReceiveRevocation(bobRevocation) + _, _, err = aliceChannel.ReceiveRevocation(bobRevocation) require.NoError(t, err, "alice unable to recv revocation") err = aliceChannel.ReceiveNewCommitment(bobNewCommitSigs.CommitSigs) require.NoError(t, err, "alice unable to rev bob's commitment") aliceRevocation, _, _, err := aliceChannel.RevokeCurrentCommitment() require.NoError(t, err, "alice unable to revoke commitment") - _, _, _, _, err = bobChannel.ReceiveRevocation(aliceRevocation) + _, _, err = bobChannel.ReceiveRevocation(aliceRevocation) require.NoError(t, err, "bob unable to recv revocation") // Both parties should now have the latest fee rate locked-in. @@ -5459,7 +5459,7 @@ func TestLockedInHtlcForwardingSkipAfterRestart(t *testing.T) { } // Alice should detect that she doesn't need to forward any HTLC's. - fwdPkg, _, _, _, err := aliceChannel.ReceiveRevocation(bobRevocation) + fwdPkg, _, err := aliceChannel.ReceiveRevocation(bobRevocation) if err != nil { t.Fatal(err) } @@ -5490,7 +5490,7 @@ func TestLockedInHtlcForwardingSkipAfterRestart(t *testing.T) { // Bob should now detect that he now has 2 incoming HTLC's that he can // forward along. - fwdPkg, _, _, _, err = bobChannel.ReceiveRevocation(aliceRevocation) + fwdPkg, _, err = bobChannel.ReceiveRevocation(aliceRevocation) if err != nil { t.Fatal(err) } @@ -5535,7 +5535,7 @@ func TestLockedInHtlcForwardingSkipAfterRestart(t *testing.T) { // At this point, Bob receives the revocation from Alice, which is now // his signal to examine all the HTLC's that have been locked in to // process. - fwdPkg, _, _, _, err = bobChannel.ReceiveRevocation(aliceRevocation) + fwdPkg, _, err = bobChannel.ReceiveRevocation(aliceRevocation) if err != nil { t.Fatal(err) } @@ -5584,22 +5584,31 @@ func TestLockedInHtlcForwardingSkipAfterRestart(t *testing.T) { // Alice should detect that she doesn't need to forward any Adds's, but // that the Fail has been locked in an can be forwarded. - _, adds, settleFails, _, err := aliceChannel.ReceiveRevocation(bobRevocation) + fwdPkg, _, err = aliceChannel.ReceiveRevocation(bobRevocation) if err != nil { t.Fatal(err) } + + adds := fwdPkg.Adds + settleFails := fwdPkg.SettleFails if len(adds) != 0 { t.Fatalf("alice shouldn't forward any HTLC's, instead wants to "+ - "forward %v htlcs", len(adds)) + "forward %v htlcs", len(fwdPkg.Adds)) } if len(settleFails) != 1 { t.Fatalf("alice should only forward %d HTLC's, instead wants to "+ - "forward %v htlcs", 1, len(settleFails)) + "forward %v htlcs", 1, len(fwdPkg.SettleFails)) + } + + fail, ok := settleFails[0].UpdateMsg.(*lnwire.UpdateFailHTLC) + if !ok { + t.Fatalf("expected UpdateFailHTLC, got %T", + settleFails[0].UpdateMsg) } - if settleFails[0].ParentIndex != htlc.ID { + if fail.ID != htlc.ID { t.Fatalf("alice should forward fail for htlcid=%d, instead "+ "forwarding id=%d", htlc.ID, - settleFails[0].ParentIndex) + fail.ID) } // We'll now restart both Alice and Bob. This emulates a reconnection @@ -5633,7 +5642,7 @@ func TestLockedInHtlcForwardingSkipAfterRestart(t *testing.T) { // Alice should detect that she doesn't need to forward any HTLC's, as // the updates haven't been committed by Bob yet. - fwdPkg, _, _, _, err = aliceChannel.ReceiveRevocation(bobRevocation) + fwdPkg, _, err = aliceChannel.ReceiveRevocation(bobRevocation) if err != nil { t.Fatal(err) } @@ -5664,7 +5673,7 @@ func TestLockedInHtlcForwardingSkipAfterRestart(t *testing.T) { // Bob should detect that he has nothing to forward, as he hasn't // received any HTLCs. - fwdPkg, _, _, _, err = bobChannel.ReceiveRevocation(aliceRevocation) + fwdPkg, _, err = bobChannel.ReceiveRevocation(aliceRevocation) if err != nil { t.Fatal(err) } @@ -5694,10 +5703,13 @@ func TestLockedInHtlcForwardingSkipAfterRestart(t *testing.T) { // When Alice receives the revocation, she should detect that she // can now forward the freshly locked-in Fail. - _, adds, settleFails, _, err = aliceChannel.ReceiveRevocation(bobRevocation) + fwdPkg, _, err = aliceChannel.ReceiveRevocation(bobRevocation) if err != nil { t.Fatal(err) } + + adds = fwdPkg.Adds + settleFails = fwdPkg.SettleFails if len(adds) != 0 { t.Fatalf("alice shouldn't forward any HTLC's, instead wants to "+ "forward %v htlcs", len(adds)) @@ -5706,10 +5718,16 @@ func TestLockedInHtlcForwardingSkipAfterRestart(t *testing.T) { t.Fatalf("alice should only forward one HTLC, instead wants to "+ "forward %v htlcs", len(settleFails)) } - if settleFails[0].ParentIndex != htlc2.ID { + + fail, ok = settleFails[0].UpdateMsg.(*lnwire.UpdateFailHTLC) + if !ok { + t.Fatalf("expected UpdateFailHTLC, got %T", + settleFails[0].UpdateMsg) + } + if fail.ID != htlc2.ID { t.Fatalf("alice should forward fail for htlcid=%d, instead "+ "forwarding id=%d", htlc2.ID, - settleFails[0].ParentIndex) + fail.ID) } } @@ -6309,13 +6327,13 @@ func TestMaxAsynchronousHtlcs(t *testing.T) { bobRevocation, _, _, err := bobChannel.RevokeCurrentCommitment() require.NoError(t, err, "unable to revoke revocation") - _, _, _, _, err = aliceChannel.ReceiveRevocation(bobRevocation) + _, _, err = aliceChannel.ReceiveRevocation(bobRevocation) require.NoError(t, err, "unable to receive revocation") aliceRevocation, _, _, err := aliceChannel.RevokeCurrentCommitment() require.NoError(t, err, "unable to revoke revocation") - _, _, _, _, err = bobChannel.ReceiveRevocation(aliceRevocation) + _, _, err = bobChannel.ReceiveRevocation(aliceRevocation) require.NoError(t, err, "unable to receive revocation") // Send the final Add which should succeed as in step 6. @@ -6997,7 +7015,7 @@ func TestChannelRestoreUpdateLogs(t *testing.T) { // sent. However her local commitment chain still won't include the // state with the HTLC, since she hasn't received a new commitment // signature from Bob yet. - _, _, _, _, err = aliceChannel.ReceiveRevocation(bobRevocation) + _, _, err = aliceChannel.ReceiveRevocation(bobRevocation) require.NoError(t, err, "unable to receive revocation") // Now make Alice send and sign an additional HTLC. We don't let Bob @@ -7173,7 +7191,7 @@ func TestChannelRestoreUpdateLogsFailedHTLC(t *testing.T) { aliceRevocation, _, _, err := aliceChannel.RevokeCurrentCommitment() require.NoError(t, err, "unable to revoke commitment") - _, _, _, _, err = bobChannel.ReceiveRevocation(aliceRevocation) + _, _, err = bobChannel.ReceiveRevocation(aliceRevocation) require.NoError(t, err, "bob unable to process alice's revocation") // At this point Alice has advanced her local commitment chain to a @@ -7203,7 +7221,7 @@ func TestChannelRestoreUpdateLogsFailedHTLC(t *testing.T) { // the corresponding Fail from the local update log. bobRevocation, _, _, err := bobChannel.RevokeCurrentCommitment() require.NoError(t, err, "unable to revoke commitment") - _, _, _, _, err = aliceChannel.ReceiveRevocation(bobRevocation) + _, _, err = aliceChannel.ReceiveRevocation(bobRevocation) require.NoError(t, err, "unable to receive revocation") assertInLogs(t, aliceChannel, 0, 0, 0, 0) @@ -7428,7 +7446,7 @@ func TestChannelRestoreCommitHeight(t *testing.T) { bobChannel = restoreAndAssertCommitHeights(t, bobChannel, true, 0, 1, 0) // Alice receives the revocation, ACKing her pending commitment. - _, _, _, _, err = aliceChannel.ReceiveRevocation(bobRevocation) + _, _, err = aliceChannel.ReceiveRevocation(bobRevocation) require.NoError(t, err, "unable to receive revocation") // However, the HTLC is still not locked into her local commitment, so @@ -7457,7 +7475,7 @@ func TestChannelRestoreCommitHeight(t *testing.T) { t, aliceChannel, false, 0, 1, 1, ) - _, _, _, _, err = bobChannel.ReceiveRevocation(aliceRevocation) + _, _, err = bobChannel.ReceiveRevocation(aliceRevocation) require.NoError(t, err, "unable to receive revocation") // Alice ACKing Bob's pending commitment shouldn't change the heights @@ -7502,7 +7520,7 @@ func TestChannelRestoreCommitHeight(t *testing.T) { bobChannel = restoreAndAssertCommitHeights(t, bobChannel, true, 1, 2, 0) // Alice receives the revocation, ACKing her pending commitment for Bob. - _, _, _, _, err = aliceChannel.ReceiveRevocation(bobRevocation) + _, _, err = aliceChannel.ReceiveRevocation(bobRevocation) require.NoError(t, err, "unable to receive revocation") // Alice receiving Bob's revocation should bump both addCommitHeightRemote @@ -7540,7 +7558,7 @@ func TestChannelRestoreCommitHeight(t *testing.T) { // Bob receives the revocation, which should set both addCommitHeightRemote // fields to 2. - _, _, _, _, err = bobChannel.ReceiveRevocation(aliceRevocation) + _, _, err = bobChannel.ReceiveRevocation(aliceRevocation) require.NoError(t, err, "unable to receive revocation") bobChannel = restoreAndAssertCommitHeights(t, bobChannel, true, 0, 2, 2) @@ -7643,7 +7661,7 @@ func TestForceCloseBorkedState(t *testing.T) { // At this point, all channel mutating methods should now fail as they // shouldn't be able to proceed if the channel is borked. - _, _, _, _, err = aliceChannel.ReceiveRevocation(revokeMsg) + _, _, err = aliceChannel.ReceiveRevocation(revokeMsg) if err != channeldb.ErrChanBorked { t.Fatalf("advance commitment tail should have failed") } @@ -9436,7 +9454,7 @@ func TestChannelUnsignedAckedFailure(t *testing.T) { // -----rev-----> aliceRevocation, _, _, err := aliceChannel.RevokeCurrentCommitment() require.NoError(t, err) - _, _, _, _, err = bobChannel.ReceiveRevocation(aliceRevocation) + _, _, err = bobChannel.ReceiveRevocation(aliceRevocation) require.NoError(t, err) // Alice should sign the next commitment and go down before @@ -9461,7 +9479,7 @@ func TestChannelUnsignedAckedFailure(t *testing.T) { // <----rev------ bobRevocation, _, _, err := bobChannel.RevokeCurrentCommitment() require.NoError(t, err) - _, _, _, _, err = newAliceChannel.ReceiveRevocation(bobRevocation) + _, _, err = newAliceChannel.ReceiveRevocation(bobRevocation) require.NoError(t, err) // Now Bob sends an HTLC to Alice. @@ -9547,7 +9565,7 @@ func TestChannelLocalUnsignedUpdatesFailure(t *testing.T) { // <----rev----- bobRevocation, _, _, err := bobChannel.RevokeCurrentCommitment() require.NoError(t, err) - _, _, _, _, err = aliceChannel.ReceiveRevocation(bobRevocation) + _, _, err = aliceChannel.ReceiveRevocation(bobRevocation) require.NoError(t, err) // Restart Alice and assert that she can receive Bob's next commitment @@ -9631,7 +9649,7 @@ func TestChannelSignedAckRegression(t *testing.T) { // <----rev----- bobRevocation, _, _, err := bobChannel.RevokeCurrentCommitment() require.NoError(t, err) - _, _, _, _, err = aliceChannel.ReceiveRevocation(bobRevocation) + _, _, err = aliceChannel.ReceiveRevocation(bobRevocation) require.NoError(t, err) // <----sig----- @@ -9658,7 +9676,7 @@ func TestChannelSignedAckRegression(t *testing.T) { // <----rev----- bobRevocation, _, _, err = bobChannel.RevokeCurrentCommitment() require.NoError(t, err) - _, _, _, _, err = aliceChannel.ReceiveRevocation(bobRevocation) + _, _, err = aliceChannel.ReceiveRevocation(bobRevocation) require.NoError(t, err) // Restart Bob's channel state here. @@ -9671,7 +9689,7 @@ func TestChannelSignedAckRegression(t *testing.T) { // -----rev----> aliceRevocation, _, _, err := aliceChannel.RevokeCurrentCommitment() require.NoError(t, err) - fwdPkg, _, _, _, err := newBobChannel.ReceiveRevocation(aliceRevocation) + fwdPkg, _, err := newBobChannel.ReceiveRevocation(aliceRevocation) require.NoError(t, err) // Assert that the fwdpkg is not empty. @@ -9763,7 +9781,7 @@ func TestIsChannelClean(t *testing.T) { // <---rev--- bobRevocation, _, _, err := bobChannel.RevokeCurrentCommitment() require.NoError(t, err) - _, _, _, _, err = aliceChannel.ReceiveRevocation(bobRevocation) + _, _, err = aliceChannel.ReceiveRevocation(bobRevocation) require.NoError(t, err) assertCleanOrDirty(false, aliceChannel, bobChannel, t) @@ -9777,7 +9795,7 @@ func TestIsChannelClean(t *testing.T) { // ---rev---> aliceRevocation, _, _, err := aliceChannel.RevokeCurrentCommitment() require.NoError(t, err) - _, _, _, _, err = bobChannel.ReceiveRevocation(aliceRevocation) + _, _, err = bobChannel.ReceiveRevocation(aliceRevocation) require.NoError(t, err) assertCleanOrDirty(false, aliceChannel, bobChannel, t) @@ -9798,7 +9816,7 @@ func TestIsChannelClean(t *testing.T) { // ---rev---> aliceRevocation, _, _, err = aliceChannel.RevokeCurrentCommitment() require.NoError(t, err) - _, _, _, _, err = bobChannel.ReceiveRevocation(aliceRevocation) + _, _, err = bobChannel.ReceiveRevocation(aliceRevocation) require.NoError(t, err) assertCleanOrDirty(false, aliceChannel, bobChannel, t) @@ -9812,7 +9830,7 @@ func TestIsChannelClean(t *testing.T) { // <---rev--- bobRevocation, _, _, err = bobChannel.RevokeCurrentCommitment() require.NoError(t, err) - _, _, _, _, err = aliceChannel.ReceiveRevocation(bobRevocation) + _, _, err = aliceChannel.ReceiveRevocation(bobRevocation) require.NoError(t, err) assertCleanOrDirty(true, aliceChannel, bobChannel, t) @@ -9836,7 +9854,7 @@ func TestIsChannelClean(t *testing.T) { // <---rev--- bobRevocation, _, _, err = bobChannel.RevokeCurrentCommitment() require.NoError(t, err) - _, _, _, _, err = aliceChannel.ReceiveRevocation(bobRevocation) + _, _, err = aliceChannel.ReceiveRevocation(bobRevocation) require.NoError(t, err) assertCleanOrDirty(false, aliceChannel, bobChannel, t) @@ -9851,7 +9869,7 @@ func TestIsChannelClean(t *testing.T) { // ---rev---> aliceRevocation, _, _, err = aliceChannel.RevokeCurrentCommitment() require.NoError(t, err) - _, _, _, _, err = bobChannel.ReceiveRevocation(aliceRevocation) + _, _, err = bobChannel.ReceiveRevocation(aliceRevocation) require.NoError(t, err) assertCleanOrDirty(true, aliceChannel, bobChannel, t) } @@ -9994,7 +10012,7 @@ func testGetDustSum(t *testing.T, chantype channeldb.ChannelType) { // dust. bobRevocation, _, _, err := bobChannel.RevokeCurrentCommitment() require.NoError(t, err) - _, _, _, _, err = aliceChannel.ReceiveRevocation(bobRevocation) + _, _, err = aliceChannel.ReceiveRevocation(bobRevocation) require.NoError(t, err) checkDust(aliceChannel, htlc2Amt, htlc2Amt) checkDust(bobChannel, htlc2Amt, htlc2Amt) @@ -10007,7 +10025,7 @@ func testGetDustSum(t *testing.T, chantype channeldb.ChannelType) { require.NoError(t, err) aliceRevocation, _, _, err := aliceChannel.RevokeCurrentCommitment() require.NoError(t, err) - _, _, _, _, err = bobChannel.ReceiveRevocation(aliceRevocation) + _, _, err = bobChannel.ReceiveRevocation(aliceRevocation) require.NoError(t, err) checkDust(aliceChannel, htlc2Amt, htlc2Amt) checkDust(bobChannel, htlc2Amt, htlc2Amt) @@ -10967,7 +10985,7 @@ func TestAsynchronousSendingWithFeeBuffer(t *testing.T) { bobRevocation, _, _, err := bobChannel.RevokeCurrentCommitment() require.NoError(t, err) - _, _, _, _, err = aliceChannel.ReceiveRevocation(bobRevocation) + _, _, err = aliceChannel.ReceiveRevocation(bobRevocation) require.NoError(t, err) // Before testing the behavior of the fee buffer, we are going to fail diff --git a/lnwallet/test_utils.go b/lnwallet/test_utils.go index 5e0cac1f8e..a9f71f24c1 100644 --- a/lnwallet/test_utils.go +++ b/lnwallet/test_utils.go @@ -566,7 +566,7 @@ func ForceStateTransition(chanA, chanB *LightningChannel) error { return err } - _, _, _, _, err = chanA.ReceiveRevocation(bobRevocation) + _, _, err = chanA.ReceiveRevocation(bobRevocation) if err != nil { return err } @@ -579,7 +579,7 @@ func ForceStateTransition(chanA, chanB *LightningChannel) error { if err != nil { return err } - _, _, _, _, err = chanB.ReceiveRevocation(aliceRevocation) + _, _, err = chanB.ReceiveRevocation(aliceRevocation) if err != nil { return err } diff --git a/lnwallet/transactions_test.go b/lnwallet/transactions_test.go index 439e7ce955..3588acfebf 100644 --- a/lnwallet/transactions_test.go +++ b/lnwallet/transactions_test.go @@ -366,7 +366,7 @@ func testVectors(t *testing.T, chanType channeldb.ChannelType, test testCase) { revMsg, _, _, err := remoteChannel.RevokeCurrentCommitment() require.NoError(t, err) - _, _, _, _, err = localChannel.ReceiveRevocation(revMsg) + _, _, err = localChannel.ReceiveRevocation(revMsg) require.NoError(t, err) remoteNewCommit, err := remoteChannel.SignNextCommitment() From addf9e8dbe8f203b9c51835217f25f904ed19e72 Mon Sep 17 00:00:00 2001 From: Keagan McClelland Date: Fri, 16 Aug 2024 14:52:44 -0700 Subject: [PATCH 093/218] htlcswitch: remove PaymentDescriptor conversion from reforwardSettleFails This is part of a systematic removal of PaymentDescriptor from the mechanics of the htlcswitch package. --- htlcswitch/switch.go | 40 +++++++++++++++------------------------- 1 file changed, 15 insertions(+), 25 deletions(-) diff --git a/htlcswitch/switch.go b/htlcswitch/switch.go index a839ec3bf0..01596ade02 100644 --- a/htlcswitch/switch.go +++ b/htlcswitch/switch.go @@ -1911,18 +1911,8 @@ func (s *Switch) loadChannelFwdPkgs(source lnwire.ShortChannelID) ([]*channeldb. // NOTE: This should mimic the behavior processRemoteSettleFails. func (s *Switch) reforwardSettleFails(fwdPkgs []*channeldb.FwdPkg) { for _, fwdPkg := range fwdPkgs { - settleFails, err := lnwallet.PayDescsFromRemoteLogUpdates( - fwdPkg.Source, fwdPkg.Height, fwdPkg.SettleFails, - ) - if err != nil { - log.Errorf("Unable to process remote log updates: %v", - err) - continue - } - - switchPackets := make([]*htlcPacket, 0, len(settleFails)) - for i, pd := range settleFails { - + switchPackets := make([]*htlcPacket, 0, len(fwdPkg.SettleFails)) + for i, update := range fwdPkg.SettleFails { // Skip any settles or fails that have already been // acknowledged by the incoming link that originated the // forwarded Add. @@ -1930,20 +1920,18 @@ func (s *Switch) reforwardSettleFails(fwdPkgs []*channeldb.FwdPkg) { continue } - switch pd.EntryType { - + switch msg := update.UpdateMsg.(type) { // A settle for an HTLC we previously forwarded HTLC has // been received. So we'll forward the HTLC to the // switch which will handle propagating the settle to // the prior hop. - case lnwallet.Settle: + case *lnwire.UpdateFulfillHTLC: + destRef := fwdPkg.DestRef(uint16(i)) settlePacket := &htlcPacket{ outgoingChanID: fwdPkg.Source, - outgoingHTLCID: pd.ParentIndex, - destRef: pd.DestRef, - htlc: &lnwire.UpdateFulfillHTLC{ - PaymentPreimage: pd.RPreimage, - }, + outgoingHTLCID: msg.ID, + destRef: &destRef, + htlc: msg, } // Add the packet to the batch to be forwarded, and @@ -1955,7 +1943,7 @@ func (s *Switch) reforwardSettleFails(fwdPkgs []*channeldb.FwdPkg) { // received. As a result a new slot will be freed up in our // commitment state, so we'll forward this to the switch so the // backwards undo can continue. - case lnwallet.Fail: + case *lnwire.UpdateFailHTLC: // Fetch the reason the HTLC was canceled so // we can continue to propagate it. This // failure originated from another node, so @@ -1964,11 +1952,13 @@ func (s *Switch) reforwardSettleFails(fwdPkgs []*channeldb.FwdPkg) { // additional circuit information for us. failPacket := &htlcPacket{ outgoingChanID: fwdPkg.Source, - outgoingHTLCID: pd.ParentIndex, - destRef: pd.DestRef, - htlc: &lnwire.UpdateFailHTLC{ - Reason: lnwire.OpaqueReason(pd.FailReason), + outgoingHTLCID: msg.ID, + destRef: &channeldb.SettleFailRef{ + Source: fwdPkg.Source, + Height: fwdPkg.Height, + Index: uint16(i), }, + htlc: msg, } // Add the packet to the batch to be forwarded, and From 7e5f6b5802a8e9790341465aa006e18bff48cada Mon Sep 17 00:00:00 2001 From: Keagan McClelland Date: Fri, 16 Aug 2024 14:54:30 -0700 Subject: [PATCH 094/218] lnwallet: remove unused function PayDescsFromRemoteLogUpdates This function is no longer used as of the last commit and it is the last remaining leak of the PaymentDescriptor type through the public API. --- lnwallet/channel.go | 97 --------------------------------------------- 1 file changed, 97 deletions(-) diff --git a/lnwallet/channel.go b/lnwallet/channel.go index 6d98372299..1e9ba58427 100644 --- a/lnwallet/channel.go +++ b/lnwallet/channel.go @@ -168,103 +168,6 @@ func (e *ErrCommitSyncLocalDataLoss) Error() string { // payments requested by the wallet/daemon. type PaymentHash [32]byte -// PayDescsFromRemoteLogUpdates converts a slice of LogUpdates received from the -// remote peer into PaymentDescriptors to inform a link's forwarding decisions. -// -// NOTE: The provided `logUpdates` MUST correspond exactly to either the Adds -// or SettleFails in this channel's forwarding package at `height`. -func PayDescsFromRemoteLogUpdates(chanID lnwire.ShortChannelID, height uint64, - logUpdates []channeldb.LogUpdate) ([]*PaymentDescriptor, error) { - - // Allocate enough space to hold all of the payment descriptors we will - // reconstruct, and also the list of pointers that will be returned to - // the caller. - payDescs := make([]PaymentDescriptor, 0, len(logUpdates)) - payDescPtrs := make([]*PaymentDescriptor, 0, len(logUpdates)) - - // Iterate over the log updates we loaded from disk, and reconstruct the - // payment descriptor corresponding to one of the four types of htlcs we - // can receive from the remote peer. We only repopulate the information - // necessary to process the packets and, if necessary, forward them to - // the switch. - // - // For each log update, we include either an AddRef or a SettleFailRef - // so that they can be ACK'd and garbage collected. - for i, logUpdate := range logUpdates { - var pd PaymentDescriptor - switch wireMsg := logUpdate.UpdateMsg.(type) { - - case *lnwire.UpdateAddHTLC: - pd = PaymentDescriptor{ - ChanID: wireMsg.ChanID, - RHash: wireMsg.PaymentHash, - Timeout: wireMsg.Expiry, - Amount: wireMsg.Amount, - EntryType: Add, - HtlcIndex: wireMsg.ID, - LogIndex: logUpdate.LogIndex, - SourceRef: &channeldb.AddRef{ - Height: height, - Index: uint16(i), - }, - BlindingPoint: wireMsg.BlindingPoint, - CustomRecords: wireMsg.CustomRecords.Copy(), - } - pd.OnionBlob = wireMsg.OnionBlob - - case *lnwire.UpdateFulfillHTLC: - pd = PaymentDescriptor{ - ChanID: wireMsg.ChanID, - RPreimage: wireMsg.PaymentPreimage, - ParentIndex: wireMsg.ID, - EntryType: Settle, - DestRef: &channeldb.SettleFailRef{ - Source: chanID, - Height: height, - Index: uint16(i), - }, - } - - case *lnwire.UpdateFailHTLC: - pd = PaymentDescriptor{ - ChanID: wireMsg.ChanID, - ParentIndex: wireMsg.ID, - EntryType: Fail, - FailReason: wireMsg.Reason[:], - DestRef: &channeldb.SettleFailRef{ - Source: chanID, - Height: height, - Index: uint16(i), - }, - } - - case *lnwire.UpdateFailMalformedHTLC: - pd = PaymentDescriptor{ - ChanID: wireMsg.ChanID, - ParentIndex: wireMsg.ID, - EntryType: MalformedFail, - FailCode: wireMsg.FailureCode, - ShaOnionBlob: wireMsg.ShaOnionBlob, - DestRef: &channeldb.SettleFailRef{ - Source: chanID, - Height: height, - Index: uint16(i), - }, - } - - // NOTE: UpdateFee is not expected since they are not forwarded. - case *lnwire.UpdateFee: - return nil, fmt.Errorf("unexpected update fee") - - } - - payDescs = append(payDescs, pd) - payDescPtrs = append(payDescPtrs, &payDescs[i]) - } - - return payDescPtrs, nil -} - // commitment represents a commitment to a new state within an active channel. // New commitments can be initiated by either side. Commitments are ordered // into a commitment chain, with one existing for both parties. Each side can From f8979c0614f35b392399d6ce010f49766ec1d2b1 Mon Sep 17 00:00:00 2001 From: Keagan McClelland Date: Fri, 14 Jun 2024 16:30:28 -0700 Subject: [PATCH 095/218] htlcswitch+lnwallet: quarantine paymentDescriptor to lnwallet The objective of this commit is to make paymentDescriptor a private data structure so we can quarantine it to the lnwallet package. To accomplish this we had to prevent it from leaking out via the arguments or return values of the public functions in lnwallet. This naturally had consequences for the htlcswitch package as we choose other mechanisms for tracking the data that paymentDescriptor was responsible for. Astoundingly, this was highly successful and allowed us to remove a ton of redundant code. The diff for this commit represents a substantial reduction in total lines of code as well as extraneous arguments and return values from key functions. This also sets the stage for future commits where we actually will be attempting to rid lnwallet of paymentDescriptor completely. --- lnwallet/channel.go | 174 +++++++++++++++++---------------- lnwallet/channel_test.go | 82 ++++++++-------- lnwallet/commitment.go | 6 +- lnwallet/payment_descriptor.go | 20 ++-- lnwallet/update_log.go | 24 ++--- 5 files changed, 154 insertions(+), 152 deletions(-) diff --git a/lnwallet/channel.go b/lnwallet/channel.go index 1e9ba58427..0000026c20 100644 --- a/lnwallet/channel.go +++ b/lnwallet/channel.go @@ -234,11 +234,11 @@ type commitment struct { // outgoingHTLCs is a slice of all the outgoing HTLC's (from our PoV) // on this commitment transaction. - outgoingHTLCs []PaymentDescriptor + outgoingHTLCs []paymentDescriptor // incomingHTLCs is a slice of all the incoming HTLC's (from our PoV) // on this commitment transaction. - incomingHTLCs []PaymentDescriptor + incomingHTLCs []paymentDescriptor // customBlob stores opaque bytes that may be used by custom channels // to store extra data for a given commitment state. @@ -254,8 +254,8 @@ type commitment struct { // this map in order to locate the details needed to validate an HTLC // signature while iterating of the outputs in the local commitment // view. - outgoingHTLCIndex map[int32]*PaymentDescriptor - incomingHTLCIndex map[int32]*PaymentDescriptor + outgoingHTLCIndex map[int32]*paymentDescriptor + incomingHTLCIndex map[int32]*paymentDescriptor } // locateOutputIndex is a small helper function to locate the output index of a @@ -263,7 +263,7 @@ type commitment struct { // passed in is to be retained for each output within the commitment // transition. This ensures that we don't assign multiple HTLCs to the same // index within the commitment transaction. -func locateOutputIndex(p *PaymentDescriptor, tx *wire.MsgTx, +func locateOutputIndex(p *paymentDescriptor, tx *wire.MsgTx, whoseCommit lntypes.ChannelParty, dups map[PaymentHash][]int32, cltvs []uint32) (int32, error) { @@ -303,7 +303,7 @@ func locateOutputIndex(p *PaymentDescriptor, tx *wire.MsgTx, // populateHtlcIndexes modifies the set of HTLCs locked-into the target view // to have full indexing information populated. This information is required as // we need to keep track of the indexes of each HTLC in order to properly write -// the current state to disk, and also to locate the PaymentDescriptor +// the current state to disk, and also to locate the paymentDescriptor // corresponding to HTLC outputs in the commitment transaction. func (c *commitment) populateHtlcIndexes(chanType channeldb.ChannelType, cltvs []uint32) error { @@ -313,12 +313,12 @@ func (c *commitment) populateHtlcIndexes(chanType channeldb.ChannelType, // must keep this index so we can validate the HTLC signatures sent to // us. dups := make(map[PaymentHash][]int32) - c.outgoingHTLCIndex = make(map[int32]*PaymentDescriptor) - c.incomingHTLCIndex = make(map[int32]*PaymentDescriptor) + c.outgoingHTLCIndex = make(map[int32]*paymentDescriptor) + c.incomingHTLCIndex = make(map[int32]*paymentDescriptor) // populateIndex is a helper function that populates the necessary // indexes within the commitment view for a particular HTLC. - populateIndex := func(htlc *PaymentDescriptor, incoming bool) error { + populateIndex := func(htlc *paymentDescriptor, incoming bool) error { isDust := HtlcIsDust( chanType, incoming, c.whoseCommit, c.feePerKw, htlc.Amount.ToSatoshis(), c.dustLimit, @@ -482,15 +482,15 @@ func (c *commitment) toDiskCommit( func (lc *LightningChannel) diskHtlcToPayDesc(feeRate chainfee.SatPerKWeight, htlc *channeldb.HTLC, commitKeys lntypes.Dual[*CommitmentKeyRing], whoseCommit lntypes.ChannelParty, - auxLeaf input.AuxTapLeaf) (PaymentDescriptor, error) { + auxLeaf input.AuxTapLeaf) (paymentDescriptor, error) { - // The proper pkScripts for this PaymentDescriptor must be + // The proper pkScripts for this paymentDescriptor must be // generated so we can easily locate them within the commitment // transaction in the future. var ( ourP2WSH, theirP2WSH []byte ourWitnessScript, theirWitnessScript []byte - pd PaymentDescriptor + pd paymentDescriptor chanType = lc.channelState.ChanType ) @@ -550,7 +550,7 @@ func (lc *LightningChannel) diskHtlcToPayDesc(feeRate chainfee.SatPerKWeight, // With the scripts reconstructed (depending on if this is our commit // vs theirs or a pending commit for the remote party), we can now // re-create the original payment descriptor. - return PaymentDescriptor{ + return paymentDescriptor{ ChanID: lc.ChannelID(), RHash: htlc.RHash, Timeout: htlc.RefundTimeout, @@ -577,16 +577,16 @@ func (lc *LightningChannel) diskHtlcToPayDesc(feeRate chainfee.SatPerKWeight, func (lc *LightningChannel) extractPayDescs(feeRate chainfee.SatPerKWeight, htlcs []channeldb.HTLC, commitKeys lntypes.Dual[*CommitmentKeyRing], whoseCommit lntypes.ChannelParty, - auxLeaves fn.Option[CommitAuxLeaves]) ([]PaymentDescriptor, - []PaymentDescriptor, error) { + auxLeaves fn.Option[CommitAuxLeaves]) ([]paymentDescriptor, + []paymentDescriptor, error) { var ( - incomingHtlcs []PaymentDescriptor - outgoingHtlcs []PaymentDescriptor + incomingHtlcs []paymentDescriptor + outgoingHtlcs []paymentDescriptor ) // For each included HTLC within this commitment state, we'll convert - // the disk format into our in memory PaymentDescriptor format, + // the disk format into our in memory paymentDescriptor format, // partitioning based on if we offered or received the HTLC. for _, htlc := range htlcs { // TODO(roasbeef): set isForwarded to false for all? need to @@ -668,7 +668,7 @@ func (lc *LightningChannel) diskCommitToMemCommit( } // With the key rings re-created, we'll now convert all the on-disk - // HTLC"s into PaymentDescriptor's so we can re-insert them into our + // HTLC"s into paymentDescriptor's so we can re-insert them into our // update log. incomingHtlcs, outgoingHtlcs, err := lc.extractPayDescs( chainfee.SatPerKWeight(diskCommit.FeePerKw), @@ -1024,7 +1024,7 @@ func (lc *LightningChannel) ResetState() { lc.Unlock() } -// logUpdateToPayDesc converts a LogUpdate into a matching PaymentDescriptor +// logUpdateToPayDesc converts a LogUpdate into a matching paymentDescriptor // entry that can be re-inserted into the update log. This method is used when // we extended a state to the remote party, but the connection was obstructed // before we could finish the commitment dance. In this case, we need to @@ -1034,25 +1034,25 @@ func (lc *LightningChannel) logUpdateToPayDesc(logUpdate *channeldb.LogUpdate, remoteUpdateLog *updateLog, commitHeight uint64, feeRate chainfee.SatPerKWeight, remoteCommitKeys *CommitmentKeyRing, remoteDustLimit btcutil.Amount, - auxLeaves fn.Option[CommitAuxLeaves]) (*PaymentDescriptor, error) { + auxLeaves fn.Option[CommitAuxLeaves]) (*paymentDescriptor, error) { // Depending on the type of update message we'll map that to a distinct - // PaymentDescriptor instance. - var pd *PaymentDescriptor + // paymentDescriptor instance. + var pd *paymentDescriptor switch wireMsg := logUpdate.UpdateMsg.(type) { - // For offered HTLC's, we'll map that to a PaymentDescriptor with the + // For offered HTLC's, we'll map that to a paymentDescriptor with the // type Add, ensuring we restore the necessary fields. From the PoV of // the commitment chain, this HTLC was included in the remote chain, // but not the local chain. case *lnwire.UpdateAddHTLC: // First, we'll map all the relevant fields in the // UpdateAddHTLC message to their corresponding fields in the - // PaymentDescriptor struct. We also set addCommitHeightRemote + // paymentDescriptor struct. We also set addCommitHeightRemote // as we've included this HTLC in our local commitment chain // for the remote party. - pd = &PaymentDescriptor{ + pd = &paymentDescriptor{ ChanID: wireMsg.ChanID, RHash: wireMsg.PaymentHash, Timeout: wireMsg.Expiry, @@ -1093,11 +1093,11 @@ func (lc *LightningChannel) logUpdateToPayDesc(logUpdate *channeldb.LogUpdate, // For HTLC's we're offered we'll fetch the original offered HTLC // from the remote party's update log so we can retrieve the same - // PaymentDescriptor that SettleHTLC would produce. + // paymentDescriptor that SettleHTLC would produce. case *lnwire.UpdateFulfillHTLC: ogHTLC := remoteUpdateLog.lookupHtlc(wireMsg.ID) - pd = &PaymentDescriptor{ + pd = &paymentDescriptor{ ChanID: wireMsg.ChanID, Amount: ogHTLC.Amount, RHash: ogHTLC.RHash, @@ -1115,7 +1115,7 @@ func (lc *LightningChannel) logUpdateToPayDesc(logUpdate *channeldb.LogUpdate, case *lnwire.UpdateFailHTLC: ogHTLC := remoteUpdateLog.lookupHtlc(wireMsg.ID) - pd = &PaymentDescriptor{ + pd = &paymentDescriptor{ ChanID: wireMsg.ChanID, Amount: ogHTLC.Amount, RHash: ogHTLC.RHash, @@ -1132,7 +1132,7 @@ func (lc *LightningChannel) logUpdateToPayDesc(logUpdate *channeldb.LogUpdate, ogHTLC := remoteUpdateLog.lookupHtlc(wireMsg.ID) // TODO(roasbeef): err if nil? - pd = &PaymentDescriptor{ + pd = &paymentDescriptor{ ChanID: wireMsg.ChanID, Amount: ogHTLC.Amount, RHash: ogHTLC.RHash, @@ -1151,7 +1151,7 @@ func (lc *LightningChannel) logUpdateToPayDesc(logUpdate *channeldb.LogUpdate, // height to the same value, as we consider the fee update locked in by // adding and removing it at the same height. case *lnwire.UpdateFee: - pd = &PaymentDescriptor{ + pd = &paymentDescriptor{ ChanID: wireMsg.ChanID, LogIndex: logUpdate.LogIndex, Amount: lnwire.NewMSatFromSatoshis( @@ -1166,29 +1166,29 @@ func (lc *LightningChannel) logUpdateToPayDesc(logUpdate *channeldb.LogUpdate, return pd, nil } -// localLogUpdateToPayDesc converts a LogUpdate into a matching PaymentDescriptor -// entry that can be re-inserted into the local update log. This method is used -// when we sent an update+sig, receive a revocation, but drop right before the -// counterparty can sign for the update we just sent. In this case, we need to -// re-insert the original entries back into the update log so we'll be expecting -// the peer to sign them. The height of the remote commitment is expected to be -// provided and we restore all log update entries with this height, even though -// the real height may be lower. In the way these fields are used elsewhere, this -// doesn't change anything. +// localLogUpdateToPayDesc converts a LogUpdate into a matching +// paymentDescriptor entry that can be re-inserted into the local update log. +// This method is used when we sent an update+sig, receive a revocation, but +// drop right before the counterparty can sign for the update we just sent. In +// this case, we need to re-insert the original entries back into the update +// log so we'll be expecting the peer to sign them. The height of the remote +// commitment is expected to be provided and we restore all log update entries +// with this height, even though the real height may be lower. In the way these +// fields are used elsewhere, this doesn't change anything. func (lc *LightningChannel) localLogUpdateToPayDesc(logUpdate *channeldb.LogUpdate, - remoteUpdateLog *updateLog, commitHeight uint64) (*PaymentDescriptor, + remoteUpdateLog *updateLog, commitHeight uint64) (*paymentDescriptor, error) { // Since Add updates aren't saved to disk under this key, the update will // never be an Add. switch wireMsg := logUpdate.UpdateMsg.(type) { // For HTLCs that we settled, we'll fetch the original offered HTLC from - // the remote update log so we can retrieve the same PaymentDescriptor that - // ReceiveHTLCSettle would produce. + // the remote update log so we can retrieve the same paymentDescriptor + // that ReceiveHTLCSettle would produce. case *lnwire.UpdateFulfillHTLC: ogHTLC := remoteUpdateLog.lookupHtlc(wireMsg.ID) - return &PaymentDescriptor{ + return &paymentDescriptor{ ChanID: wireMsg.ChanID, Amount: ogHTLC.Amount, RHash: ogHTLC.RHash, @@ -1205,7 +1205,7 @@ func (lc *LightningChannel) localLogUpdateToPayDesc(logUpdate *channeldb.LogUpda case *lnwire.UpdateFailHTLC: ogHTLC := remoteUpdateLog.lookupHtlc(wireMsg.ID) - return &PaymentDescriptor{ + return &paymentDescriptor{ ChanID: wireMsg.ChanID, Amount: ogHTLC.Amount, RHash: ogHTLC.RHash, @@ -1221,7 +1221,7 @@ func (lc *LightningChannel) localLogUpdateToPayDesc(logUpdate *channeldb.LogUpda case *lnwire.UpdateFailMalformedHTLC: ogHTLC := remoteUpdateLog.lookupHtlc(wireMsg.ID) - return &PaymentDescriptor{ + return &paymentDescriptor{ ChanID: wireMsg.ChanID, Amount: ogHTLC.Amount, RHash: ogHTLC.RHash, @@ -1234,7 +1234,7 @@ func (lc *LightningChannel) localLogUpdateToPayDesc(logUpdate *channeldb.LogUpda }, nil case *lnwire.UpdateFee: - return &PaymentDescriptor{ + return &paymentDescriptor{ ChanID: wireMsg.ChanID, LogIndex: logUpdate.LogIndex, Amount: lnwire.NewMSatFromSatoshis( @@ -1251,7 +1251,7 @@ func (lc *LightningChannel) localLogUpdateToPayDesc(logUpdate *channeldb.LogUpda } // remoteLogUpdateToPayDesc converts a LogUpdate into a matching -// PaymentDescriptor entry that can be re-inserted into the update log. This +// paymentDescriptor entry that can be re-inserted into the update log. This // method is used when we revoked a local commitment, but the connection was // obstructed before we could sign a remote commitment that contains these // updates. In this case, we need to re-insert the original entries back into @@ -1261,12 +1261,12 @@ func (lc *LightningChannel) localLogUpdateToPayDesc(logUpdate *channeldb.LogUpda // may be lower. In the way these fields are used elsewhere, this doesn't change // anything. func (lc *LightningChannel) remoteLogUpdateToPayDesc(logUpdate *channeldb.LogUpdate, - localUpdateLog *updateLog, commitHeight uint64) (*PaymentDescriptor, + localUpdateLog *updateLog, commitHeight uint64) (*paymentDescriptor, error) { switch wireMsg := logUpdate.UpdateMsg.(type) { case *lnwire.UpdateAddHTLC: - pd := &PaymentDescriptor{ + pd := &paymentDescriptor{ ChanID: wireMsg.ChanID, RHash: wireMsg.PaymentHash, Timeout: wireMsg.Expiry, @@ -1287,11 +1287,11 @@ func (lc *LightningChannel) remoteLogUpdateToPayDesc(logUpdate *channeldb.LogUpd // For HTLCs that the remote party settled, we'll fetch the original // offered HTLC from the local update log so we can retrieve the same - // PaymentDescriptor that ReceiveHTLCSettle would produce. + // paymentDescriptor that ReceiveHTLCSettle would produce. case *lnwire.UpdateFulfillHTLC: ogHTLC := localUpdateLog.lookupHtlc(wireMsg.ID) - return &PaymentDescriptor{ + return &paymentDescriptor{ ChanID: wireMsg.ChanID, Amount: ogHTLC.Amount, RHash: ogHTLC.RHash, @@ -1308,7 +1308,7 @@ func (lc *LightningChannel) remoteLogUpdateToPayDesc(logUpdate *channeldb.LogUpd case *lnwire.UpdateFailHTLC: ogHTLC := localUpdateLog.lookupHtlc(wireMsg.ID) - return &PaymentDescriptor{ + return &paymentDescriptor{ ChanID: wireMsg.ChanID, Amount: ogHTLC.Amount, RHash: ogHTLC.RHash, @@ -1324,7 +1324,7 @@ func (lc *LightningChannel) remoteLogUpdateToPayDesc(logUpdate *channeldb.LogUpd case *lnwire.UpdateFailMalformedHTLC: ogHTLC := localUpdateLog.lookupHtlc(wireMsg.ID) - return &PaymentDescriptor{ + return &paymentDescriptor{ ChanID: wireMsg.ChanID, Amount: ogHTLC.Amount, RHash: ogHTLC.RHash, @@ -1343,7 +1343,7 @@ func (lc *LightningChannel) remoteLogUpdateToPayDesc(logUpdate *channeldb.LogUpd // height to the same value, as we consider the fee update locked in by // adding and removing it at the same height. case *lnwire.UpdateFee: - return &PaymentDescriptor{ + return &paymentDescriptor{ ChanID: wireMsg.ChanID, LogIndex: logUpdate.LogIndex, Amount: lnwire.NewMSatFromSatoshis( @@ -2536,10 +2536,10 @@ type HtlcView struct { NextHeight uint64 // OurUpdates are our outgoing HTLCs. - OurUpdates []*PaymentDescriptor + OurUpdates []*paymentDescriptor // TheirUpdates are their incoming HTLCs. - TheirUpdates []*PaymentDescriptor + TheirUpdates []*paymentDescriptor // FeePerKw is the fee rate in sat/kw of the commitment transaction. FeePerKw chainfee.SatPerKWeight @@ -2551,7 +2551,7 @@ type HtlcView struct { func (lc *LightningChannel) fetchHTLCView(theirLogIndex, ourLogIndex uint64) *HtlcView { - var ourHTLCs []*PaymentDescriptor + var ourHTLCs []*paymentDescriptor for e := lc.updateLogs.Local.Front(); e != nil; e = e.Next() { htlc := e.Value @@ -2563,7 +2563,7 @@ func (lc *LightningChannel) fetchHTLCView(theirLogIndex, } } - var theirHTLCs []*PaymentDescriptor + var theirHTLCs []*paymentDescriptor for e := lc.updateLogs.Remote.Front(); e != nil; e = e.Next() { htlc := e.Value @@ -2701,13 +2701,13 @@ func (lc *LightningChannel) fetchCommitmentView( // commitment are mutated, we'll manually copy over each HTLC to its // respective slice. c.outgoingHTLCs = make( - []PaymentDescriptor, len(filteredHTLCView.OurUpdates), + []paymentDescriptor, len(filteredHTLCView.OurUpdates), ) for i, htlc := range filteredHTLCView.OurUpdates { c.outgoingHTLCs[i] = *htlc } c.incomingHTLCs = make( - []PaymentDescriptor, len(filteredHTLCView.TheirUpdates), + []paymentDescriptor, len(filteredHTLCView.TheirUpdates), ) for i, htlc := range filteredHTLCView.TheirUpdates { c.incomingHTLCs[i] = *htlc @@ -2882,9 +2882,9 @@ func (lc *LightningChannel) evaluateHTLCView(view *HtlcView, ourBalance, // fetchParent is a helper that looks up update log parent entries in the // appropriate log. -func (lc *LightningChannel) fetchParent(entry *PaymentDescriptor, +func (lc *LightningChannel) fetchParent(entry *paymentDescriptor, whoseCommitChain, whoseUpdateLog lntypes.ChannelParty, -) (*PaymentDescriptor, error) { +) (*paymentDescriptor, error) { var ( updateLog *updateLog @@ -2937,7 +2937,7 @@ func (lc *LightningChannel) fetchParent(entry *PaymentDescriptor, // If the HTLC hasn't yet been committed in either chain, then the height it // was committed is updated. Keeping track of this inclusion height allows us to // later compact the log once the change is fully committed in both chains. -func processAddEntry(htlc *PaymentDescriptor, ourBalance, +func processAddEntry(htlc *paymentDescriptor, ourBalance, theirBalance *lnwire.MilliSatoshi, nextHeight uint64, whoseCommitChain lntypes.ChannelParty, isIncoming, mutateState bool) { @@ -2975,7 +2975,7 @@ func processAddEntry(htlc *PaymentDescriptor, ourBalance, // processRemoveEntry processes a log entry which settles or times out a // previously added HTLC. If the removal entry has already been processed, it // is skipped. -func processRemoveEntry(htlc *PaymentDescriptor, ourBalance, +func processRemoveEntry(htlc *paymentDescriptor, ourBalance, theirBalance *lnwire.MilliSatoshi, nextHeight uint64, whoseCommitChain lntypes.ChannelParty, isIncoming, mutateState bool) { @@ -3024,7 +3024,7 @@ func processRemoveEntry(htlc *PaymentDescriptor, ourBalance, // processFeeUpdate processes a log update that updates the current commitment // fee. -func processFeeUpdate(feeUpdate *PaymentDescriptor, nextHeight uint64, +func processFeeUpdate(feeUpdate *paymentDescriptor, nextHeight uint64, whoseCommitChain lntypes.ChannelParty, mutateState bool, view *HtlcView) { @@ -3298,7 +3298,7 @@ func (lc *LightningChannel) createCommitDiff(newCommit *commitment, continue } - // We'll map the type of the PaymentDescriptor to one of the + // We'll map the type of the paymentDescriptor to one of the // four messages that it corresponds to. With this set of // messages obtained, we can simply read from disk and re-send // them in the case of a needed channel sync. @@ -3333,7 +3333,7 @@ func (lc *LightningChannel) createCommitDiff(newCommit *commitment, // Nothing special to do. } - logUpdates = append(logUpdates, pd.ToLogUpdate()) + logUpdates = append(logUpdates, pd.toLogUpdate()) } // With the set of log updates mapped into wire messages, we'll now @@ -3391,7 +3391,7 @@ func (lc *LightningChannel) getUnsignedAckedUpdates() []channeldb.LogUpdate { continue } - logUpdates = append(logUpdates, pd.ToLogUpdate()) + logUpdates = append(logUpdates, pd.toLogUpdate()) } return logUpdates @@ -3535,11 +3535,11 @@ func (lc *LightningChannel) applyCommitFee( // commitment transaction in terms of the ChannelConstraints that we and our // remote peer agreed upon during the funding workflow. The // predict[Our|Their]Add should parameters should be set to a valid -// PaymentDescriptor if we are validating in the state when adding a new HTLC, +// paymentDescriptor if we are validating in the state when adding a new HTLC, // or nil otherwise. func (lc *LightningChannel) validateCommitmentSanity(theirLogCounter, ourLogCounter uint64, whoseCommitChain lntypes.ChannelParty, - buffer BufferType, predictOurAdd, predictTheirAdd *PaymentDescriptor, + buffer BufferType, predictOurAdd, predictTheirAdd *paymentDescriptor, ) error { // First fetch the initial balance before applying any updates. @@ -3666,7 +3666,7 @@ func (lc *LightningChannel) validateCommitmentSanity(theirLogCounter, // validateUpdates take a set of updates, and validates them against // the passed channel constraints. - validateUpdates := func(updates []*PaymentDescriptor, + validateUpdates := func(updates []*paymentDescriptor, constraints *channeldb.ChannelConfig) error { // We keep track of the number of HTLCs in flight for the @@ -5390,7 +5390,7 @@ func (lc *LightningChannel) ReceiveRevocation(revMsg *lnwire.RevokeAndAck) ( // updates that we will eventually put into the // FwdPkg at this height. addUpdatesToForward = append( - addUpdatesToForward, pd.ToLogUpdate(), + addUpdatesToForward, pd.toLogUpdate(), ) case pd.EntryType != Add && committedRmv && shouldFwdRmv: @@ -5410,10 +5410,12 @@ func (lc *LightningChannel) ReceiveRevocation(revMsg *lnwire.RevokeAndAck) ( // updates that we will eventually put into the // FwdPkg at this height. settleFailUpdatesToForward = append( - settleFailUpdatesToForward, pd.ToLogUpdate(), + settleFailUpdatesToForward, pd.toLogUpdate(), ) default: + // The update was not "freshly locked in" so we will + // ignore it as we construct the forwarding package. continue } } @@ -5720,9 +5722,9 @@ func (lc *LightningChannel) MayAddOutgoingHtlc(amt lnwire.MilliSatoshi) error { // htlcAddDescriptor returns a payment descriptor for the htlc and open key // provided to add to our local update log. func (lc *LightningChannel) htlcAddDescriptor(htlc *lnwire.UpdateAddHTLC, - openKey *models.CircuitKey) *PaymentDescriptor { + openKey *models.CircuitKey) *paymentDescriptor { - return &PaymentDescriptor{ + return &paymentDescriptor{ ChanID: htlc.ChanID, EntryType: Add, RHash: PaymentHash(htlc.PaymentHash), @@ -5739,7 +5741,7 @@ func (lc *LightningChannel) htlcAddDescriptor(htlc *lnwire.UpdateAddHTLC, // validateAddHtlc validates the addition of an outgoing htlc to our local and // remote commitments. -func (lc *LightningChannel) validateAddHtlc(pd *PaymentDescriptor, +func (lc *LightningChannel) validateAddHtlc(pd *paymentDescriptor, buffer BufferType) error { // Make sure adding this HTLC won't violate any of the constraints we // must keep on the commitment transactions. @@ -5785,7 +5787,7 @@ func (lc *LightningChannel) ReceiveHTLC(htlc *lnwire.UpdateAddHTLC) (uint64, "ID %d", htlc.ID, lc.updateLogs.Remote.htlcCounter) } - pd := &PaymentDescriptor{ + pd := &paymentDescriptor{ ChanID: htlc.ChanID, EntryType: Add, RHash: PaymentHash(htlc.PaymentHash), @@ -5866,7 +5868,7 @@ func (lc *LightningChannel) SettleHTLC(preimage [32]byte, return ErrInvalidSettlePreimage{preimage[:], htlc.RHash[:]} } - pd := &PaymentDescriptor{ + pd := &paymentDescriptor{ ChanID: lc.ChannelID(), Amount: htlc.Amount, RPreimage: preimage, @@ -5912,7 +5914,7 @@ func (lc *LightningChannel) ReceiveHTLCSettle(preimage [32]byte, htlcIndex uint6 return ErrInvalidSettlePreimage{preimage[:], htlc.RHash[:]} } - pd := &PaymentDescriptor{ + pd := &paymentDescriptor{ ChanID: lc.ChannelID(), Amount: htlc.Amount, RPreimage: preimage, @@ -5974,7 +5976,7 @@ func (lc *LightningChannel) FailHTLC(htlcIndex uint64, reason []byte, return ErrHtlcIndexAlreadyFailed(htlcIndex) } - pd := &PaymentDescriptor{ + pd := &paymentDescriptor{ ChanID: lc.ChannelID(), Amount: htlc.Amount, RHash: htlc.RHash, @@ -6025,7 +6027,7 @@ func (lc *LightningChannel) MalformedFailHTLC(htlcIndex uint64, return ErrHtlcIndexAlreadyFailed(htlcIndex) } - pd := &PaymentDescriptor{ + pd := &paymentDescriptor{ ChanID: lc.ChannelID(), Amount: htlc.Amount, RHash: htlc.RHash, @@ -6068,7 +6070,7 @@ func (lc *LightningChannel) ReceiveFailHTLC(htlcIndex uint64, reason []byte, return ErrHtlcIndexAlreadyFailed(htlcIndex) } - pd := &PaymentDescriptor{ + pd := &paymentDescriptor{ ChanID: lc.ChannelID(), Amount: htlc.Amount, RHash: htlc.RHash, @@ -8188,7 +8190,7 @@ func (lc *LightningChannel) UpdateFee(feePerKw chainfee.SatPerKWeight) error { return err } - pd := &PaymentDescriptor{ + pd := &paymentDescriptor{ ChanID: lc.ChannelID(), LogIndex: lc.updateLogs.Local.logIndex, Amount: lnwire.NewMSatFromSatoshis(btcutil.Amount(feePerKw)), @@ -8261,7 +8263,7 @@ func (lc *LightningChannel) ReceiveUpdateFee(feePerKw chainfee.SatPerKWeight) er } // TODO(roasbeef): or just modify to use the other balance? - pd := &PaymentDescriptor{ + pd := &paymentDescriptor{ ChanID: lc.ChannelID(), LogIndex: lc.updateLogs.Remote.logIndex, Amount: lnwire.NewMSatFromSatoshis(btcutil.Amount(feePerKw)), @@ -8752,7 +8754,7 @@ func (lc *LightningChannel) unsignedLocalUpdates(remoteMessageIndex, // sends. if pd.LogIndex < remoteMessageIndex && pd.LogIndex >= localMessageIndex { localPeerUpdates = append( - localPeerUpdates, pd.ToLogUpdate(), + localPeerUpdates, pd.toLogUpdate(), ) } } diff --git a/lnwallet/channel_test.go b/lnwallet/channel_test.go index a10872a1fd..d97a920424 100644 --- a/lnwallet/channel_test.go +++ b/lnwallet/channel_test.go @@ -6896,8 +6896,8 @@ func TestNewBreachRetributionSkipsDustHtlcs(t *testing.T) { } } -// compareHtlcs compares two PaymentDescriptors. -func compareHtlcs(htlc1, htlc2 *PaymentDescriptor) error { +// compareHtlcs compares two paymentDescriptors. +func compareHtlcs(htlc1, htlc2 *paymentDescriptor) error { if htlc1.LogIndex != htlc2.LogIndex { return fmt.Errorf("htlc log index did not match") } @@ -6915,7 +6915,7 @@ func compareHtlcs(htlc1, htlc2 *PaymentDescriptor) error { } // compareIndexes is a helper method to compare two index maps. -func compareIndexes(a, b map[uint64]*fn.Node[*PaymentDescriptor]) error { +func compareIndexes(a, b map[uint64]*fn.Node[*paymentDescriptor]) error { for k1, e1 := range a { e2, ok := b[k1] if !ok { @@ -7384,7 +7384,7 @@ func TestChannelRestoreCommitHeight(t *testing.T) { t.Fatalf("unable to create new channel: %v", err) } - var pd *PaymentDescriptor + var pd *paymentDescriptor if remoteLog { h := newChannel.updateLogs.Local.lookupHtlc(htlcIndex) if h != nil { @@ -8152,8 +8152,8 @@ func TestFetchParent(t *testing.T) { name string whoseCommitChain lntypes.ChannelParty whoseUpdateLog lntypes.ChannelParty - localEntries []*PaymentDescriptor - remoteEntries []*PaymentDescriptor + localEntries []*paymentDescriptor + remoteEntries []*paymentDescriptor // parentIndex is the parent index of the entry that we will // lookup with fetch parent. @@ -8187,7 +8187,7 @@ func TestFetchParent(t *testing.T) { { name: "remote log + chain, remote add height 0", localEntries: nil, - remoteEntries: []*PaymentDescriptor{ + remoteEntries: []*paymentDescriptor{ // This entry will be added at log index =0. { HtlcIndex: 1, @@ -8209,7 +8209,7 @@ func TestFetchParent(t *testing.T) { }, { name: "remote log, local chain, local add height 0", - remoteEntries: []*PaymentDescriptor{ + remoteEntries: []*paymentDescriptor{ // This entry will be added at log index =0. { HtlcIndex: 1, @@ -8232,7 +8232,7 @@ func TestFetchParent(t *testing.T) { }, { name: "local log + chain, local add height 0", - localEntries: []*PaymentDescriptor{ + localEntries: []*paymentDescriptor{ // This entry will be added at log index =0. { HtlcIndex: 1, @@ -8256,7 +8256,7 @@ func TestFetchParent(t *testing.T) { { name: "local log + remote chain, remote add height 0", - localEntries: []*PaymentDescriptor{ + localEntries: []*paymentDescriptor{ // This entry will be added at log index =0. { HtlcIndex: 1, @@ -8280,7 +8280,7 @@ func TestFetchParent(t *testing.T) { { name: "remote log found", localEntries: nil, - remoteEntries: []*PaymentDescriptor{ + remoteEntries: []*paymentDescriptor{ // This entry will be added at log index =0. { HtlcIndex: 1, @@ -8303,7 +8303,7 @@ func TestFetchParent(t *testing.T) { }, { name: "local log found", - localEntries: []*PaymentDescriptor{ + localEntries: []*paymentDescriptor{ // This entry will be added at log index =0. { HtlcIndex: 1, @@ -8349,7 +8349,7 @@ func TestFetchParent(t *testing.T) { } parent, err := lc.fetchParent( - &PaymentDescriptor{ + &paymentDescriptor{ ParentIndex: test.parentIndex, }, test.whoseCommitChain, @@ -8412,8 +8412,8 @@ func TestEvaluateView(t *testing.T) { tests := []struct { name string - ourHtlcs []*PaymentDescriptor - theirHtlcs []*PaymentDescriptor + ourHtlcs []*paymentDescriptor + theirHtlcs []*paymentDescriptor whoseCommitChain lntypes.ChannelParty mutateState bool @@ -8445,7 +8445,7 @@ func TestEvaluateView(t *testing.T) { name: "our fee update is applied", whoseCommitChain: lntypes.Local, mutateState: false, - ourHtlcs: []*PaymentDescriptor{ + ourHtlcs: []*paymentDescriptor{ { Amount: ourFeeUpdateAmt, EntryType: FeeUpdate, @@ -8462,8 +8462,8 @@ func TestEvaluateView(t *testing.T) { name: "their fee update is applied", whoseCommitChain: lntypes.Local, mutateState: false, - ourHtlcs: []*PaymentDescriptor{}, - theirHtlcs: []*PaymentDescriptor{ + ourHtlcs: []*paymentDescriptor{}, + theirHtlcs: []*paymentDescriptor{ { Amount: theirFeeUpdateAmt, EntryType: FeeUpdate, @@ -8480,14 +8480,14 @@ func TestEvaluateView(t *testing.T) { name: "htlcs adds without settles", whoseCommitChain: lntypes.Local, mutateState: false, - ourHtlcs: []*PaymentDescriptor{ + ourHtlcs: []*paymentDescriptor{ { HtlcIndex: 0, Amount: htlcAddAmount, EntryType: Add, }, }, - theirHtlcs: []*PaymentDescriptor{ + theirHtlcs: []*paymentDescriptor{ { HtlcIndex: 0, Amount: htlcAddAmount, @@ -8514,7 +8514,7 @@ func TestEvaluateView(t *testing.T) { name: "our htlc settled, state mutated", whoseCommitChain: lntypes.Local, mutateState: true, - ourHtlcs: []*PaymentDescriptor{ + ourHtlcs: []*paymentDescriptor{ { HtlcIndex: 0, Amount: htlcAddAmount, @@ -8522,7 +8522,7 @@ func TestEvaluateView(t *testing.T) { addCommitHeightLocal: addHeight, }, }, - theirHtlcs: []*PaymentDescriptor{ + theirHtlcs: []*paymentDescriptor{ { HtlcIndex: 0, Amount: htlcAddAmount, @@ -8549,7 +8549,7 @@ func TestEvaluateView(t *testing.T) { name: "our htlc settled, state not mutated", whoseCommitChain: lntypes.Local, mutateState: false, - ourHtlcs: []*PaymentDescriptor{ + ourHtlcs: []*paymentDescriptor{ { HtlcIndex: 0, Amount: htlcAddAmount, @@ -8557,7 +8557,7 @@ func TestEvaluateView(t *testing.T) { addCommitHeightLocal: addHeight, }, }, - theirHtlcs: []*PaymentDescriptor{ + theirHtlcs: []*paymentDescriptor{ { HtlcIndex: 0, Amount: htlcAddAmount, @@ -8584,7 +8584,7 @@ func TestEvaluateView(t *testing.T) { name: "their htlc settled, state mutated", whoseCommitChain: lntypes.Local, mutateState: true, - ourHtlcs: []*PaymentDescriptor{ + ourHtlcs: []*paymentDescriptor{ { HtlcIndex: 0, Amount: htlcAddAmount, @@ -8599,7 +8599,7 @@ func TestEvaluateView(t *testing.T) { ParentIndex: 1, }, }, - theirHtlcs: []*PaymentDescriptor{ + theirHtlcs: []*paymentDescriptor{ { HtlcIndex: 0, Amount: htlcAddAmount, @@ -8628,7 +8628,7 @@ func TestEvaluateView(t *testing.T) { whoseCommitChain: lntypes.Local, mutateState: false, - ourHtlcs: []*PaymentDescriptor{ + ourHtlcs: []*paymentDescriptor{ { HtlcIndex: 0, Amount: htlcAddAmount, @@ -8643,7 +8643,7 @@ func TestEvaluateView(t *testing.T) { ParentIndex: 0, }, }, - theirHtlcs: []*PaymentDescriptor{ + theirHtlcs: []*paymentDescriptor{ { HtlcIndex: 0, Amount: htlcAddAmount, @@ -8750,9 +8750,9 @@ func TestEvaluateView(t *testing.T) { // checkExpectedHtlcs checks that a set of htlcs that we have contains all the // htlcs we expect. -func checkExpectedHtlcs(t *testing.T, actual []*PaymentDescriptor, - expected map[uint64]bool, -) { +func checkExpectedHtlcs(t *testing.T, actual []*paymentDescriptor, + expected map[uint64]bool) { + if len(expected) != len(actual) { t.Fatalf("expected: %v htlcs, got: %v", len(expected), len(actual)) @@ -8942,7 +8942,7 @@ func TestProcessFeeUpdate(t *testing.T) { // Create a fee update with add and remove heights as // set in the test. heights := test.startHeights - update := &PaymentDescriptor{ + update := &paymentDescriptor{ Amount: ourFeeUpdateAmt, addCommitHeightRemote: heights.remoteAdd, addCommitHeightLocal: heights.localAdd, @@ -8969,7 +8969,7 @@ func TestProcessFeeUpdate(t *testing.T) { } } -func checkHeights(t *testing.T, update *PaymentDescriptor, expected heights) { +func checkHeights(t *testing.T, update *paymentDescriptor, expected heights) { updateHeights := heights{ localAdd: update.addCommitHeightLocal, localRemove: update.removeCommitHeightLocal, @@ -9338,7 +9338,7 @@ func TestProcessAddRemoveEntry(t *testing.T) { t.Parallel() heights := test.startHeights - update := &PaymentDescriptor{ + update := &paymentDescriptor{ Amount: updateAmount, addCommitHeightLocal: heights.localAdd, addCommitHeightRemote: heights.remoteAdd, @@ -10531,7 +10531,7 @@ func testNewBreachRetribution(t *testing.T, chanType channeldb.ChannelType) { } // TestExtractPayDescs asserts that `extractPayDescs` can correctly turn a -// slice of htlcs into two slices of PaymentDescriptors. +// slice of htlcs into two slices of paymentDescriptors. func TestExtractPayDescs(t *testing.T) { t.Parallel() @@ -10568,24 +10568,24 @@ func TestExtractPayDescs(t *testing.T) { ) require.NoError(t, err) - // Assert the incoming PaymentDescriptors are matched. + // Assert the incoming paymentDescriptors are matched. for i, pd := range incomingPDs { htlc := incomings[i] assertPayDescMatchHTLC(t, pd, htlc) } - // Assert the outgoing PaymentDescriptors are matched. + // Assert the outgoing paymentDescriptors are matched. for i, pd := range outgoingPDs { htlc := outgoings[i] assertPayDescMatchHTLC(t, pd, htlc) } } -// assertPayDescMatchHTLC compares a PaymentDescriptor to a channeldb.HTLC and +// assertPayDescMatchHTLC compares a paymentDescriptor to a channeldb.HTLC and // asserts that the fields are matched. -func assertPayDescMatchHTLC(t *testing.T, pd PaymentDescriptor, - htlc channeldb.HTLC, -) { +func assertPayDescMatchHTLC(t *testing.T, pd paymentDescriptor, + htlc channeldb.HTLC) { + require := require.New(t) require.EqualValues(htlc.RHash, pd.RHash, "RHash") diff --git a/lnwallet/commitment.go b/lnwallet/commitment.go index 2ff23ab635..73565ea184 100644 --- a/lnwallet/commitment.go +++ b/lnwallet/commitment.go @@ -1229,10 +1229,10 @@ func genHtlcScript(chanType channeldb.ChannelType, isIncoming bool, // is incoming and if it's being applied to our commitment transaction or that // of the remote node's. Additionally, in order to be able to efficiently // locate the added HTLC on the commitment transaction from the -// PaymentDescriptor that generated it, the generated script is stored within +// paymentDescriptor that generated it, the generated script is stored within // the descriptor itself. func addHTLC(commitTx *wire.MsgTx, whoseCommit lntypes.ChannelParty, - isIncoming bool, paymentDesc *PaymentDescriptor, + isIncoming bool, paymentDesc *paymentDescriptor, keyRing *CommitmentKeyRing, chanType channeldb.ChannelType, auxLeaf input.AuxTapLeaf) error { @@ -1253,7 +1253,7 @@ func addHTLC(commitTx *wire.MsgTx, whoseCommit lntypes.ChannelParty, amountPending := int64(paymentDesc.Amount.ToSatoshis()) commitTx.AddTxOut(wire.NewTxOut(amountPending, pkScript)) - // Store the pkScript of this particular PaymentDescriptor so we can + // Store the pkScript of this particular paymentDescriptor so we can // quickly locate it within the commitment transaction later. if whoseCommit.IsLocal() { paymentDesc.ourPkScript = pkScript diff --git a/lnwallet/payment_descriptor.go b/lnwallet/payment_descriptor.go index 2d6c158cd9..5a51f29ce5 100644 --- a/lnwallet/payment_descriptor.go +++ b/lnwallet/payment_descriptor.go @@ -62,17 +62,17 @@ func (u updateType) String() string { } } -// PaymentDescriptor represents a commitment state update which either adds, -// settles, or removes an HTLC. PaymentDescriptors encapsulate all necessary +// paymentDescriptor represents a commitment state update which either adds, +// settles, or removes an HTLC. paymentDescriptors encapsulate all necessary // metadata w.r.t to an HTLC, and additional data pairing a settle message to // the original added HTLC. // // TODO(roasbeef): LogEntry interface?? // - need to separate attrs for cancel/add/settle/feeupdate -type PaymentDescriptor struct { +type paymentDescriptor struct { // ChanID is the ChannelID of the LightningChannel that this - // PaymentDescriptor belongs to. We track this here so we can - // reconstruct the Messages that this PaymentDescriptor is built from. + // paymentDescriptor belongs to. We track this here so we can + // reconstruct the Messages that this paymentDescriptor is built from. ChanID lnwire.ChannelID // RHash is the payment hash for this HTLC. The HTLC can be settled iff @@ -170,7 +170,7 @@ type PaymentDescriptor struct { // removeCommitHeight[Remote|Local] encodes the height of the // commitment which removed the parent pointer of this - // PaymentDescriptor either due to a timeout or a settle. Once both + // paymentDescriptor either due to a timeout or a settle. Once both // these heights are below the tail of both chains, the log entries can // safely be removed. removeCommitHeightRemote uint64 @@ -199,7 +199,7 @@ type PaymentDescriptor struct { // [our|their|]PkScript are the raw public key scripts that encodes the // redemption rules for this particular HTLC. These fields will only be - // populated iff the EntryType of this PaymentDescriptor is Add. + // populated iff the EntryType of this paymentDescriptor is Add. // ourPkScript is the ourPkScript from the context of our local // commitment chain. theirPkScript is the latest pkScript from the // context of the remote commitment chain. @@ -212,7 +212,7 @@ type PaymentDescriptor struct { theirPkScript []byte theirWitnessScript []byte - // EntryType denotes the exact type of the PaymentDescriptor. In the + // EntryType denotes the exact type of the paymentDescriptor. In the // case of a Timeout, or Settle type, then the Parent field will point // into the log to the HTLC being modified. EntryType updateType @@ -232,11 +232,11 @@ type PaymentDescriptor struct { CustomRecords lnwire.CustomRecords } -// ToLogUpdate recovers the underlying LogUpdate from the paymentDescriptor. +// toLogUpdate recovers the underlying LogUpdate from the paymentDescriptor. // This operation is lossy and will forget some extra information tracked by the // paymentDescriptor but the function is total in that all paymentDescriptors // can be converted back to LogUpdates. -func (pd *PaymentDescriptor) ToLogUpdate() channeldb.LogUpdate { +func (pd *paymentDescriptor) toLogUpdate() channeldb.LogUpdate { var msg lnwire.Message switch pd.EntryType { case Add: diff --git a/lnwallet/update_log.go b/lnwallet/update_log.go index 5b9d6c5657..42e5373a22 100644 --- a/lnwallet/update_log.go +++ b/lnwallet/update_log.go @@ -29,16 +29,16 @@ type updateLog struct { // List is the updatelog itself, we embed this value so updateLog has // access to all the method of a list.List. - *fn.List[*PaymentDescriptor] + *fn.List[*paymentDescriptor] // updateIndex maps a `logIndex` to a particular update entry. It // deals with the four update types: // `Fail|MalformedFail|Settle|FeeUpdate` - updateIndex map[uint64]*fn.Node[*PaymentDescriptor] + updateIndex map[uint64]*fn.Node[*paymentDescriptor] // htlcIndex maps a `htlcCounter` to an offered HTLC entry, hence the // `Add` update. - htlcIndex map[uint64]*fn.Node[*PaymentDescriptor] + htlcIndex map[uint64]*fn.Node[*paymentDescriptor] // modifiedHtlcs is a set that keeps track of all the current modified // htlcs, hence update types `Fail|MalformedFail|Settle`. A modified @@ -50,9 +50,9 @@ type updateLog struct { // newUpdateLog creates a new updateLog instance. func newUpdateLog(logIndex, htlcCounter uint64) *updateLog { return &updateLog{ - List: fn.NewList[*PaymentDescriptor](), - updateIndex: make(map[uint64]*fn.Node[*PaymentDescriptor]), - htlcIndex: make(map[uint64]*fn.Node[*PaymentDescriptor]), + List: fn.NewList[*paymentDescriptor](), + updateIndex: make(map[uint64]*fn.Node[*paymentDescriptor]), + htlcIndex: make(map[uint64]*fn.Node[*paymentDescriptor]), logIndex: logIndex, htlcCounter: htlcCounter, modifiedHtlcs: fn.NewSet[uint64](), @@ -64,7 +64,7 @@ func newUpdateLog(logIndex, htlcCounter uint64) *updateLog { // state. This function differs from appendHtlc in that it won't increment // either of log's counters. If the HTLC is already present, then it is // ignored. -func (u *updateLog) restoreHtlc(pd *PaymentDescriptor) { +func (u *updateLog) restoreHtlc(pd *paymentDescriptor) { if _, ok := u.htlcIndex[pd.HtlcIndex]; ok { return } @@ -74,7 +74,7 @@ func (u *updateLog) restoreHtlc(pd *PaymentDescriptor) { // appendUpdate appends a new update to the tip of the updateLog. The entry is // also added to index accordingly. -func (u *updateLog) appendUpdate(pd *PaymentDescriptor) { +func (u *updateLog) appendUpdate(pd *paymentDescriptor) { u.updateIndex[u.logIndex] = u.PushBack(pd) u.logIndex++ } @@ -82,13 +82,13 @@ func (u *updateLog) appendUpdate(pd *PaymentDescriptor) { // restoreUpdate appends a new update to the tip of the updateLog. The entry is // also added to index accordingly. This function differs from appendUpdate in // that it won't increment the log index counter. -func (u *updateLog) restoreUpdate(pd *PaymentDescriptor) { +func (u *updateLog) restoreUpdate(pd *paymentDescriptor) { u.updateIndex[pd.LogIndex] = u.PushBack(pd) } // appendHtlc appends a new HTLC offer to the tip of the update log. The entry // is also added to the offer index accordingly. -func (u *updateLog) appendHtlc(pd *PaymentDescriptor) { +func (u *updateLog) appendHtlc(pd *paymentDescriptor) { u.htlcIndex[u.htlcCounter] = u.PushBack(pd) u.htlcCounter++ @@ -97,7 +97,7 @@ func (u *updateLog) appendHtlc(pd *PaymentDescriptor) { // lookupHtlc attempts to look up an offered HTLC according to its offer // index. If the entry isn't found, then a nil pointer is returned. -func (u *updateLog) lookupHtlc(i uint64) *PaymentDescriptor { +func (u *updateLog) lookupHtlc(i uint64) *paymentDescriptor { htlc, ok := u.htlcIndex[i] if !ok { return nil @@ -145,7 +145,7 @@ func compactLogs(ourLog, theirLog *updateLog, localChainTail, remoteChainTail uint64) { compactLog := func(logA, logB *updateLog) { - var nextA *fn.Node[*PaymentDescriptor] + var nextA *fn.Node[*paymentDescriptor] for e := logA.Front(); e != nil; e = nextA { // Assign next iteration element at top of loop because // we may remove the current element from the list, From 56866448dc066149f97cae9cd465e3271884f0a6 Mon Sep 17 00:00:00 2001 From: Keagan McClelland Date: Fri, 14 Jun 2024 17:45:36 -0700 Subject: [PATCH 096/218] lnwallet: remove unnecessary chanID argument form unsignedLocalUpdates --- lnwallet/channel.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/lnwallet/channel.go b/lnwallet/channel.go index 0000026c20..32492171ff 100644 --- a/lnwallet/channel.go +++ b/lnwallet/channel.go @@ -5324,7 +5324,6 @@ func (lc *LightningChannel) ReceiveRevocation(revMsg *lnwire.RevokeAndAck) ( localChainTail := lc.commitChains.Local.tail().height source := lc.ShortChanID() - chanID := lnwire.NewChanIDFromOutPoint(lc.channelState.FundingOutpoint) // Determine the set of htlcs that can be forwarded as a result of // having received the revocation. We will simultaneously construct the @@ -5426,7 +5425,7 @@ func (lc *LightningChannel) ReceiveRevocation(revMsg *lnwire.RevokeAndAck) ( localMessageIndex := lc.commitChains.Local.tail().messageIndices.Local localPeerUpdates := lc.unsignedLocalUpdates( - remoteMessageIndex, localMessageIndex, chanID, + remoteMessageIndex, localMessageIndex, ) // Now that we have gathered the set of HTLCs to forward, separated by @@ -8736,7 +8735,7 @@ func (lc *LightningChannel) FwdMinHtlc() lnwire.MilliSatoshi { // NOTE: remoteMessageIndex is the height on the tip because this is called // before the tail is advanced to the tip during ReceiveRevocation. func (lc *LightningChannel) unsignedLocalUpdates(remoteMessageIndex, - localMessageIndex uint64, chanID lnwire.ChannelID) []channeldb.LogUpdate { + localMessageIndex uint64) []channeldb.LogUpdate { var localPeerUpdates []channeldb.LogUpdate for e := lc.updateLogs.Local.Front(); e != nil; e = e.Next() { From f73f5f5582e08bc38d3e1b077fdf91da3350fb8c Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Thu, 4 Apr 2024 17:25:09 -0700 Subject: [PATCH 097/218] lnwallet: add new AuxFundingDesc struct This struct will house all the information we'll need to do a class of custom channels that relies primarily on adding additional items to the tapscript root of the HTLC/commitment/funding outputs. --- lnwallet/wallet.go | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/lnwallet/wallet.go b/lnwallet/wallet.go index 648c5b9ce4..e0f0f37718 100644 --- a/lnwallet/wallet.go +++ b/lnwallet/wallet.go @@ -32,6 +32,7 @@ import ( "github.com/lightningnetwork/lnd/lnwallet/chanvalidate" "github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/shachain" + "github.com/lightningnetwork/lnd/tlv" ) const ( @@ -90,6 +91,33 @@ func (p *PsbtFundingRequired) Error() string { return ErrPsbtFundingRequired.Error() } +// AuxFundingDesc stores a series of attributes that may be used to modify the +// way the channel funding occurs. This struct contains information that can +// only be derived once both sides have received and sent their contributions +// to the channel (keys, etc.). +type AuxFundingDesc struct { + // CustomFundingBlob is a custom blob that'll be stored in the database + // within the OpenChannel struct. This should represent information + // static to the channel lifetime. + CustomFundingBlob tlv.Blob + + // CustomLocalCommitBlob is a custom blob that'll be stored in the + // first commitment entry for the local party. + CustomLocalCommitBlob tlv.Blob + + // CustomRemoteCommitBlob is a custom blob that'll be stored in the + // first commitment entry for the remote party. + CustomRemoteCommitBlob tlv.Blob + + // LocalInitAuxLeaves is the set of aux leaves that'll be used for our + // very first commitment state. + LocalInitAuxLeaves CommitAuxLeaves + + // RemoteInitAuxLeaves is the set of aux leaves that'll be used for the + // very first commitment state for the remote party. + RemoteInitAuxLeaves CommitAuxLeaves +} + // InitFundingReserveMsg is the first message sent to initiate the workflow // required to open a payment channel with a remote peer. The initial required // parameters are configurable across channels. These parameters are to be From 5f5bbf9ae2c1554c72121e98a1c496360388e61b Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Thu, 4 Apr 2024 17:26:03 -0700 Subject: [PATCH 098/218] lnwallet: use AuxFundingDesc to populate all custom chan info With this commit, we'll now populate all the custom channel information within the OpenChannel and ChannelCommitment structs. --- lnwallet/wallet.go | 57 +++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 51 insertions(+), 6 deletions(-) diff --git a/lnwallet/wallet.go b/lnwallet/wallet.go index e0f0f37718..4271f92372 100644 --- a/lnwallet/wallet.go +++ b/lnwallet/wallet.go @@ -239,9 +239,8 @@ type InitFundingReserveMsg struct { // channel that will be useful to our future selves. Memo []byte - // TapscriptRoot is the root of the tapscript tree that will be used to - // create the funding output. This is an optional field that should - // only be set for taproot channels. + // TapscriptRoot is an optional tapscript root that if provided, will + // be used to create the combined key for musig2 based channels. TapscriptRoot fn.Option[chainhash.Hash] // err is a channel in which all errors will be sent across. Will be @@ -292,6 +291,10 @@ type addContributionMsg struct { type continueContributionMsg struct { pendingFundingID uint64 + // auxFundingDesc is an optional descriptor that contains information + // about the custom channel funding flow. + auxFundingDesc fn.Option[AuxFundingDesc] + // NOTE: In order to avoid deadlocks, this channel MUST be buffered. err chan error } @@ -347,6 +350,10 @@ type addCounterPartySigsMsg struct { type addSingleFunderSigsMsg struct { pendingFundingID uint64 + // auxFundingDesc is an optional descriptor that contains information + // about the custom channel funding flow. + auxFundingDesc fn.Option[AuxFundingDesc] + // fundingOutpoint is the outpoint of the completed funding // transaction as assembled by the workflow initiator. fundingOutpoint *wire.OutPoint @@ -1501,7 +1508,8 @@ func (l *LightningWallet) handleFundingCancelRequest(req *fundingReserveCancelMs // createCommitOpts is a struct that holds the options for creating a new // commitment transaction. type createCommitOpts struct { - auxLeaves fn.Option[CommitAuxLeaves] + localAuxLeaves fn.Option[CommitAuxLeaves] + remoteAuxLeaves fn.Option[CommitAuxLeaves] } // defaultCommitOpts returns a new createCommitOpts with default values. @@ -1509,6 +1517,17 @@ func defaultCommitOpts() createCommitOpts { return createCommitOpts{} } +// WithAuxLeaves is a functional option that can be used to set the aux leaves +// for a new commitment transaction. +func WithAuxLeaves(localLeaves, + remoteLeaves fn.Option[CommitAuxLeaves]) CreateCommitOpt { + + return func(o *createCommitOpts) { + o.localAuxLeaves = localLeaves + o.remoteAuxLeaves = remoteLeaves + } +} + // CreateCommitOpt is a functional option that can be used to modify the way a // new commitment transaction is created. type CreateCommitOpt func(*createCommitOpts) @@ -1542,7 +1561,7 @@ func CreateCommitmentTxns(localBalance, remoteBalance btcutil.Amount, ourCommitTx, err := CreateCommitTx( chanType, fundingTxIn, localCommitmentKeys, ourChanCfg, theirChanCfg, localBalance, remoteBalance, 0, initiator, - leaseExpiry, options.auxLeaves, + leaseExpiry, options.localAuxLeaves, ) if err != nil { return nil, nil, err @@ -1556,7 +1575,7 @@ func CreateCommitmentTxns(localBalance, remoteBalance btcutil.Amount, theirCommitTx, err := CreateCommitTx( chanType, fundingTxIn, remoteCommitmentKeys, theirChanCfg, ourChanCfg, remoteBalance, localBalance, 0, !initiator, - leaseExpiry, options.auxLeaves, + leaseExpiry, options.remoteAuxLeaves, ) if err != nil { return nil, nil, err @@ -1899,6 +1918,18 @@ func (l *LightningWallet) handleChanPointReady(req *continueContributionMsg) { if pendingReservation.partialState.ChanType.HasLeaseExpiration() { leaseExpiry = pendingReservation.partialState.ThawHeight } + + localAuxLeaves := fn.MapOption( + func(desc AuxFundingDesc) CommitAuxLeaves { + return desc.LocalInitAuxLeaves + }, + )(req.auxFundingDesc) + remoteAuxLeaves := fn.MapOption( + func(desc AuxFundingDesc) CommitAuxLeaves { + return desc.RemoteInitAuxLeaves + }, + )(req.auxFundingDesc) + ourCommitTx, theirCommitTx, err := CreateCommitmentTxns( localBalance, remoteBalance, ourContribution.ChannelConfig, theirContribution.ChannelConfig, @@ -1906,6 +1937,7 @@ func (l *LightningWallet) handleChanPointReady(req *continueContributionMsg) { theirContribution.FirstCommitmentPoint, fundingTxIn, pendingReservation.partialState.ChanType, pendingReservation.partialState.IsInitiator, leaseExpiry, + WithAuxLeaves(localAuxLeaves, remoteAuxLeaves), ) if err != nil { req.err <- err @@ -2332,6 +2364,18 @@ func (l *LightningWallet) handleSingleFunderSigs(req *addSingleFunderSigsMsg) { if pendingReservation.partialState.ChanType.HasLeaseExpiration() { leaseExpiry = pendingReservation.partialState.ThawHeight } + + localAuxLeaves := fn.MapOption( + func(desc AuxFundingDesc) CommitAuxLeaves { + return desc.LocalInitAuxLeaves + }, + )(req.auxFundingDesc) + remoteAuxLeaves := fn.MapOption( + func(desc AuxFundingDesc) CommitAuxLeaves { + return desc.RemoteInitAuxLeaves + }, + )(req.auxFundingDesc) + ourCommitTx, theirCommitTx, err := CreateCommitmentTxns( localBalance, remoteBalance, pendingReservation.ourContribution.ChannelConfig, @@ -2340,6 +2384,7 @@ func (l *LightningWallet) handleSingleFunderSigs(req *addSingleFunderSigsMsg) { pendingReservation.theirContribution.FirstCommitmentPoint, *fundingTxIn, chanType, pendingReservation.partialState.IsInitiator, leaseExpiry, + WithAuxLeaves(localAuxLeaves, remoteAuxLeaves), ) if err != nil { req.err <- err From 7f3aff9e10746738debca1746bbb3a2ccb73dbe7 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Thu, 4 Apr 2024 17:27:20 -0700 Subject: [PATCH 099/218] funding: create new AuxFundingController interface In this commit, we make a new `AuxFundingController` interface capable of processing messages off the wire. In addition, we can use it to abstract away details w.r.t how we obtain a `AuxFundingDesc` for a given channel. We'll now use this whenever we get a channel funding request, to make sure we pass along the custom state that a channel may require. --- funding/aux_funding.go | 51 ++++++++++++++++++++++++++++++++++++++++++ funding/manager.go | 42 ++++++++++++++++++++++++++++++++++ 2 files changed, 93 insertions(+) create mode 100644 funding/aux_funding.go diff --git a/funding/aux_funding.go b/funding/aux_funding.go new file mode 100644 index 0000000000..492612145a --- /dev/null +++ b/funding/aux_funding.go @@ -0,0 +1,51 @@ +package funding + +import ( + "github.com/btcsuite/btcd/chaincfg/chainhash" + "github.com/lightningnetwork/lnd/fn" + "github.com/lightningnetwork/lnd/lntypes" + "github.com/lightningnetwork/lnd/lnwallet" + "github.com/lightningnetwork/lnd/msgmux" +) + +// AuxFundingDescResult is a type alias for a function that returns an optional +// aux funding desc. +type AuxFundingDescResult = fn.Result[fn.Option[lnwallet.AuxFundingDesc]] + +// AuxTapscriptResult is a type alias for a function that returns an optional +// tapscript root. +type AuxTapscriptResult = fn.Result[fn.Option[chainhash.Hash]] + +// AuxFundingController permits the implementation of the funding of custom +// channels types. The controller serves as a MsgEndpoint which allows it to +// intercept custom messages, or even the regular funding messages. The +// controller might also pass along an aux funding desc based on an existing +// pending channel ID. +type AuxFundingController interface { + // Endpoint is the embedded interface that signals that the funding + // controller is also a message endpoint. This'll allow it to handle + // custom messages specific to the funding type. + msgmux.Endpoint + + // DescFromPendingChanID takes a pending channel ID, that may already be + // known due to prior custom channel messages, and maybe returns an aux + // funding desc which can be used to modify how a channel is funded. + DescFromPendingChanID(pid PendingChanID, openChan lnwallet.AuxChanState, + keyRing lntypes.Dual[lnwallet.CommitmentKeyRing], + initiator bool) AuxFundingDescResult + + // DeriveTapscriptRoot takes a pending channel ID and maybe returns a + // tapscript root that should be used when creating any MuSig2 sessions + // for a channel. + DeriveTapscriptRoot(PendingChanID) AuxTapscriptResult + + // ChannelReady is called when a channel has been fully opened (multiple + // confirmations) and is ready to be used. This can be used to perform + // any final setup or cleanup. + ChannelReady(openChan lnwallet.AuxChanState) error + + // ChannelFinalized is called when a channel has been fully finalized. + // In this state, we've received the commitment sig from the remote + // party, so we are safe to broadcast the funding transaction. + ChannelFinalized(PendingChanID) error +} diff --git a/funding/manager.go b/funding/manager.go index fb36bd9903..3d3ed4c6fd 100644 --- a/funding/manager.go +++ b/funding/manager.go @@ -549,6 +549,12 @@ type Config struct { // AuxLeafStore is an optional store that can be used to store auxiliary // leaves for certain custom channel types. AuxLeafStore fn.Option[lnwallet.AuxLeafStore] + + // AuxFundingController is an optional controller that can be used to + // modify the way we handle certain custom channel types. It's also + // able to automatically handle new custom protocol messages related to + // the funding process. + AuxFundingController fn.Option[AuxFundingController] } // Manager acts as an orchestrator/bridge between the wallet's @@ -1626,6 +1632,23 @@ func (f *Manager) fundeeProcessOpenChannel(peer lnpeer.Peer, return } + // At this point, if we have an AuxFundingController active, we'll + // check to see if we have a special tapscript root to use in our + // MuSig funding output. + tapscriptRoot, err := fn.MapOptionZ( + f.cfg.AuxFundingController, + func(c AuxFundingController) AuxTapscriptResult { + return c.DeriveTapscriptRoot(msg.PendingChannelID) + }, + ).Unpack() + if err != nil { + err = fmt.Errorf("error deriving tapscript root: %w", err) + log.Error(err) + f.failFundingFlow(peer, cid, err) + + return + } + req := &lnwallet.InitFundingReserveMsg{ ChainHash: &msg.ChainHash, PendingChanID: msg.PendingChannelID, @@ -1642,6 +1665,7 @@ func (f *Manager) fundeeProcessOpenChannel(peer lnpeer.Peer, ZeroConf: zeroConf, OptionScidAlias: scid, ScidAliasFeature: scidFeatureVal, + TapscriptRoot: tapscriptRoot, } reservation, err := f.cfg.Wallet.InitChannelReservation(req) @@ -4634,6 +4658,23 @@ func (f *Manager) handleInitFundingMsg(msg *InitFundingMsg) { scidFeatureVal = true } + // At this point, if we have an AuxFundingController active, we'll check + // to see if we have a special tapscript root to use in our MuSig2 + // funding output. + tapscriptRoot, err := fn.MapOptionZ( + f.cfg.AuxFundingController, + func(c AuxFundingController) AuxTapscriptResult { + return c.DeriveTapscriptRoot(chanID) + }, + ).Unpack() + if err != nil { + err = fmt.Errorf("error deriving tapscript root: %w", err) + log.Error(err) + msg.Err <- err + + return + } + req := &lnwallet.InitFundingReserveMsg{ ChainHash: &msg.ChainHash, PendingChanID: chanID, @@ -4673,6 +4714,7 @@ func (f *Manager) handleInitFundingMsg(msg *InitFundingMsg) { OptionScidAlias: scid, ScidAliasFeature: scidFeatureVal, Memo: msg.Memo, + TapscriptRoot: tapscriptRoot, } reservation, err := f.cfg.Wallet.InitChannelReservation(req) From 723621bd2b57552ba8dd38457cda79b5d3a5bb9a Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Thu, 4 Apr 2024 17:48:11 -0700 Subject: [PATCH 100/218] config+serer: add AuxFundingController as top level cfg option --- config_builder.go | 7 +++++++ server.go | 7 ++++--- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/config_builder.go b/config_builder.go index 21b1ccee0b..d7625839ee 100644 --- a/config_builder.go +++ b/config_builder.go @@ -34,6 +34,7 @@ import ( "github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/clock" "github.com/lightningnetwork/lnd/fn" + "github.com/lightningnetwork/lnd/funding" "github.com/lightningnetwork/lnd/invoices" "github.com/lightningnetwork/lnd/keychain" "github.com/lightningnetwork/lnd/kvdb" @@ -167,6 +168,12 @@ type AuxComponents struct { // MsgRouter is an optional message router that if set will be used in // place of a new blank default message router. MsgRouter fn.Option[msgmux.Router] + + // AuxFundingController is an optional controller that can be used to + // modify the way we handle certain custom channel types. It's also + // able to automatically handle new custom protocol messages related to + // the funding process. + AuxFundingController fn.Option[funding.AuxFundingController] } // DefaultWalletImpl is the default implementation of our normal, btcwallet diff --git a/server.go b/server.go index d7d0d1a6e0..a50b12507b 100644 --- a/server.go +++ b/server.go @@ -1529,9 +1529,10 @@ func newServer(cfg *Config, listenAddrs []net.Addr, EnableUpfrontShutdown: cfg.EnableUpfrontShutdown, MaxAnchorsCommitFeeRate: chainfee.SatPerKVByte( s.cfg.MaxCommitFeeRateAnchors * 1000).FeePerKWeight(), - DeleteAliasEdge: deleteAliasEdge, - AliasManager: s.aliasMgr, - IsSweeperOutpoint: s.sweeper.IsSweeperOutpoint, + DeleteAliasEdge: deleteAliasEdge, + AliasManager: s.aliasMgr, + IsSweeperOutpoint: s.sweeper.IsSweeperOutpoint, + AuxFundingController: implCfg.AuxFundingController, }) if err != nil { return nil, err From 0a7139c9ca1f8ae4d82780ca07dd0f7a93faa821 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Tue, 16 Apr 2024 16:25:22 -0700 Subject: [PATCH 101/218] lnwallet: add TaprootInternalKey method to ShimIntent If this is a taproot channel, then we'll return the internal key which'll be useful to callers. --- lnwallet/chanfunding/canned_assembler.go | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/lnwallet/chanfunding/canned_assembler.go b/lnwallet/chanfunding/canned_assembler.go index 177cc35c38..b3457f21bf 100644 --- a/lnwallet/chanfunding/canned_assembler.go +++ b/lnwallet/chanfunding/canned_assembler.go @@ -4,6 +4,7 @@ import ( "fmt" "github.com/btcsuite/btcd/btcec/v2" + "github.com/btcsuite/btcd/btcec/v2/schnorr/musig2" "github.com/btcsuite/btcd/btcutil" "github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/btcsuite/btcd/wire" @@ -98,6 +99,26 @@ func (s *ShimIntent) FundingOutput() ([]byte, *wire.TxOut, error) { ) } +// TaprootInternalKey may return the internal key for a MuSig2 funding output, +// but only if this is actually a MuSig2 channel. +func (s *ShimIntent) TaprootInternalKey() fn.Option[*btcec.PublicKey] { + if !s.musig2 { + return fn.None[*btcec.PublicKey]() + } + + // Similar to the existing p2wsh script, we'll always ensure the keys + // are sorted before use. Since we're only interested in the internal + // key, we don't need to take into account any tapscript root. + // + // We ignore the error here as this is only called after FundingOutput + // is called. + combinedKey, _, _, _ := musig2.AggregateKeys( + []*btcec.PublicKey{s.localKey.PubKey, s.remoteKey}, true, + ) + + return fn.Some(combinedKey.PreTweakedKey) +} + // Cancel allows the caller to cancel a funding Intent at any time. This will // return any resources such as coins back to the eligible pool to be used in // order channel fundings. From 39b10801669fef0ad83fc7be98125826d31d9c87 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Tue, 16 Apr 2024 16:25:57 -0700 Subject: [PATCH 102/218] lnwallet: for PsbtIntent return the internal key in the POutput We also add a new assertion to the itests to ensure the field is being properly set. --- itest/lnd_psbt_test.go | 11 +++++++++++ lnwallet/chanfunding/psbt_assembler.go | 15 ++++++++++++++- 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/itest/lnd_psbt_test.go b/itest/lnd_psbt_test.go index 91d67ba376..c2dffcaa21 100644 --- a/itest/lnd_psbt_test.go +++ b/itest/lnd_psbt_test.go @@ -177,6 +177,17 @@ func runPsbtChanFunding(ht *lntest.HarnessTest, carol, dave *node.HarnessNode, }, ) + // If this is a taproot channel, then we'll decode the PSBT to assert + // that an internal key is included. + if commitType == lnrpc.CommitmentType_SIMPLE_TAPROOT { + decodedPSBT, err := psbt.NewFromRawBytes( + bytes.NewReader(tempPsbt), false, + ) + require.NoError(ht, err) + + require.Len(ht, decodedPSBT.Outputs[0].TaprootInternalKey, 32) + } + // Let's add a second channel to the batch. This time between Carol and // Alice. We will publish the batch TX once this channel funding is // complete. diff --git a/lnwallet/chanfunding/psbt_assembler.go b/lnwallet/chanfunding/psbt_assembler.go index 10bcd70159..d37f1b7347 100644 --- a/lnwallet/chanfunding/psbt_assembler.go +++ b/lnwallet/chanfunding/psbt_assembler.go @@ -6,11 +6,13 @@ import ( "sync" "github.com/btcsuite/btcd/btcec/v2" + "github.com/btcsuite/btcd/btcec/v2/schnorr" "github.com/btcsuite/btcd/btcutil" "github.com/btcsuite/btcd/btcutil/psbt" "github.com/btcsuite/btcd/chaincfg" "github.com/btcsuite/btcd/txscript" "github.com/btcsuite/btcd/wire" + "github.com/lightningnetwork/lnd/fn" "github.com/lightningnetwork/lnd/input" "github.com/lightningnetwork/lnd/keychain" ) @@ -208,7 +210,18 @@ func (i *PsbtIntent) FundingParams() (btcutil.Address, int64, *psbt.Packet, } } packet.UnsignedTx.TxOut = append(packet.UnsignedTx.TxOut, out) - packet.Outputs = append(packet.Outputs, psbt.POutput{}) + + var pOut psbt.POutput + + // If this is a MuSig2 channel, we also need to communicate the internal + // key to the caller. Otherwise, they cannot verify the construction of + // the P2TR output script. + pOut.TaprootInternalKey = fn.MapOptionZ( + i.TaprootInternalKey(), schnorr.SerializePubKey, + ) + + packet.Outputs = append(packet.Outputs, pOut) + return addr, out.Value, packet, nil } From f2dff16c37e5d52043b9bbb896114f4b0cf64461 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Wed, 17 Apr 2024 18:43:01 -0700 Subject: [PATCH 103/218] funding+lnwallet: only blind tapscript root early in funding flow In this commit, we modify the aux funding work flow slightly. We won't be able to generate the full AuxFundingDesc until both sides has sent+received funding params. So we'll now only attempt to bind the tapscript root as soon as we send+recv the open_channel message. We'll now also make sure that we pass the tapscript root all the way down into the musig2 session creation. --- lnwallet/chanfunding/psbt_assembler.go | 8 ++++++ lnwallet/wallet.go | 40 ++++++++++++-------------- 2 files changed, 27 insertions(+), 21 deletions(-) diff --git a/lnwallet/chanfunding/psbt_assembler.go b/lnwallet/chanfunding/psbt_assembler.go index d37f1b7347..f678f520fc 100644 --- a/lnwallet/chanfunding/psbt_assembler.go +++ b/lnwallet/chanfunding/psbt_assembler.go @@ -10,6 +10,7 @@ import ( "github.com/btcsuite/btcd/btcutil" "github.com/btcsuite/btcd/btcutil/psbt" "github.com/btcsuite/btcd/chaincfg" + "github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/btcsuite/btcd/txscript" "github.com/btcsuite/btcd/wire" "github.com/lightningnetwork/lnd/fn" @@ -164,6 +165,13 @@ func (i *PsbtIntent) BindKeys(localKey *keychain.KeyDescriptor, i.State = PsbtOutputKnown } +// BindTapscriptRoot takes an optional tapscript root and binds it to the +// underlying funding intent. This only applies to musig2 channels, and will be +// used to make the musig2 funding output. +func (i *PsbtIntent) BindTapscriptRoot(root fn.Option[chainhash.Hash]) { + i.tapscriptRoot = root +} + // FundingParams returns the parameters that are necessary to start funding the // channel output this intent was created for. It returns the P2WSH funding // address, the exact funding amount and a PSBT packet that contains exactly one diff --git a/lnwallet/wallet.go b/lnwallet/wallet.go index 4271f92372..2603b44431 100644 --- a/lnwallet/wallet.go +++ b/lnwallet/wallet.go @@ -278,7 +278,6 @@ type fundingReserveCancelMsg struct { type addContributionMsg struct { pendingFundingID uint64 - // TODO(roasbeef): Should also carry SPV proofs in we're in SPV mode contribution *ChannelContribution // NOTE: In order to avoid deadlocks, this channel MUST be buffered. @@ -457,8 +456,6 @@ type LightningWallet struct { quit chan struct{} wg sync.WaitGroup - - // TODO(roasbeef): handle wallet lock/unlock } // NewLightningWallet creates/opens and initializes a LightningWallet instance. @@ -503,7 +500,6 @@ func (l *LightningWallet) Startup() error { } l.wg.Add(1) - // TODO(roasbeef): multiple request handlers? go l.requestHandler() return nil @@ -1459,7 +1455,6 @@ func (l *LightningWallet) initOurContribution(reservation *ChannelReservation, // transaction via coin selection are freed allowing future reservations to // include them. func (l *LightningWallet) handleFundingCancelRequest(req *fundingReserveCancelMsg) { - // TODO(roasbeef): holding lock too long l.limboMtx.Lock() defer l.limboMtx.Unlock() @@ -1484,11 +1479,6 @@ func (l *LightningWallet) handleFundingCancelRequest(req *fundingReserveCancelMs ) } - // TODO(roasbeef): is it even worth it to keep track of unused keys? - - // TODO(roasbeef): Is it possible to mark the unused change also as - // available? - delete(l.fundingLimbo, req.pendingFundingID) pid := pendingReservation.pendingChanID @@ -1668,16 +1658,24 @@ func (l *LightningWallet) handleContributionMsg(req *addContributionMsg) { // and remote key which will be needed to calculate the multisig // funding output in a next step. pendingChanID := pendingReservation.pendingChanID + walletLog.Debugf("Advancing PSBT funding flow for "+ "pending_id(%x), binding keys local_key=%v, "+ "remote_key=%x", pendingChanID, &ourContribution.MultiSigKey, theirContribution.MultiSigKey.PubKey.SerializeCompressed()) + fundingIntent.BindKeys( &ourContribution.MultiSigKey, theirContribution.MultiSigKey.PubKey, ) + // We might have a tapscript root, so we'll bind that now to + // ensure we make the proper funding output. + fundingIntent.BindTapscriptRoot( + pendingReservation.partialState.TapscriptRoot, + ) + // Exit early because we can't continue the funding flow yet. req.err <- &PsbtFundingRequired{ Intent: fundingIntent, @@ -1750,16 +1748,17 @@ func (l *LightningWallet) handleContributionMsg(req *addContributionMsg) { // the commitment transaction for the remote party, and verify their incoming // partial signature. func genMusigSession(ourContribution, theirContribution *ChannelContribution, - signer input.MuSig2Signer, - fundingOutput *wire.TxOut) *MusigPairSession { + signer input.MuSig2Signer, fundingOutput *wire.TxOut, + tapscriptRoot fn.Option[chainhash.Hash]) *MusigPairSession { return NewMusigPairSession(&MusigSessionCfg{ - LocalKey: ourContribution.MultiSigKey, - RemoteKey: theirContribution.MultiSigKey, - LocalNonce: *ourContribution.LocalNonce, - RemoteNonce: *theirContribution.LocalNonce, - Signer: signer, - InputTxOut: fundingOutput, + LocalKey: ourContribution.MultiSigKey, + RemoteKey: theirContribution.MultiSigKey, + LocalNonce: *ourContribution.LocalNonce, + RemoteNonce: *theirContribution.LocalNonce, + Signer: signer, + InputTxOut: fundingOutput, + TapscriptTweak: tapscriptRoot, }) } @@ -1809,6 +1808,7 @@ func (l *LightningWallet) signCommitTx(pendingReservation *ChannelReservation, musigSessions := genMusigSession( ourContribution, theirContribution, l.Cfg.Signer, fundingOutput, + pendingReservation.partialState.TapscriptRoot, ) pendingReservation.musigSessions = musigSessions } @@ -2198,6 +2198,7 @@ func (l *LightningWallet) verifyCommitSig(res *ChannelReservation, res.musigSessions = genMusigSession( res.ourContribution, res.theirContribution, l.Cfg.Signer, fundingOutput, + res.partialState.TapscriptRoot, ) } @@ -2288,9 +2289,6 @@ func (l *LightningWallet) handleFundingCounterPartySigs(msg *addCounterPartySigs // As we're about to broadcast the funding transaction, we'll take note // of the current height for record keeping purposes. - // - // TODO(roasbeef): this info can also be piped into light client's - // basic fee estimation? _, bestHeight, err := l.Cfg.ChainIO.GetBestBlock() if err != nil { msg.err <- err From 1402f087ab65cfc6aa8a46ab18323bd04f6eed78 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Wed, 17 Apr 2024 23:35:44 -0700 Subject: [PATCH 104/218] funding+lnwallet: finish hook up new aux funding flow For the initiator, once we get the signal that the PSBT has been finalized, we'll call into the aux funder to get the funding desc. For the responder, once we receive the funding_created message, we'll do the same. We now also have local+remote aux leaves for the commitment transaction. Some old TODO comments that in retrospect aren't required anymore are removed as well. --- funding/manager.go | 64 +++++++++++++++++----- lnwallet/reservation.go | 96 ++++++++++++++++++++++++++++++--- lnwallet/test/test_interface.go | 2 + lnwallet/wallet.go | 38 ++++++++++++- 4 files changed, 181 insertions(+), 19 deletions(-) diff --git a/funding/manager.go b/funding/manager.go index 3d3ed4c6fd..cb1ec04b93 100644 --- a/funding/manager.go +++ b/funding/manager.go @@ -99,7 +99,6 @@ const ( // you and limitless channel size (apart from 21 million cap). MaxBtcFundingAmountWumbo = btcutil.Amount(1000000000) - // TODO(roasbeef): tune. msgBufferSize = 50 // MaxWaitNumBlocksFundingConf is the maximum number of blocks to wait @@ -1262,8 +1261,8 @@ func (f *Manager) stateStep(channel *channeldb.OpenChannel, // advancePendingChannelState waits for a pending channel's funding tx to // confirm, and marks it open in the database when that happens. -func (f *Manager) advancePendingChannelState( - channel *channeldb.OpenChannel, pendingChanID PendingChanID) error { +func (f *Manager) advancePendingChannelState(channel *channeldb.OpenChannel, + pendingChanID PendingChanID) error { if channel.IsZeroConf() { // Persist the alias to the alias database. @@ -2285,10 +2284,34 @@ func (f *Manager) waitForPsbt(intent *chanfunding.PsbtIntent, return } + // At this point, we'll see if there's an AuxFundingDesc we + // need to deliver so the funding process can continue + // properly. + auxFundingDesc, err := fn.MapOptionZ( + f.cfg.AuxFundingController, + func(c AuxFundingController) AuxFundingDescResult { + return c.DescFromPendingChanID( + cid.tempChanID, + lnwallet.NewAuxChanState( + resCtx.reservation.ChanState(), + ), + resCtx.reservation.CommitmentKeyRings(), + true, + ) + }, + ).Unpack() + if err != nil { + failFlow("error continuing PSBT flow", err) + return + } + // A non-nil error means we can continue the funding flow. // Notify the wallet so it can prepare everything we need to // continue. - err = resCtx.reservation.ProcessPsbt() + // + // We'll also pass along the aux funding controller as well, + // which may be used to help process the finalized PSBT. + err = resCtx.reservation.ProcessPsbt(auxFundingDesc) if err != nil { failFlow("error continuing PSBT flow", err) return @@ -2414,7 +2437,6 @@ func (f *Manager) fundeeProcessFundingCreated(peer lnpeer.Peer, // final funding transaction, as well as a signature for our version of // the commitment transaction. So at this point, we can validate the // initiator's commitment transaction, then send our own if it's valid. - // TODO(roasbeef): make case (p vs P) consistent throughout fundingOut := msg.FundingPoint log.Infof("completing pending_id(%x) with ChannelPoint(%v)", pendingChanID[:], fundingOut) @@ -2446,16 +2468,38 @@ func (f *Manager) fundeeProcessFundingCreated(peer lnpeer.Peer, } } + // At this point, we'll see if there's an AuxFundingDesc we need to + // deliver so the funding process can continue properly. + auxFundingDesc, err := fn.MapOptionZ( + f.cfg.AuxFundingController, + func(c AuxFundingController) AuxFundingDescResult { + return c.DescFromPendingChanID( + cid.tempChanID, lnwallet.NewAuxChanState( + resCtx.reservation.ChanState(), + ), resCtx.reservation.CommitmentKeyRings(), + true, + ) + }, + ).Unpack() + if err != nil { + log.Errorf("error continuing PSBT flow: %v", err) + f.failFundingFlow(peer, cid, err) + return + } + // With all the necessary data available, attempt to advance the // funding workflow to the next stage. If this succeeds then the // funding transaction will broadcast after our next message. // CompleteReservationSingle will also mark the channel as 'IsPending' // in the database. + // + // We'll also directly pass in the AuxFunding controller as well, + // which may be used by the reservation system to finalize funding our + // side. completeChan, err := resCtx.reservation.CompleteReservationSingle( - &fundingOut, commitSig, + &fundingOut, commitSig, auxFundingDesc, ) if err != nil { - // TODO(roasbeef): better error logging: peerID, channelID, etc. log.Errorf("unable to complete single reservation: %v", err) f.failFundingFlow(peer, cid, err) return @@ -2766,9 +2810,6 @@ func (f *Manager) funderProcessFundingSigned(peer lnpeer.Peer, // Send an update to the upstream client that the negotiation process // is over. - // - // TODO(roasbeef): add abstraction over updates to accommodate - // long-polling, or SSE, etc. upd := &lnrpc.OpenStatusUpdate{ Update: &lnrpc.OpenStatusUpdate_ChanPending{ ChanPending: &lnrpc.PendingUpdate{ @@ -3670,7 +3711,7 @@ func (f *Manager) annAfterSixConfs(completeChan *channeldb.OpenChannel, // waitForZeroConfChannel is called when the state is addedToGraph with // a zero-conf channel. This will wait for the real confirmation, add the -// confirmed SCID to the graph, and then announce after six confs. +// confirmed SCID to the router graph, and then announce after six confs. func (f *Manager) waitForZeroConfChannel(c *channeldb.OpenChannel) error { // First we'll check whether the channel is confirmed on-chain. If it // is already confirmed, the chainntnfs subsystem will return with the @@ -4468,7 +4509,6 @@ func (f *Manager) announceChannel(localIDKey, remoteIDKey *btcec.PublicKey, // InitFundingWorkflow sends a message to the funding manager instructing it // to initiate a single funder workflow with the source peer. -// TODO(roasbeef): re-visit blocking nature.. func (f *Manager) InitFundingWorkflow(msg *InitFundingMsg) { f.fundingRequests <- msg } diff --git a/lnwallet/reservation.go b/lnwallet/reservation.go index 1f0000e8e4..7f1a89c228 100644 --- a/lnwallet/reservation.go +++ b/lnwallet/reservation.go @@ -11,6 +11,7 @@ import ( "github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/btcsuite/btcd/wire" "github.com/lightningnetwork/lnd/channeldb" + "github.com/lightningnetwork/lnd/fn" "github.com/lightningnetwork/lnd/input" "github.com/lightningnetwork/lnd/keychain" "github.com/lightningnetwork/lnd/lntypes" @@ -25,7 +26,7 @@ type CommitmentType int const ( // CommitmentTypeLegacy is the legacy commitment format with a tweaked // to_remote key. - CommitmentTypeLegacy = iota + CommitmentTypeLegacy CommitmentType = iota // CommitmentTypeTweakless is a newer commitment format where the // to_remote key is static. @@ -100,6 +101,28 @@ func (c CommitmentType) String() string { } } +// ReservationState is a type that represents the current state of a channel +// reservation within the funding workflow. +type ReservationState int + +const ( + // WaitingToSend is the state either the funder/fundee is in after + // creating a reservation, but hasn't sent a message yet. + WaitingToSend ReservationState = iota + + // SentOpenChannel is the state the funder is in after sending the + // OpenChannel message. + SentOpenChannel + + // SentAcceptChannel is the state the fundee is in after sending the + // AcceptChannel message. + SentAcceptChannel + + // SentFundingCreated is the state the funder is in after sending the + // FundingCreated message. + SentFundingCreated +) + // ChannelContribution is the primary constituent of the funding workflow // within lnwallet. Each side first exchanges their respective contributions // along with channel specific parameters like the min fee/KB. Once @@ -223,6 +246,8 @@ type ChannelReservation struct { nextRevocationKeyLoc keychain.KeyLocator musigSessions *MusigPairSession + + state ReservationState } // NewChannelReservation creates a new channel reservation. This function is @@ -459,6 +484,7 @@ func NewChannelReservation(capacity, localFundingAmt btcutil.Amount, reservationID: id, wallet: wallet, chanFunder: req.ChanFunder, + state: WaitingToSend, }, nil } @@ -470,6 +496,22 @@ func (r *ChannelReservation) AddAlias(scid lnwire.ShortChannelID) { r.partialState.ShortChannelID = scid } +// SetState sets the ReservationState. +func (r *ChannelReservation) SetState(state ReservationState) { + r.Lock() + defer r.Unlock() + + r.state = state +} + +// State returns the current ReservationState. +func (r *ChannelReservation) State() ReservationState { + r.RLock() + defer r.RUnlock() + + return r.state +} + // SetNumConfsRequired sets the number of confirmations that are required for // the ultimate funding transaction before the channel can be considered open. // This is distinct from the main reservation workflow as it allows @@ -610,12 +652,15 @@ func (r *ChannelReservation) IsCannedShim() bool { } // ProcessPsbt continues a previously paused funding flow that involves PSBT to -// construct the funding transaction. This method can be called once the PSBT is -// finalized and the signed transaction is available. -func (r *ChannelReservation) ProcessPsbt() error { +// construct the funding transaction. This method can be called once the PSBT +// is finalized and the signed transaction is available. +func (r *ChannelReservation) ProcessPsbt( + auxFundingDesc fn.Option[AuxFundingDesc]) error { + errChan := make(chan error, 1) r.wallet.msgChan <- &continueContributionMsg{ + auxFundingDesc: auxFundingDesc, pendingFundingID: r.reservationID, err: errChan, } @@ -717,8 +762,10 @@ func (r *ChannelReservation) CompleteReservation(fundingInputScripts []*input.Sc // available via the .OurSignatures() method. As this method should only be // called as a response to a single funder channel, only a commitment signature // will be populated. -func (r *ChannelReservation) CompleteReservationSingle(fundingPoint *wire.OutPoint, - commitSig input.Signature) (*channeldb.OpenChannel, error) { +func (r *ChannelReservation) CompleteReservationSingle( + fundingPoint *wire.OutPoint, commitSig input.Signature, + auxFundingDesc fn.Option[AuxFundingDesc]) (*channeldb.OpenChannel, + error) { errChan := make(chan error, 1) completeChan := make(chan *channeldb.OpenChannel, 1) @@ -728,6 +775,7 @@ func (r *ChannelReservation) CompleteReservationSingle(fundingPoint *wire.OutPoi fundingOutpoint: fundingPoint, theirCommitmentSig: commitSig, completeChan: completeChan, + auxFundingDesc: auxFundingDesc, err: errChan, } @@ -813,6 +861,42 @@ func (r *ChannelReservation) Cancel() error { return <-errChan } +// ChanState the current open channel state. +func (r *ChannelReservation) ChanState() *channeldb.OpenChannel { + r.RLock() + defer r.RUnlock() + + return r.partialState +} + +// CommitmentKeyRings returns the local+remote key ring used for the very first +// commitment transaction both parties. +// +//nolint:lll +func (r *ChannelReservation) CommitmentKeyRings() lntypes.Dual[CommitmentKeyRing] { + r.RLock() + defer r.RUnlock() + + chanType := r.partialState.ChanType + ourChanCfg := r.ourContribution.ChannelConfig + theirChanCfg := r.theirContribution.ChannelConfig + + localKeys := DeriveCommitmentKeys( + r.ourContribution.FirstCommitmentPoint, lntypes.Local, chanType, + ourChanCfg, theirChanCfg, + ) + + remoteKeys := DeriveCommitmentKeys( + r.theirContribution.FirstCommitmentPoint, lntypes.Remote, + chanType, ourChanCfg, theirChanCfg, + ) + + return lntypes.Dual[CommitmentKeyRing]{ + Local: *localKeys, + Remote: *remoteKeys, + } +} + // VerifyConstraints is a helper function that can be used to check the sanity // of various channel constraints. func VerifyConstraints(bounds *channeldb.ChannelStateBounds, diff --git a/lnwallet/test/test_interface.go b/lnwallet/test/test_interface.go index bcb396cafa..4a02f0324a 100644 --- a/lnwallet/test/test_interface.go +++ b/lnwallet/test/test_interface.go @@ -34,6 +34,7 @@ import ( "github.com/lightningnetwork/lnd/chainntnfs" "github.com/lightningnetwork/lnd/chainntnfs/btcdnotify" "github.com/lightningnetwork/lnd/channeldb" + "github.com/lightningnetwork/lnd/fn" "github.com/lightningnetwork/lnd/input" "github.com/lightningnetwork/lnd/keychain" "github.com/lightningnetwork/lnd/kvdb" @@ -940,6 +941,7 @@ func testSingleFunderReservationWorkflow(miner *rpctest.Harness, fundingPoint := aliceChanReservation.FundingOutpoint() _, err = bobChanReservation.CompleteReservationSingle( fundingPoint, aliceCommitSig, + fn.None[lnwallet.AuxFundingDesc](), ) require.NoError(t, err, "bob unable to consume single reservation") diff --git a/lnwallet/wallet.go b/lnwallet/wallet.go index 2603b44431..8a1a8fb9b6 100644 --- a/lnwallet/wallet.go +++ b/lnwallet/wallet.go @@ -1844,6 +1844,26 @@ func (l *LightningWallet) handleChanPointReady(req *continueContributionMsg) { return } + chanState := pendingReservation.partialState + + // If we have an aux funding desc, then we can use it to populate some + // of the optional, but opaque TLV blobs we'll carry for the channel. + chanState.CustomBlob = fn.MapOption(func(desc AuxFundingDesc) tlv.Blob { + return desc.CustomFundingBlob + })(req.auxFundingDesc) + + chanState.LocalCommitment.CustomBlob = fn.MapOption( + func(desc AuxFundingDesc) tlv.Blob { + return desc.CustomLocalCommitBlob + }, + )(req.auxFundingDesc) + + chanState.RemoteCommitment.CustomBlob = fn.MapOption( + func(desc AuxFundingDesc) tlv.Blob { + return desc.CustomRemoteCommitBlob + }, + )(req.auxFundingDesc) + ourContribution := pendingReservation.ourContribution theirContribution := pendingReservation.theirContribution chanPoint := pendingReservation.partialState.FundingOutpoint @@ -1902,7 +1922,6 @@ func (l *LightningWallet) handleChanPointReady(req *continueContributionMsg) { // Store their current commitment point. We'll need this after the // first state transition in order to verify the authenticity of the // revocation. - chanState := pendingReservation.partialState chanState.RemoteCurrentRevocation = theirContribution.FirstCommitmentPoint // Create the txin to our commitment transaction; required to construct @@ -2349,6 +2368,23 @@ func (l *LightningWallet) handleSingleFunderSigs(req *addSingleFunderSigsMsg) { defer pendingReservation.Unlock() chanState := pendingReservation.partialState + + // If we have an aux funding desc, then we can use it to populate some + // of the optional, but opaque TLV blobs we'll carry for the channel. + chanState.CustomBlob = fn.MapOption(func(desc AuxFundingDesc) tlv.Blob { + return desc.CustomFundingBlob + })(req.auxFundingDesc) + chanState.LocalCommitment.CustomBlob = fn.MapOption( + func(desc AuxFundingDesc) tlv.Blob { + return desc.CustomLocalCommitBlob + }, + )(req.auxFundingDesc) + chanState.RemoteCommitment.CustomBlob = fn.MapOption( + func(desc AuxFundingDesc) tlv.Blob { + return desc.CustomRemoteCommitBlob + }, + )(req.auxFundingDesc) + chanType := pendingReservation.partialState.ChanType chanState.FundingOutpoint = *req.fundingOutpoint fundingTxIn := wire.NewTxIn(req.fundingOutpoint, nil, nil) From 18dca6f4c88df0e86e8f759c8c3a0a9f7e5c6cb9 Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Fri, 17 May 2024 12:39:12 +0200 Subject: [PATCH 105/218] multi: add tapscript root to gossip message --- channeldb/models/channel_edge_info.go | 6 ++++++ discovery/gossiper.go | 19 ++++++++++++++++--- funding/manager.go | 7 ++++--- graph/builder.go | 8 ++++---- 4 files changed, 30 insertions(+), 10 deletions(-) diff --git a/channeldb/models/channel_edge_info.go b/channeldb/models/channel_edge_info.go index 1afa2d6272..0f91e2bbec 100644 --- a/channeldb/models/channel_edge_info.go +++ b/channeldb/models/channel_edge_info.go @@ -8,6 +8,7 @@ import ( "github.com/btcsuite/btcd/btcutil" "github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/btcsuite/btcd/wire" + "github.com/lightningnetwork/lnd/fn" ) // ChannelEdgeInfo represents a fully authenticated channel along with all its @@ -62,6 +63,11 @@ type ChannelEdgeInfo struct { // the value output in the outpoint that created this channel. Capacity btcutil.Amount + // TapscriptRoot is the optional Merkle root of the tapscript tree if + // this channel is a taproot channel that also commits to a tapscript + // tree (custom channel). + TapscriptRoot fn.Option[chainhash.Hash] + // ExtraOpaqueData is the set of data that was appended to this // message, some of which we may not actually know how to iterate or // parse. By holding onto this data, we ensure that we're able to diff --git a/discovery/gossiper.go b/discovery/gossiper.go index 84fae767f0..1d3051de57 100644 --- a/discovery/gossiper.go +++ b/discovery/gossiper.go @@ -20,6 +20,7 @@ import ( "github.com/lightningnetwork/lnd/chainntnfs" "github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/channeldb/models" + "github.com/lightningnetwork/lnd/fn" "github.com/lightningnetwork/lnd/graph" "github.com/lightningnetwork/lnd/keychain" "github.com/lightningnetwork/lnd/kvdb" @@ -82,9 +83,10 @@ var ( // can provide that serve useful when processing a specific network // announcement. type optionalMsgFields struct { - capacity *btcutil.Amount - channelPoint *wire.OutPoint - remoteAlias *lnwire.ShortChannelID + capacity *btcutil.Amount + channelPoint *wire.OutPoint + remoteAlias *lnwire.ShortChannelID + tapscriptRoot fn.Option[chainhash.Hash] } // apply applies the optional fields within the functional options. @@ -115,6 +117,14 @@ func ChannelPoint(op wire.OutPoint) OptionalMsgField { } } +// TapscriptRoot is an optional field that lets the gossiper know of the root of +// the tapscript tree for a custom channel. +func TapscriptRoot(root fn.Option[chainhash.Hash]) OptionalMsgField { + return func(f *optionalMsgFields) { + f.tapscriptRoot = root + } +} + // RemoteAlias is an optional field that lets the gossiper know that a locally // sent channel update is actually an update for the peer that should replace // the ShortChannelID field with the remote's alias. This is only used for @@ -2578,6 +2588,9 @@ func (d *AuthenticatedGossiper) handleChanAnnouncement(nMsg *networkMsg, cp := *nMsg.optionalMsgFields.channelPoint edge.ChannelPoint = cp } + + // Optional tapscript root for custom channels. + edge.TapscriptRoot = nMsg.optionalMsgFields.tapscriptRoot } log.Debugf("Adding edge for short_chan_id: %v", scid.ToUint64()) diff --git a/funding/manager.go b/funding/manager.go index cb1ec04b93..7c8309e939 100644 --- a/funding/manager.go +++ b/funding/manager.go @@ -3515,6 +3515,7 @@ func (f *Manager) addToGraph(completeChan *channeldb.OpenChannel, errChan := f.cfg.SendAnnouncement( ann.chanAnn, discovery.ChannelCapacity(completeChan.Capacity), discovery.ChannelPoint(completeChan.FundingOutpoint), + discovery.TapscriptRoot(completeChan.TapscriptRoot), ) select { case err := <-errChan: @@ -4441,9 +4442,9 @@ func (f *Manager) announceChannel(localIDKey, remoteIDKey *btcec.PublicKey, // // We can pass in zeroes for the min and max htlc policy, because we // only use the channel announcement message from the returned struct. - ann, err := f.newChanAnnouncement(localIDKey, remoteIDKey, - localFundingKey, remoteFundingKey, shortChanID, chanID, - 0, 0, nil, chanType, + ann, err := f.newChanAnnouncement( + localIDKey, remoteIDKey, localFundingKey, remoteFundingKey, + shortChanID, chanID, 0, 0, nil, chanType, ) if err != nil { log.Errorf("can't generate channel announcement: %v", err) diff --git a/graph/builder.go b/graph/builder.go index 717d3e5ad8..3c882058bf 100644 --- a/graph/builder.go +++ b/graph/builder.go @@ -1092,8 +1092,8 @@ func (b *Builder) addZombieEdge(chanID uint64) error { // segwit v1 (taproot) channels. // // TODO(roasbeef: export and use elsewhere? -func makeFundingScript(bitcoinKey1, bitcoinKey2 []byte, - chanFeatures []byte) ([]byte, error) { +func makeFundingScript(bitcoinKey1, bitcoinKey2 []byte, chanFeatures []byte, + tapscriptRoot fn.Option[chainhash.Hash]) ([]byte, error) { legacyFundingScript := func() ([]byte, error) { witnessScript, err := input.GenMultiSigScript( @@ -1140,7 +1140,7 @@ func makeFundingScript(bitcoinKey1, bitcoinKey2 []byte, } fundingScript, _, err := input.GenTaprootFundingScript( - pubKey1, pubKey2, 0, fn.None[chainhash.Hash](), + pubKey1, pubKey2, 0, tapscriptRoot, ) if err != nil { return nil, err @@ -1272,7 +1272,7 @@ func (b *Builder) processUpdate(msg interface{}, // reality. fundingPkScript, err := makeFundingScript( msg.BitcoinKey1Bytes[:], msg.BitcoinKey2Bytes[:], - msg.Features, + msg.Features, msg.TapscriptRoot, ) if err != nil { return err From dbb87b4ef91de41e33a10292a1bd9da75b4bc131 Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Fri, 17 May 2024 12:40:22 +0200 Subject: [PATCH 106/218] funding: inform aux controller about channel ready/finalize --- funding/manager.go | 70 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/funding/manager.go b/funding/manager.go index 7c8309e939..2ae0bbed01 100644 --- a/funding/manager.go +++ b/funding/manager.go @@ -1921,6 +1921,8 @@ func (f *Manager) fundeeProcessOpenChannel(peer lnpeer.Peer, log.Debugf("Remote party accepted commitment rendering params: %v", lnutils.SpewLogClosure(params)) + reservation.SetState(lnwallet.SentAcceptChannel) + // With the initiator's contribution recorded, respond with our // contribution in the next message of the workflow. fundingAccept := lnwire.AcceptChannel{ @@ -1981,6 +1983,10 @@ func (f *Manager) funderProcessAcceptChannel(peer lnpeer.Peer, // Update the timestamp once the fundingAcceptMsg has been handled. defer resCtx.updateTimestamp() + if resCtx.reservation.State() != lnwallet.SentOpenChannel { + return + } + log.Infof("Recv'd fundingResponse for pending_id(%x)", pendingChanID[:]) @@ -2406,6 +2412,8 @@ func (f *Manager) continueFundingAccept(resCtx *reservationWithCtx, } } + resCtx.reservation.SetState(lnwallet.SentFundingCreated) + if err := resCtx.peer.SendMessage(true, fundingCreated); err != nil { log.Errorf("Unable to send funding complete message: %v", err) f.failFundingFlow(resCtx.peer, cid, err) @@ -2441,6 +2449,10 @@ func (f *Manager) fundeeProcessFundingCreated(peer lnpeer.Peer, log.Infof("completing pending_id(%x) with ChannelPoint(%v)", pendingChanID[:], fundingOut) + if resCtx.reservation.State() != lnwallet.SentAcceptChannel { + return + } + // Create the channel identifier without setting the active channel ID. cid := newChanIdentifier(pendingChanID) @@ -2700,6 +2712,14 @@ func (f *Manager) funderProcessFundingSigned(peer lnpeer.Peer, return } + if resCtx.reservation.State() != lnwallet.SentFundingCreated { + err := fmt.Errorf("unable to find reservation for chan_id=%x", + msg.ChanID) + f.failFundingFlow(peer, cid, err) + + return + } + // Create an entry in the local discovery map so we can ensure that we // process the channel confirmation fully before we receive a // channel_ready message. @@ -2795,6 +2815,21 @@ func (f *Manager) funderProcessFundingSigned(peer lnpeer.Peer, } } + // Before we proceed, if we have a funding hook that wants a + // notification that it's safe to broadcast the funding transaction, + // then we'll send that now. + err = fn.MapOptionZ( + f.cfg.AuxFundingController, + func(controller AuxFundingController) error { + return controller.ChannelFinalized(cid.tempChanID) + }, + ) + if err != nil { + log.Errorf("Failed to inform aux funding controller about "+ + "ChannelPoint(%v) being finalized: %v", fundingPoint, + err) + } + // Now that we have a finalized reservation for this funding flow, // we'll send the to be active channel to the ChainArbitrator so it can // watch for any on-chain actions before the channel has fully @@ -4043,6 +4078,26 @@ func (f *Manager) handleChannelReady(peer lnpeer.Peer, //nolint:funlen PubNonce: remoteNonce, }), ) + + // Inform the aux funding controller that the liquidity in the + // custom channel is now ready to be advertised. We potentially + // haven't sent our own channel ready message yet, but other + // than that the channel is ready to count toward available + // liquidity. + err = fn.MapOptionZ( + f.cfg.AuxFundingController, + func(controller AuxFundingController) error { + return controller.ChannelReady( + lnwallet.NewAuxChanState(channel), + ) + }, + ) + if err != nil { + cid := newChanIdentifier(msg.ChanID) + f.sendWarning(peer, cid, err) + + return + } } // The channel_ready message contains the next commitment point we'll @@ -4129,6 +4184,19 @@ func (f *Manager) handleChannelReadyReceived(channel *channeldb.OpenChannel, log.Debugf("Channel(%v) with ShortChanID %v: successfully "+ "added to graph", chanID, scid) + err = fn.MapOptionZ( + f.cfg.AuxFundingController, + func(controller AuxFundingController) error { + return controller.ChannelReady( + lnwallet.NewAuxChanState(channel), + ) + }, + ) + if err != nil { + return fmt.Errorf("failed notifying aux funding controller "+ + "about channel ready: %w", err) + } + // Give the caller a final update notifying them that the channel is fundingPoint := channel.FundingOutpoint cp := &lnrpc.ChannelPoint{ @@ -4907,6 +4975,8 @@ func (f *Manager) handleInitFundingMsg(msg *InitFundingMsg) { log.Infof("Starting funding workflow with %v for pending_id(%x), "+ "committype=%v", msg.Peer.Address(), chanID, commitType) + reservation.SetState(lnwallet.SentOpenChannel) + fundingOpen := lnwire.OpenChannel{ ChainHash: *f.cfg.Wallet.Cfg.NetParams.GenesisHash, PendingChannelID: chanID, From 6dda51d0260704503bd6d2d659f6603f83c4a524 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Mon, 8 Apr 2024 19:47:05 -0700 Subject: [PATCH 107/218] lnwallet: add new AuxSigner interface to mirror SigPool In this commit, we add a new aux signer interface that's meant to mirror the SigPool. If present, this'll be used to (maybe) obtain signatures for second level HTLCs for certain classes of custom channels. --- lnwallet/aux_signer.go | 245 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 245 insertions(+) create mode 100644 lnwallet/aux_signer.go diff --git a/lnwallet/aux_signer.go b/lnwallet/aux_signer.go new file mode 100644 index 0000000000..a724f20cbe --- /dev/null +++ b/lnwallet/aux_signer.go @@ -0,0 +1,245 @@ +package lnwallet + +import ( + "github.com/btcsuite/btcd/wire" + "github.com/lightningnetwork/lnd/fn" + "github.com/lightningnetwork/lnd/input" + "github.com/lightningnetwork/lnd/lntypes" + "github.com/lightningnetwork/lnd/lnwire" + "github.com/lightningnetwork/lnd/tlv" +) + +// AuxHtlcDescriptor is a struct that contains the information needed to sign or +// verify an HTLC for custom channels. +type AuxHtlcDescriptor struct { + // ChanID is the ChannelID of the LightningChannel that this + // paymentDescriptor belongs to. We track this here so we can + // reconstruct the Messages that this paymentDescriptor is built from. + ChanID lnwire.ChannelID + + // RHash is the payment hash for this HTLC. The HTLC can be settled iff + // the preimage to this hash is presented. + RHash PaymentHash + + // Timeout is the absolute timeout in blocks, after which this HTLC + // expires. + Timeout uint32 + + // Amount is the HTLC amount in milli-satoshis. + Amount lnwire.MilliSatoshi + + // HtlcIndex is the index within the main update log for this HTLC. + // Entries within the log of type Add will have this field populated, + // as other entries will point to the entry via this counter. + // + // NOTE: This field will only be populated if EntryType is Add. + HtlcIndex uint64 + + // ParentIndex is the HTLC index of the entry that this update settles + // or times out. + // + // NOTE: This field will only be populated if EntryType is Fail or + // Settle. + ParentIndex uint64 + + // EntryType denotes the exact type of the paymentDescriptor. In the + // case of a Timeout, or Settle type, then the Parent field will point + // into the log to the HTLC being modified. + EntryType updateType + + // CustomRecords also stores the set of optional custom records that + // may have been attached to a sent HTLC. + CustomRecords lnwire.CustomRecords + + // addCommitHeight[Remote|Local] encodes the height of the commitment + // which included this HTLC on either the remote or local commitment + // chain. This value is used to determine when an HTLC is fully + // "locked-in". + addCommitHeightRemote uint64 + addCommitHeightLocal uint64 + + // removeCommitHeight[Remote|Local] encodes the height of the + // commitment which removed the parent pointer of this + // paymentDescriptor either due to a timeout or a settle. Once both + // these heights are below the tail of both chains, the log entries can + // safely be removed. + removeCommitHeightRemote uint64 + removeCommitHeightLocal uint64 +} + +// AddHeight returns the height at which the HTLC was added to the commitment +// chain. The height is returned based on the chain the HTLC is being added to +// (local or remote chain). +func (a *AuxHtlcDescriptor) AddHeight( + whoseCommitChain lntypes.ChannelParty) uint64 { + + if whoseCommitChain.IsRemote() { + return a.addCommitHeightRemote + } + + return a.addCommitHeightLocal +} + +// RemoveHeight returns the height at which the HTLC was removed from the +// commitment chain. The height is returned based on the chain the HTLC is being +// removed from (local or remote chain). +func (a *AuxHtlcDescriptor) RemoveHeight( + whoseCommitChain lntypes.ChannelParty) uint64 { + + if whoseCommitChain.IsRemote() { + return a.removeCommitHeightRemote + } + + return a.removeCommitHeightLocal +} + +// newAuxHtlcDescriptor creates a new AuxHtlcDescriptor from a payment +// descriptor. +func newAuxHtlcDescriptor(p *paymentDescriptor) AuxHtlcDescriptor { + return AuxHtlcDescriptor{ + ChanID: p.ChanID, + RHash: p.RHash, + Timeout: p.Timeout, + Amount: p.Amount, + HtlcIndex: p.HtlcIndex, + ParentIndex: p.ParentIndex, + EntryType: p.EntryType, + CustomRecords: p.CustomRecords.Copy(), + addCommitHeightRemote: p.addCommitHeightRemote, + addCommitHeightLocal: p.addCommitHeightLocal, + removeCommitHeightRemote: p.removeCommitHeightRemote, + removeCommitHeightLocal: p.removeCommitHeightLocal, + } +} + +// BaseAuxJob is a struct that contains the common fields that are shared among +// the aux sign/verify jobs. +type BaseAuxJob struct { + // OutputIndex is the output index of the HTLC on the commitment + // transaction being signed. + // + // NOTE: If the output is dust from the PoV of the commitment chain, + // then this value will be -1. + OutputIndex int32 + + // KeyRing is the commitment key ring that contains the keys needed to + // generate the second level HTLC signatures. + KeyRing CommitmentKeyRing + + // HTLC is the HTLC that is being signed or verified. + HTLC AuxHtlcDescriptor + + // Incoming is a boolean that indicates if the HTLC is incoming or + // outgoing. + Incoming bool + + // CommitBlob is the commitment transaction blob that contains the aux + // information for this channel. + CommitBlob fn.Option[tlv.Blob] + + // HtlcLeaf is the aux tap leaf that corresponds to the HTLC being + // signed/verified. + HtlcLeaf input.AuxTapLeaf +} + +// AuxSigJob is a struct that contains all the information needed to sign an +// HTLC for custom channels. +type AuxSigJob struct { + // SignDesc is the sign desc for this HTLC. + SignDesc input.SignDescriptor + + BaseAuxJob + + // Resp is a channel that will be used to send the result of the sign + // job. + Resp chan AuxSigJobResp + + // Cancel is a channel that should be closed if the caller wishes to + // abandon all pending sign jobs part of a single batch. + Cancel chan struct{} +} + +// NewAuxSigJob creates a new AuxSigJob. +func NewAuxSigJob(sigJob SignJob, keyRing CommitmentKeyRing, incoming bool, + htlc AuxHtlcDescriptor, commitBlob fn.Option[tlv.Blob], + htlcLeaf input.AuxTapLeaf, cancelChan chan struct{}) AuxSigJob { + + return AuxSigJob{ + SignDesc: sigJob.SignDesc, + BaseAuxJob: BaseAuxJob{ + OutputIndex: sigJob.OutputIndex, + KeyRing: keyRing, + HTLC: htlc, + Incoming: incoming, + CommitBlob: commitBlob, + HtlcLeaf: htlcLeaf, + }, + Resp: make(chan AuxSigJobResp, 1), + Cancel: cancelChan, + } +} + +// AuxSigJobResp is a struct that contains the result of a sign job. +type AuxSigJobResp struct { + // SigBlob is the signature blob that was generated for the HTLC. This + // is an opaque TLV field that may contain the signature and other data. + SigBlob fn.Option[tlv.Blob] + + // HtlcIndex is the index of the HTLC that was signed. + HtlcIndex uint64 + + // Err is the error that occurred when executing the specified + // signature job. In the case that no error occurred, this value will + // be nil. + Err error +} + +// AuxVerifyJob is a struct that contains all the information needed to verify +// an HTLC for custom channels. +type AuxVerifyJob struct { + // SigBlob is the signature blob that was generated for the HTLC. This + // is an opaque TLV field that may contain the signature and other data. + SigBlob fn.Option[tlv.Blob] + + BaseAuxJob +} + +// NewAuxVerifyJob creates a new AuxVerifyJob. +func NewAuxVerifyJob(sig fn.Option[tlv.Blob], keyRing CommitmentKeyRing, + incoming bool, htlc AuxHtlcDescriptor, commitBlob fn.Option[tlv.Blob], + htlcLeaf input.AuxTapLeaf) AuxVerifyJob { + + return AuxVerifyJob{ + SigBlob: sig, + BaseAuxJob: BaseAuxJob{ + KeyRing: keyRing, + HTLC: htlc, + Incoming: incoming, + CommitBlob: commitBlob, + HtlcLeaf: htlcLeaf, + }, + } +} + +// AuxSigner is an interface that is used to sign and verify HTLCs for custom +// channels. It is similar to the existing SigPool, but uses opaque blobs to +// shuffle around signature information and other metadata. +type AuxSigner interface { + // SubmitSecondLevelSigBatch takes a batch of aux sign jobs and + // processes them asynchronously. + SubmitSecondLevelSigBatch(chanState AuxChanState, commitTx *wire.MsgTx, + sigJob []AuxSigJob) error + + // PackSigs takes a series of aux signatures and packs them into a + // single blob that can be sent alongside the CommitSig messages. + PackSigs([]fn.Option[tlv.Blob]) fn.Result[fn.Option[tlv.Blob]] + + // UnpackSigs takes a packed blob of signatures and returns the + // original signatures for each HTLC, keyed by HTLC index. + UnpackSigs(fn.Option[tlv.Blob]) fn.Result[[]fn.Option[tlv.Blob]] + + // VerifySecondLevelSigs attempts to synchronously verify a batch of aux + // sig jobs. + VerifySecondLevelSigs(chanState AuxChanState, commitTx *wire.MsgTx, + verifyJob []AuxVerifyJob) error +} From 266aba8a6c45c715606eab7922e861ee5e41fe8e Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Fri, 13 Sep 2024 15:47:33 +0200 Subject: [PATCH 108/218] lnwallet: allow read-only access to HtlcView's HTLCs Due to a recent refactor, the HTLCs are no longer an exported type. Custom channels need access to those updates, so we provide them in a read-only manner. --- lnwallet/channel.go | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/lnwallet/channel.go b/lnwallet/channel.go index 32492171ff..a208cda930 100644 --- a/lnwallet/channel.go +++ b/lnwallet/channel.go @@ -2545,6 +2545,18 @@ type HtlcView struct { FeePerKw chainfee.SatPerKWeight } +// AuxOurUpdates returns the outgoing HTLCs as a read-only copy of +// AuxHtlcDescriptors. +func (v *HtlcView) AuxOurUpdates() []AuxHtlcDescriptor { + return fn.Map(newAuxHtlcDescriptor, v.OurUpdates) +} + +// AuxTheirUpdates returns the incoming HTLCs as a read-only copy of +// AuxHtlcDescriptors. +func (v *HtlcView) AuxTheirUpdates() []AuxHtlcDescriptor { + return fn.Map(newAuxHtlcDescriptor, v.TheirUpdates) +} + // fetchHTLCView returns all the candidate HTLC updates which should be // considered for inclusion within a commitment based on the passed HTLC log // indexes. From 6d3f8af0079858e8673d74b199357a3cb0596986 Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Wed, 11 Sep 2024 15:28:46 +0200 Subject: [PATCH 109/218] lnwallet: clarify usage of cancel and response channels --- lnwallet/aux_signer.go | 11 ++++++----- lnwallet/sigpool.go | 21 +++++++++++---------- 2 files changed, 17 insertions(+), 15 deletions(-) diff --git a/lnwallet/aux_signer.go b/lnwallet/aux_signer.go index a724f20cbe..6ce2e99da8 100644 --- a/lnwallet/aux_signer.go +++ b/lnwallet/aux_signer.go @@ -151,18 +151,19 @@ type AuxSigJob struct { BaseAuxJob // Resp is a channel that will be used to send the result of the sign - // job. + // job. This channel MUST be buffered. Resp chan AuxSigJobResp - // Cancel is a channel that should be closed if the caller wishes to - // abandon all pending sign jobs part of a single batch. - Cancel chan struct{} + // Cancel is a channel that is closed by the caller if they wish to + // abandon all pending sign jobs part of a single batch. This should + // never be closed by the validator. + Cancel <-chan struct{} } // NewAuxSigJob creates a new AuxSigJob. func NewAuxSigJob(sigJob SignJob, keyRing CommitmentKeyRing, incoming bool, htlc AuxHtlcDescriptor, commitBlob fn.Option[tlv.Blob], - htlcLeaf input.AuxTapLeaf, cancelChan chan struct{}) AuxSigJob { + htlcLeaf input.AuxTapLeaf, cancelChan <-chan struct{}) AuxSigJob { return AuxSigJob{ SignDesc: sigJob.SignDesc, diff --git a/lnwallet/sigpool.go b/lnwallet/sigpool.go index 2424757f93..2296e17031 100644 --- a/lnwallet/sigpool.go +++ b/lnwallet/sigpool.go @@ -45,17 +45,17 @@ type VerifyJob struct { // party's update log. HtlcIndex uint64 - // Cancel is a channel that should be closed if the caller wishes to + // Cancel is a channel that is closed by the caller if they wish to // cancel all pending verification jobs part of a single batch. This - // channel is to be closed in the case that a single signature in a - // batch has been returned as invalid, as there is no need to verify - // the remainder of the signatures. - Cancel chan struct{} + // channel is closed in the case that a single signature in a batch has + // been returned as invalid, as there is no need to verify the remainder + // of the signatures. + Cancel <-chan struct{} // ErrResp is the channel that the result of the signature verification // is to be sent over. In the see that the signature is valid, a nil // error will be passed. Otherwise, a concrete error detailing the - // issue will be passed. + // issue will be passed. This channel MUST be buffered. ErrResp chan *HtlcIndexErr } @@ -86,12 +86,13 @@ type SignJob struct { // transaction being signed. OutputIndex int32 - // Cancel is a channel that should be closed if the caller wishes to - // abandon all pending sign jobs part of a single batch. - Cancel chan struct{} + // Cancel is a channel that is closed by the caller if they wish to + // abandon all pending sign jobs part of a single batch. This should + // never be closed by the validator. + Cancel <-chan struct{} // Resp is the channel that the response to this particular SignJob - // will be sent over. + // will be sent over. This channel MUST be buffered. // // TODO(roasbeef): actually need to allow caller to set, need to retain // order mark commit sig as special From b83783cc3df45f94ea6d5c6a1266ca5a9b8dca68 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Mon, 8 Apr 2024 19:47:26 -0700 Subject: [PATCH 110/218] lnwallet: add WithAuxSigner option to channel --- lnwallet/channel.go | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/lnwallet/channel.go b/lnwallet/channel.go index a208cda930..3f81000a25 100644 --- a/lnwallet/channel.go +++ b/lnwallet/channel.go @@ -760,6 +760,10 @@ type LightningChannel struct { // signatures, of which there may be hundreds. sigPool *SigPool + // auxSigner is a special signer used to obtain opaque signatures for + // custom channel variants. + auxSigner fn.Option[AuxSigner] + // Capacity is the total capacity of this channel. Capacity btcutil.Amount @@ -821,6 +825,7 @@ type channelOpts struct { remoteNonce *musig2.Nonces leafStore fn.Option[AuxLeafStore] + auxSigner fn.Option[AuxSigner] skipNonceInit bool } @@ -859,6 +864,13 @@ func WithLeafStore(store AuxLeafStore) ChannelOpt { } } +// WithAuxSigner is used to specify a custom aux signer for the channel. +func WithAuxSigner(signer AuxSigner) ChannelOpt { + return func(o *channelOpts) { + o.auxSigner = fn.Some[AuxSigner](signer) + } +} + // defaultChannelOpts returns the set of default options for a new channel. func defaultChannelOpts() *channelOpts { return &channelOpts{} @@ -911,6 +923,7 @@ func NewLightningChannel(signer input.Signer, lc := &LightningChannel{ Signer: signer, leafStore: opts.leafStore, + auxSigner: opts.auxSigner, sigPool: sigPool, currentHeight: localCommit.CommitHeight, commitChains: commitChains, From 4db0d76ca47f5c8de6275c1d6bd9df7ff04308b9 Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Mon, 2 Sep 2024 14:33:49 +0200 Subject: [PATCH 111/218] lnwire: add custom records field to type `CommitSig` --- lnwire/commit_sig.go | 38 +++++---- lnwire/commit_sig_test.go | 168 ++++++++++++++++++++++++++++++++++++++ lnwire/lnwire_test.go | 2 + 3 files changed, 193 insertions(+), 15 deletions(-) create mode 100644 lnwire/commit_sig_test.go diff --git a/lnwire/commit_sig.go b/lnwire/commit_sig.go index 7deb64ae1c..3a475e71ff 100644 --- a/lnwire/commit_sig.go +++ b/lnwire/commit_sig.go @@ -45,6 +45,10 @@ type CommitSig struct { // being signed for. In this case, the above Sig type MUST be blank. PartialSig OptPartialSigWithNonceTLV + // CustomRecords maps TLV types to byte slices, storing arbitrary data + // intended for inclusion in the ExtraData field. + CustomRecords CustomRecords + // ExtraData is the set of data that was appended to this message to // fill out the full maximum transport message size. These fields can // be used to specify optional data such as custom TLV fields. @@ -53,9 +57,7 @@ type CommitSig struct { // NewCommitSig creates a new empty CommitSig message. func NewCommitSig() *CommitSig { - return &CommitSig{ - ExtraData: make([]byte, 0), - } + return &CommitSig{} } // A compile time check to ensure CommitSig implements the lnwire.Message @@ -67,34 +69,37 @@ var _ Message = (*CommitSig)(nil) // // This is part of the lnwire.Message interface. func (c *CommitSig) Decode(r io.Reader, pver uint32) error { + // msgExtraData is a temporary variable used to read the message extra + // data field from the reader. + var msgExtraData ExtraOpaqueData + err := ReadElements(r, &c.ChanID, &c.CommitSig, &c.HtlcSigs, + &msgExtraData, ) if err != nil { return err } - var tlvRecords ExtraOpaqueData - if err := ReadElements(r, &tlvRecords); err != nil { - return err - } - + // Extract TLV records from the extra data field. partialSig := c.PartialSig.Zero() - typeMap, err := tlvRecords.ExtractRecords(&partialSig) + + customRecords, parsed, extraData, err := ParseAndExtractCustomRecords( + msgExtraData, &partialSig, + ) if err != nil { return err } // Set the corresponding TLV types if they were included in the stream. - if val, ok := typeMap[c.PartialSig.TlvType()]; ok && val == nil { + if _, ok := parsed[partialSig.TlvType()]; ok { c.PartialSig = tlv.SomeRecordT(partialSig) } - if len(tlvRecords) != 0 { - c.ExtraData = tlvRecords - } + c.CustomRecords = customRecords + c.ExtraData = extraData return nil } @@ -108,7 +113,10 @@ func (c *CommitSig) Encode(w *bytes.Buffer, pver uint32) error { c.PartialSig.WhenSome(func(sig PartialSigWithNonceTLV) { recordProducers = append(recordProducers, &sig) }) - err := EncodeMessageExtraData(&c.ExtraData, recordProducers...) + + extraData, err := MergeAndEncode( + recordProducers, c.ExtraData, c.CustomRecords, + ) if err != nil { return err } @@ -125,7 +133,7 @@ func (c *CommitSig) Encode(w *bytes.Buffer, pver uint32) error { return err } - return WriteBytes(w, c.ExtraData) + return WriteBytes(w, extraData) } // MsgType returns the integer uniquely identifying this message type on the diff --git a/lnwire/commit_sig_test.go b/lnwire/commit_sig_test.go new file mode 100644 index 0000000000..0772a2fb83 --- /dev/null +++ b/lnwire/commit_sig_test.go @@ -0,0 +1,168 @@ +package lnwire + +import ( + "bytes" + "fmt" + "testing" + + "github.com/btcsuite/btcd/btcec/v2" + "github.com/btcsuite/btcd/btcec/v2/schnorr/musig2" + "github.com/lightningnetwork/lnd/tlv" + "github.com/stretchr/testify/require" +) + +// testCase is a test case for the CommitSig message. +type commitSigTestCase struct { + // Msg is the message to be encoded and decoded. + Msg CommitSig + + // ExpectEncodeError is a flag that indicates whether we expect the + // encoding of the message to fail. + ExpectEncodeError bool +} + +// generateCommitSigTestCases generates a set of CommitSig message test cases. +func generateCommitSigTestCases(t *testing.T) []commitSigTestCase { + // Firstly, we'll set basic values for the message fields. + // + // Generate random channel ID. + chanIDBytes, err := generateRandomBytes(32) + require.NoError(t, err) + + var chanID ChannelID + copy(chanID[:], chanIDBytes) + + // Generate random commit sig. + commitSigBytes, err := generateRandomBytes(64) + require.NoError(t, err) + + sig, err := NewSigFromSchnorrRawSignature(commitSigBytes) + require.NoError(t, err) + + sigScalar := new(btcec.ModNScalar) + sigScalar.SetByteSlice(sig.RawBytes()) + + var nonce [musig2.PubNonceSize]byte + copy(nonce[:], commitSigBytes) + + sigWithNonce := NewPartialSigWithNonce(nonce, *sigScalar) + partialSig := MaybePartialSigWithNonce(sigWithNonce) + + // Define custom records. + recordKey1 := uint64(MinCustomRecordsTlvType + 1) + recordValue1, err := generateRandomBytes(10) + require.NoError(t, err) + + recordKey2 := uint64(MinCustomRecordsTlvType + 2) + recordValue2, err := generateRandomBytes(10) + require.NoError(t, err) + + customRecords := CustomRecords{ + recordKey1: recordValue1, + recordKey2: recordValue2, + } + + // Construct an instance of extra data that contains records with TLV + // types below the minimum custom records threshold and that lack + // corresponding fields in the message struct. Content should persist in + // the extra data field after encoding and decoding. + var ( + recordBytes45 = []byte("recordBytes45") + tlvRecord45 = tlv.NewPrimitiveRecord[tlv.TlvType45]( + recordBytes45, + ) + + recordBytes55 = []byte("recordBytes55") + tlvRecord55 = tlv.NewPrimitiveRecord[tlv.TlvType55]( + recordBytes55, + ) + ) + + var extraData ExtraOpaqueData + err = extraData.PackRecords( + []tlv.RecordProducer{&tlvRecord45, &tlvRecord55}..., + ) + require.NoError(t, err) + + invalidCustomRecords := CustomRecords{ + MinCustomRecordsTlvType - 1: recordValue1, + } + + return []commitSigTestCase{ + { + Msg: CommitSig{ + ChanID: chanID, + CommitSig: sig, + PartialSig: partialSig, + CustomRecords: customRecords, + ExtraData: extraData, + }, + }, + // Add a test case where the blinding point field is not + // populated. + { + Msg: CommitSig{ + ChanID: chanID, + CommitSig: sig, + CustomRecords: customRecords, + }, + }, + // Add a test case where the custom records field is not + // populated. + { + Msg: CommitSig{ + ChanID: chanID, + CommitSig: sig, + PartialSig: partialSig, + }, + }, + // Add a case where the custom records are invalid. + { + Msg: CommitSig{ + ChanID: chanID, + CommitSig: sig, + PartialSig: partialSig, + CustomRecords: invalidCustomRecords, + }, + ExpectEncodeError: true, + }, + } +} + +// TestCommitSigEncodeDecode tests CommitSig message encoding and decoding for +// all supported field values. +func TestCommitSigEncodeDecode(t *testing.T) { + t.Parallel() + + // Generate test cases. + testCases := generateCommitSigTestCases(t) + + // Execute test cases. + for tcIdx, tc := range testCases { + t.Run(fmt.Sprintf("testcase-%d", tcIdx), func(t *testing.T) { + // Encode test case message. + var buf bytes.Buffer + err := tc.Msg.Encode(&buf, 0) + + // Check if we expect an encoding error. + if tc.ExpectEncodeError { + require.Error(t, err) + return + } + + require.NoError(t, err) + + // Decode the encoded message bytes message. + var actualMsg CommitSig + decodeReader := bytes.NewReader(buf.Bytes()) + err = actualMsg.Decode(decodeReader, 0) + require.NoError(t, err) + + // The signature type isn't serialized. + actualMsg.CommitSig.ForceSchnorr() + + // Compare the two messages to ensure equality. + require.Equal(t, tc.Msg, actualMsg) + }) + } +} diff --git a/lnwire/lnwire_test.go b/lnwire/lnwire_test.go index 7eb434f454..e941962d57 100644 --- a/lnwire/lnwire_test.go +++ b/lnwire/lnwire_test.go @@ -945,6 +945,8 @@ func TestLightningWireProtocol(t *testing.T) { } } + req.CustomRecords = randCustomRecords(t, r) + // 50/50 chance to attach a partial sig. if r.Int31()%2 == 0 { req.PartialSig = somePartialSigWithNonce(t, r) From 69155bc60b13a349f6daae92df5777b90b13902e Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Mon, 8 Apr 2024 19:48:36 -0700 Subject: [PATCH 112/218] multi: obtain+verify aux sigs for all second level HTLCs In this commit, we start to use the new AuxSigner to obtain+verify aux sigs for all second level HTLCs. This is similar to the existing SigPool, but we'll only attempt to do this if the AuxSigner is present (won't be for most channels). --- chainreg/chainregistry.go | 4 + config_builder.go | 10 +- contractcourt/breach_arbitrator_test.go | 5 + contractcourt/chain_arbitrator.go | 10 + funding/manager.go | 7 + funding/manager_test.go | 4 + htlcswitch/link.go | 24 ++- htlcswitch/link_test.go | 6 + htlcswitch/test_utils.go | 9 + lnwallet/aux_signer.go | 4 + lnwallet/channel.go | 264 ++++++++++++++++++++---- lnwallet/channel_test.go | 178 +++++++++++++++- lnwallet/config.go | 4 + lnwallet/mock.go | 52 +++++ lnwallet/test_utils.go | 44 ++++ lnwallet/transactions_test.go | 5 + lnwallet/wallet.go | 3 + peer/brontide.go | 10 + peer/test_utils.go | 4 + server.go | 3 + 20 files changed, 597 insertions(+), 53 deletions(-) diff --git a/chainreg/chainregistry.go b/chainreg/chainregistry.go index 41a2fcbb7f..da1f8e08ad 100644 --- a/chainreg/chainregistry.go +++ b/chainreg/chainregistry.go @@ -68,6 +68,10 @@ type Config struct { // leaves for certain custom channel types. AuxLeafStore fn.Option[lnwallet.AuxLeafStore] + // AuxSigner is an optional signer that can be used to sign auxiliary + // leaves for certain custom channel types. + AuxSigner fn.Option[lnwallet.AuxSigner] + // BlockCache is the main cache for storing block information. BlockCache *blockcache.BlockCache diff --git a/config_builder.go b/config_builder.go index d7625839ee..ca32ac4cda 100644 --- a/config_builder.go +++ b/config_builder.go @@ -174,6 +174,10 @@ type AuxComponents struct { // able to automatically handle new custom protocol messages related to // the funding process. AuxFundingController fn.Option[funding.AuxFundingController] + + // AuxSigner is an optional signer that can be used to sign auxiliary + // leaves for certain custom channel types. + AuxSigner fn.Option[lnwallet.AuxSigner] } // DefaultWalletImpl is the default implementation of our normal, btcwallet @@ -580,6 +584,7 @@ func (d *DefaultWalletImpl) BuildWalletConfig(ctx context.Context, ChanStateDB: dbs.ChanStateDB.ChannelStateDB(), NeutrinoCS: neutrinoCS, AuxLeafStore: aux.AuxLeafStore, + AuxSigner: aux.AuxSigner, ActiveNetParams: d.cfg.ActiveNetParams, FeeURL: d.cfg.FeeURL, Fee: &lncfg.Fee{ @@ -737,6 +742,7 @@ func (d *DefaultWalletImpl) BuildChainControl( NetParams: *walletConfig.NetParams, CoinSelectionStrategy: walletConfig.CoinSelectionStrategy, AuxLeafStore: partialChainControl.Cfg.AuxLeafStore, + AuxSigner: partialChainControl.Cfg.AuxSigner, } // The broadcast is already always active for neutrino nodes, so we @@ -919,10 +925,6 @@ type DatabaseInstances struct { // for native SQL queries for tables that already support it. This may // be nil if the use-native-sql flag was not set. NativeSQLStore *sqldb.BaseDB - - // AuxLeafStore is an optional data source that can be used by custom - // channels to fetch+store various data. - AuxLeafStore fn.Option[lnwallet.AuxLeafStore] } // DefaultDatabaseBuilder is a type that builds the default database backends diff --git a/contractcourt/breach_arbitrator_test.go b/contractcourt/breach_arbitrator_test.go index 5940ee25bd..cf575f1534 100644 --- a/contractcourt/breach_arbitrator_test.go +++ b/contractcourt/breach_arbitrator_test.go @@ -2360,9 +2360,12 @@ func createInitChannels(t *testing.T) ( ) bobSigner := input.NewMockSigner([]*btcec.PrivateKey{bobKeyPriv}, nil) + signerMock := lnwallet.NewDefaultAuxSignerMock(t) alicePool := lnwallet.NewSigPool(1, aliceSigner) channelAlice, err := lnwallet.NewLightningChannel( aliceSigner, aliceChannelState, alicePool, + lnwallet.WithLeafStore(&lnwallet.MockAuxLeafStore{}), + lnwallet.WithAuxSigner(signerMock), ) if err != nil { return nil, nil, err @@ -2375,6 +2378,8 @@ func createInitChannels(t *testing.T) ( bobPool := lnwallet.NewSigPool(1, bobSigner) channelBob, err := lnwallet.NewLightningChannel( bobSigner, bobChannelState, bobPool, + lnwallet.WithLeafStore(&lnwallet.MockAuxLeafStore{}), + lnwallet.WithAuxSigner(signerMock), ) if err != nil { return nil, nil, err diff --git a/contractcourt/chain_arbitrator.go b/contractcourt/chain_arbitrator.go index dbc97939a9..d61e479018 100644 --- a/contractcourt/chain_arbitrator.go +++ b/contractcourt/chain_arbitrator.go @@ -221,6 +221,10 @@ type ChainArbitratorConfig struct { // AuxLeafStore is an optional store that can be used to store auxiliary // leaves for certain custom channel types. AuxLeafStore fn.Option[lnwallet.AuxLeafStore] + + // AuxSigner is an optional signer that can be used to sign auxiliary + // leaves for certain custom channel types. + AuxSigner fn.Option[lnwallet.AuxSigner] } // ChainArbitrator is a sub-system that oversees the on-chain resolution of all @@ -307,6 +311,9 @@ func (a *arbChannel) NewAnchorResolutions() (*lnwallet.AnchorResolutions, a.c.cfg.AuxLeafStore.WhenSome(func(s lnwallet.AuxLeafStore) { chanOpts = append(chanOpts, lnwallet.WithLeafStore(s)) }) + a.c.cfg.AuxSigner.WhenSome(func(s lnwallet.AuxSigner) { + chanOpts = append(chanOpts, lnwallet.WithAuxSigner(s)) + }) chanMachine, err := lnwallet.NewLightningChannel( a.c.cfg.Signer, channel, nil, chanOpts..., @@ -357,6 +364,9 @@ func (a *arbChannel) ForceCloseChan() (*lnwallet.LocalForceCloseSummary, error) a.c.cfg.AuxLeafStore.WhenSome(func(s lnwallet.AuxLeafStore) { chanOpts = append(chanOpts, lnwallet.WithLeafStore(s)) }) + a.c.cfg.AuxSigner.WhenSome(func(s lnwallet.AuxSigner) { + chanOpts = append(chanOpts, lnwallet.WithAuxSigner(s)) + }) // Finally, we'll force close the channel completing // the force close workflow. diff --git a/funding/manager.go b/funding/manager.go index 2ae0bbed01..ed0ef0e7fc 100644 --- a/funding/manager.go +++ b/funding/manager.go @@ -554,6 +554,10 @@ type Config struct { // able to automatically handle new custom protocol messages related to // the funding process. AuxFundingController fn.Option[AuxFundingController] + + // AuxSigner is an optional signer that can be used to sign auxiliary + // leaves for certain custom channel types. + AuxSigner fn.Option[lnwallet.AuxSigner] } // Manager acts as an orchestrator/bridge between the wallet's @@ -1083,6 +1087,9 @@ func (f *Manager) advanceFundingState(channel *channeldb.OpenChannel, f.cfg.AuxLeafStore.WhenSome(func(s lnwallet.AuxLeafStore) { chanOpts = append(chanOpts, lnwallet.WithLeafStore(s)) }) + f.cfg.AuxSigner.WhenSome(func(s lnwallet.AuxSigner) { + chanOpts = append(chanOpts, lnwallet.WithAuxSigner(s)) + }) // We create the state-machine object which wraps the database state. lnChannel, err := lnwallet.NewLightningChannel( diff --git a/funding/manager_test.go b/funding/manager_test.go index d1aa9ec146..898d2c3629 100644 --- a/funding/manager_test.go +++ b/funding/manager_test.go @@ -567,6 +567,9 @@ func createTestFundingManager(t *testing.T, privKey *btcec.PrivateKey, AuxLeafStore: fn.Some[lnwallet.AuxLeafStore]( &lnwallet.MockAuxLeafStore{}, ), + AuxSigner: fn.Some[lnwallet.AuxSigner]( + &lnwallet.MockAuxSigner{}, + ), } for _, op := range options { @@ -677,6 +680,7 @@ func recreateAliceFundingManager(t *testing.T, alice *testNode) { DeleteAliasEdge: oldCfg.DeleteAliasEdge, AliasManager: oldCfg.AliasManager, AuxLeafStore: oldCfg.AuxLeafStore, + AuxSigner: oldCfg.AuxSigner, }) require.NoError(t, err, "failed recreating aliceFundingManager") diff --git a/htlcswitch/link.go b/htlcswitch/link.go index 83495f357b..c92c431712 100644 --- a/htlcswitch/link.go +++ b/htlcswitch/link.go @@ -2191,10 +2191,20 @@ func (l *channelLink) handleUpstreamMsg(msg lnwire.Message) { // We just received a new updates to our local commitment // chain, validate this new commitment, closing the link if // invalid. + auxSigBlob, err := msg.CustomRecords.Serialize() + if err != nil { + l.failf( + LinkFailureError{code: ErrInvalidCommitment}, + "unable to serialize custom records: %v", err, + ) + + return + } err = l.channel.ReceiveNewCommitment(&lnwallet.CommitSigs{ CommitSig: msg.CommitSig, HtlcSigs: msg.HtlcSigs, PartialSig: msg.PartialSig, + AuxSigBlob: auxSigBlob, }) if err != nil { // If we were unable to reconstruct their proposed @@ -2621,11 +2631,17 @@ func (l *channelLink) updateCommitTx() error { default: } + auxBlobRecords, err := lnwire.ParseCustomRecords(newCommit.AuxSigBlob) + if err != nil { + return fmt.Errorf("error parsing aux sigs: %w", err) + } + commitSig := &lnwire.CommitSig{ - ChanID: l.ChanID(), - CommitSig: newCommit.CommitSig, - HtlcSigs: newCommit.HtlcSigs, - PartialSig: newCommit.PartialSig, + ChanID: l.ChanID(), + CommitSig: newCommit.CommitSig, + HtlcSigs: newCommit.HtlcSigs, + PartialSig: newCommit.PartialSig, + CustomRecords: auxBlobRecords, } l.cfg.Peer.SendMessage(false, commitSig) diff --git a/htlcswitch/link_test.go b/htlcswitch/link_test.go index 53a084209e..02fbd3ba0e 100644 --- a/htlcswitch/link_test.go +++ b/htlcswitch/link_test.go @@ -268,9 +268,12 @@ func TestChannelLinkRevThenSig(t *testing.T) { // Restart Bob as well by calling NewLightningChannel. bobSigner := harness.bobChannel.Signer + signerMock := lnwallet.NewDefaultAuxSignerMock(t) bobPool := lnwallet.NewSigPool(runtime.NumCPU(), bobSigner) bobChannel, err := lnwallet.NewLightningChannel( bobSigner, harness.bobChannel.State(), bobPool, + lnwallet.WithLeafStore(&lnwallet.MockAuxLeafStore{}), + lnwallet.WithAuxSigner(signerMock), ) require.NoError(t, err) err = bobPool.Start() @@ -403,9 +406,12 @@ func TestChannelLinkSigThenRev(t *testing.T) { // Restart Bob as well by calling NewLightningChannel. bobSigner := harness.bobChannel.Signer + signerMock := lnwallet.NewDefaultAuxSignerMock(t) bobPool := lnwallet.NewSigPool(runtime.NumCPU(), bobSigner) bobChannel, err := lnwallet.NewLightningChannel( bobSigner, harness.bobChannel.State(), bobPool, + lnwallet.WithLeafStore(&lnwallet.MockAuxLeafStore{}), + lnwallet.WithAuxSigner(signerMock), ) require.NoError(t, err) err = bobPool.Start() diff --git a/htlcswitch/test_utils.go b/htlcswitch/test_utils.go index a71577ef2b..1786765fb5 100644 --- a/htlcswitch/test_utils.go +++ b/htlcswitch/test_utils.go @@ -351,8 +351,11 @@ func createTestChannel(t *testing.T, alicePrivKey, bobPrivKey []byte, ) alicePool := lnwallet.NewSigPool(runtime.NumCPU(), aliceSigner) + signerMock := lnwallet.NewDefaultAuxSignerMock(t) channelAlice, err := lnwallet.NewLightningChannel( aliceSigner, aliceChannelState, alicePool, + lnwallet.WithLeafStore(&lnwallet.MockAuxLeafStore{}), + lnwallet.WithAuxSigner(signerMock), ) if err != nil { return nil, nil, err @@ -362,6 +365,8 @@ func createTestChannel(t *testing.T, alicePrivKey, bobPrivKey []byte, bobPool := lnwallet.NewSigPool(runtime.NumCPU(), bobSigner) channelBob, err := lnwallet.NewLightningChannel( bobSigner, bobChannelState, bobPool, + lnwallet.WithLeafStore(&lnwallet.MockAuxLeafStore{}), + lnwallet.WithAuxSigner(signerMock), ) if err != nil { return nil, nil, err @@ -423,6 +428,8 @@ func createTestChannel(t *testing.T, alicePrivKey, bobPrivKey []byte, newAliceChannel, err := lnwallet.NewLightningChannel( aliceSigner, aliceStoredChannel, alicePool, + lnwallet.WithLeafStore(&lnwallet.MockAuxLeafStore{}), + lnwallet.WithAuxSigner(signerMock), ) if err != nil { return nil, errors.Errorf("unable to create new channel: %v", @@ -469,6 +476,8 @@ func createTestChannel(t *testing.T, alicePrivKey, bobPrivKey []byte, newBobChannel, err := lnwallet.NewLightningChannel( bobSigner, bobStoredChannel, bobPool, + lnwallet.WithLeafStore(&lnwallet.MockAuxLeafStore{}), + lnwallet.WithAuxSigner(signerMock), ) if err != nil { return nil, errors.Errorf("unable to create new channel: %v", diff --git a/lnwallet/aux_signer.go b/lnwallet/aux_signer.go index 6ce2e99da8..5d4bc79241 100644 --- a/lnwallet/aux_signer.go +++ b/lnwallet/aux_signer.go @@ -9,6 +9,10 @@ import ( "github.com/lightningnetwork/lnd/tlv" ) +// htlcCustomSigType is the TLV type that is used to encode the custom HTLC +// signatures within the custom data for an existing HTLC. +var htlcCustomSigType tlv.TlvType65543 + // AuxHtlcDescriptor is a struct that contains the information needed to sign or // verify an HTLC for custom channels. type AuxHtlcDescriptor struct { diff --git a/lnwallet/channel.go b/lnwallet/channel.go index 3f81000a25..b344b543b6 100644 --- a/lnwallet/channel.go +++ b/lnwallet/channel.go @@ -3089,7 +3089,8 @@ func processFeeUpdate(feeUpdate *paymentDescriptor, nextHeight uint64, func genRemoteHtlcSigJobs(keyRing *CommitmentKeyRing, chanState *channeldb.OpenChannel, leaseExpiry uint32, remoteCommitView *commitment, - leafStore fn.Option[AuxLeafStore]) ([]SignJob, chan struct{}, error) { + leafStore fn.Option[AuxLeafStore]) ([]SignJob, []AuxSigJob, + chan struct{}, error) { var ( isRemoteInitiator = !chanState.IsInitiator @@ -3109,6 +3110,7 @@ func genRemoteHtlcSigJobs(keyRing *CommitmentKeyRing, numSigs := len(remoteCommitView.incomingHTLCs) + len(remoteCommitView.outgoingHTLCs) sigBatch := make([]SignJob, 0, numSigs) + auxSigBatch := make([]AuxSigJob, 0, numSigs) var err error cancelChan := make(chan struct{}) @@ -3123,8 +3125,8 @@ func genRemoteHtlcSigJobs(keyRing *CommitmentKeyRing, }, ).Unpack() if err != nil { - return nil, nil, fmt.Errorf("unable to fetch aux leaves: %w", - err) + return nil, nil, nil, fmt.Errorf("unable to fetch aux leaves: "+ + "%w", err) } // For each outgoing and incoming HTLC, if the HTLC isn't considered a @@ -3173,12 +3175,9 @@ func genRemoteHtlcSigJobs(keyRing *CommitmentKeyRing, auxLeaf, ) if err != nil { - return nil, nil, err + return nil, nil, nil, err } - // TODO(roasbeef): hook up signer interface here (later commit - // in this PR). - // Construct a full hash cache as we may be signing a segwit v1 // sighash. txOut := remoteCommitView.txn.TxOut[htlc.remoteOutputIndex] @@ -3210,6 +3209,11 @@ func genRemoteHtlcSigJobs(keyRing *CommitmentKeyRing, } sigBatch = append(sigBatch, sigJob) + + auxSigBatch = append(auxSigBatch, NewAuxSigJob( + sigJob, *keyRing, true, newAuxHtlcDescriptor(&htlc), + remoteCommitView.customBlob, auxLeaf, cancelChan, + )) } for _, htlc := range remoteCommitView.outgoingHTLCs { if HtlcIsDust( @@ -3253,7 +3257,7 @@ func genRemoteHtlcSigJobs(keyRing *CommitmentKeyRing, auxLeaf, ) if err != nil { - return nil, nil, err + return nil, nil, nil, err } // Construct a full hash cache as we may be signing a segwit v1 @@ -3282,13 +3286,19 @@ func genRemoteHtlcSigJobs(keyRing *CommitmentKeyRing, // If this is a taproot channel, then we'll need to set the // method type to ensure we generate a valid signature. if chanType.IsTaproot() { - sigJob.SignDesc.SignMethod = input.TaprootScriptSpendSignMethod //nolint:lll + //nolint:lll + sigJob.SignDesc.SignMethod = input.TaprootScriptSpendSignMethod } sigBatch = append(sigBatch, sigJob) + + auxSigBatch = append(auxSigBatch, NewAuxSigJob( + sigJob, *keyRing, false, newAuxHtlcDescriptor(&htlc), + remoteCommitView.customBlob, auxLeaf, cancelChan, + )) } - return sigBatch, cancelChan, nil + return sigBatch, auxSigBatch, cancelChan, nil } // createCommitDiff will create a commit diff given a new pending commitment @@ -3297,7 +3307,8 @@ func genRemoteHtlcSigJobs(keyRing *CommitmentKeyRing, // new commitment to the remote party. The commit diff returned contains all // information necessary for retransmission. func (lc *LightningChannel) createCommitDiff(newCommit *commitment, - commitSig lnwire.Sig, htlcSigs []lnwire.Sig) *channeldb.CommitDiff { + commitSig lnwire.Sig, htlcSigs []lnwire.Sig, + auxSigs []fn.Option[tlv.Blob]) (*channeldb.CommitDiff, error) { var ( logUpdates []channeldb.LogUpdate @@ -3366,21 +3377,71 @@ func (lc *LightningChannel) createCommitDiff(newCommit *commitment, // disk. diskCommit := newCommit.toDiskCommit(lntypes.Remote) - return &channeldb.CommitDiff{ - Commitment: *diskCommit, - CommitSig: &lnwire.CommitSig{ - ChanID: lnwire.NewChanIDFromOutPoint( - lc.channelState.FundingOutpoint, - ), - CommitSig: commitSig, - HtlcSigs: htlcSigs, + // We prepare the commit sig message to be sent to the remote party. + commitSigMsg := &lnwire.CommitSig{ + ChanID: lnwire.NewChanIDFromOutPoint( + lc.channelState.FundingOutpoint, + ), + CommitSig: commitSig, + HtlcSigs: htlcSigs, + } + + // Encode and check the size of the custom records now. + auxCustomRecords, err := fn.MapOptionZ( + lc.auxSigner, + func(s AuxSigner) fn.Result[lnwire.CustomRecords] { + blobOption, err := s.PackSigs(auxSigs).Unpack() + if err != nil { + return fn.Err[lnwire.CustomRecords](err) + } + + // We now serialize the commit sig message without the + // custom records to make sure we have space for them. + var buf bytes.Buffer + err = commitSigMsg.Encode(&buf, 0) + if err != nil { + return fn.Err[lnwire.CustomRecords](err) + } + + // The number of available bytes is the max message size + // minus the size of the message without the custom + // records. We also subtract 8 bytes for encoding + // overhead of the custom records (just some safety + // padding). + available := lnwire.MaxMsgBody - buf.Len() - 8 + + blob := blobOption.UnwrapOr(nil) + if len(blob) > available { + err = fmt.Errorf("aux sigs size %d exceeds "+ + "max allowed size of %d", len(blob), + available) + + return fn.Err[lnwire.CustomRecords](err) + } + + records, err := lnwire.ParseCustomRecords(blob) + if err != nil { + return fn.Err[lnwire.CustomRecords](err) + } + + return fn.Ok(records) }, + ).Unpack() + if err != nil { + return nil, fmt.Errorf("error packing aux sigs: %w", err) + } + + commitSigMsg.CustomRecords = auxCustomRecords + + return &channeldb.CommitDiff{ + Commitment: *diskCommit, + CommitSig: commitSigMsg, LogUpdates: logUpdates, OpenedCircuitKeys: openCircuitKeys, ClosedCircuitKeys: closedCircuitKeys, AddAcks: ackAddRefs, SettleFailAcks: settleFailRefs, - } + }, nil } // getUnsignedAckedUpdates returns all remote log updates that we haven't @@ -3773,6 +3834,10 @@ type CommitSigs struct { // PartialSig is the musig2 partial signature for taproot commitment // transactions. PartialSig lnwire.OptPartialSigWithNonceTLV + + // AuxSigBlob is the blob containing all the auxiliary signatures for + // this new commitment state. + AuxSigBlob tlv.Blob } // NewCommitState wraps the various signatures needed to properly @@ -3797,6 +3862,8 @@ type NewCommitState struct { // any). The HTLC signatures are sorted according to the BIP 69 order of the // HTLC's on the commitment transaction. Finally, the new set of pending HTLCs // for the remote party's commitment are also returned. +// +//nolint:funlen func (lc *LightningChannel) SignNextCommitment() (*NewCommitState, error) { lc.Lock() defer lc.Unlock() @@ -3889,7 +3956,7 @@ func (lc *LightningChannel) SignNextCommitment() (*NewCommitState, error) { if lc.channelState.ChanType.HasLeaseExpiration() { leaseExpiry = lc.channelState.ThawHeight } - sigBatch, cancelChan, err := genRemoteHtlcSigJobs( + sigBatch, auxSigBatch, cancelChan, err := genRemoteHtlcSigJobs( keyRing, lc.channelState, leaseExpiry, newCommitView, lc.leafStore, ) @@ -3898,6 +3965,17 @@ func (lc *LightningChannel) SignNextCommitment() (*NewCommitState, error) { } lc.sigPool.SubmitSignBatch(sigBatch) + err = fn.MapOptionZ(lc.auxSigner, func(a AuxSigner) error { + return a.SubmitSecondLevelSigBatch( + NewAuxChanState(lc.channelState), newCommitView.txn, + auxSigBatch, + ) + }) + if err != nil { + return nil, fmt.Errorf("error submitting second level sig "+ + "batch: %w", err) + } + // While the jobs are being carried out, we'll Sign their version of // the new commitment transaction while we're waiting for the rest of // the HTLC signatures to be processed. @@ -3941,11 +4019,16 @@ func (lc *LightningChannel) SignNextCommitment() (*NewCommitState, error) { sort.Slice(sigBatch, func(i, j int) bool { return sigBatch[i].OutputIndex < sigBatch[j].OutputIndex }) + sort.Slice(auxSigBatch, func(i, j int) bool { + return auxSigBatch[i].OutputIndex < auxSigBatch[j].OutputIndex + }) // With the jobs sorted, we'll now iterate through all the responses to // gather each of the signatures in order. htlcSigs = make([]lnwire.Sig, 0, len(sigBatch)) - for _, htlcSigJob := range sigBatch { + auxSigs := make([]fn.Option[tlv.Blob], 0, len(auxSigBatch)) + for i := range sigBatch { + htlcSigJob := sigBatch[i] jobResp := <-htlcSigJob.Resp // If an error occurred, then we'll cancel any other active @@ -3956,12 +4039,34 @@ func (lc *LightningChannel) SignNextCommitment() (*NewCommitState, error) { } htlcSigs = append(htlcSigs, jobResp.Sig) + + if lc.auxSigner.IsNone() { + continue + } + + auxHtlcSigJob := auxSigBatch[i] + auxJobResp := <-auxHtlcSigJob.Resp + + // If an error occurred, then we'll cancel any other active + // jobs. + if auxJobResp.Err != nil { + close(cancelChan) + return nil, auxJobResp.Err + } + + auxSigs = append(auxSigs, auxJobResp.SigBlob) } // As we're about to proposer a new commitment state for the remote // party, we'll write this pending state to disk before we exit, so we // can retransmit it if necessary. - commitDiff := lc.createCommitDiff(newCommitView, sig, htlcSigs) + commitDiff, err := lc.createCommitDiff( + newCommitView, sig, htlcSigs, auxSigs, + ) + if err != nil { + return nil, err + } + err = lc.channelState.AppendRemoteCommitChain(commitDiff) if err != nil { return nil, err @@ -3975,11 +4080,18 @@ func (lc *LightningChannel) SignNextCommitment() (*NewCommitState, error) { // latest commitment update. lc.commitChains.Remote.addCommitment(newCommitView) + auxSigBlob, err := commitDiff.CommitSig.CustomRecords.Serialize() + if err != nil { + return nil, fmt.Errorf("unable to serialize aux sig blob: %w", + err) + } + return &NewCommitState{ CommitSigs: &CommitSigs{ CommitSig: sig, HtlcSigs: htlcSigs, PartialSig: lnwire.MaybePartialSigWithNonce(partialSig), + AuxSigBlob: auxSigBlob, }, PendingHTLCs: commitDiff.Commitment.Htlcs, }, nil @@ -3991,8 +4103,8 @@ func (lc *LightningChannel) SignNextCommitment() (*NewCommitState, error) { // each time. After we receive the channel reestablish message, we learn the // nonce we need to use for the remote party. As a result, we need to generate // the partial signature again with the new nonce. -func (lc *LightningChannel) resignMusigCommit(commitTx *wire.MsgTx, -) (lnwire.OptPartialSigWithNonceTLV, error) { +func (lc *LightningChannel) resignMusigCommit( + commitTx *wire.MsgTx) (lnwire.OptPartialSigWithNonceTLV, error) { remoteSession := lc.musigSessions.RemoteSession musig, err := remoteSession.SignCommit(commitTx) @@ -4197,13 +4309,23 @@ func (lc *LightningChannel) ProcessChanSyncMsg( // If we signed this state, then we'll accumulate // another update to send over. case err == nil: + customRecords, err := lnwire.ParseCustomRecords( + newCommit.AuxSigBlob, + ) + if err != nil { + sErr := fmt.Errorf("error parsing aux "+ + "sigs: %w", err) + return nil, nil, nil, sErr + } + commitSig := &lnwire.CommitSig{ ChanID: lnwire.NewChanIDFromOutPoint( lc.channelState.FundingOutpoint, ), - CommitSig: newCommit.CommitSig, - HtlcSigs: newCommit.HtlcSigs, - PartialSig: newCommit.PartialSig, + CommitSig: newCommit.CommitSig, + HtlcSigs: newCommit.HtlcSigs, + PartialSig: newCommit.PartialSig, + CustomRecords: customRecords, } updates = append(updates, commitSig) @@ -4496,7 +4618,8 @@ func (lc *LightningChannel) computeView(view *HtlcView, func genHtlcSigValidationJobs(chanState *channeldb.OpenChannel, localCommitmentView *commitment, keyRing *CommitmentKeyRing, htlcSigs []lnwire.Sig, leaseExpiry uint32, - leafStore fn.Option[AuxLeafStore]) ([]VerifyJob, error) { + leafStore fn.Option[AuxLeafStore], auxSigner fn.Option[AuxSigner], + sigBlob fn.Option[tlv.Blob]) ([]VerifyJob, []AuxVerifyJob, error) { var ( isLocalInitiator = chanState.IsInitiator @@ -4515,6 +4638,7 @@ func genHtlcSigValidationJobs(chanState *channeldb.OpenChannel, numHtlcs := len(localCommitmentView.incomingHTLCs) + len(localCommitmentView.outgoingHTLCs) verifyJobs := make([]VerifyJob, 0, numHtlcs) + auxVerifyJobs := make([]AuxVerifyJob, 0, numHtlcs) diskCommit := localCommitmentView.toDiskCommit(lntypes.Local) auxResult, err := fn.MapOptionZ( @@ -4526,7 +4650,20 @@ func genHtlcSigValidationJobs(chanState *channeldb.OpenChannel, }, ).Unpack() if err != nil { - return nil, fmt.Errorf("unable to fetch aux leaves: %w", err) + return nil, nil, fmt.Errorf("unable to fetch aux leaves: %w", + err) + } + + // If we have a sig blob, then we'll attempt to map that to individual + // blobs for each HTLC we might need a signature for. + auxHtlcSigs, err := fn.MapOptionZ( + auxSigner, func(a AuxSigner) fn.Result[[]fn.Option[tlv.Blob]] { + return a.UnpackSigs(sigBlob) + }, + ).Unpack() + if err != nil { + return nil, nil, fmt.Errorf("error unpacking aux sigs: %w", + err) } // We'll iterate through each output in the commitment transaction, @@ -4539,6 +4676,9 @@ func genHtlcSigValidationJobs(chanState *channeldb.OpenChannel, htlcIndex uint64 sigHash func() ([]byte, error) sig input.Signature + htlc *paymentDescriptor + incoming bool + auxLeaf input.AuxTapLeaf err error ) @@ -4548,10 +4688,12 @@ func genHtlcSigValidationJobs(chanState *channeldb.OpenChannel, // If this output index is found within the incoming HTLC // index, then this means that we need to generate an HTLC // success transaction in order to validate the signature. + //nolint:lll case localCommitmentView.incomingHTLCIndex[outputIndex] != nil: - htlc := localCommitmentView.incomingHTLCIndex[outputIndex] + htlc = localCommitmentView.incomingHTLCIndex[outputIndex] htlcIndex = htlc.HtlcIndex + incoming = true sigHash = func() ([]byte, error) { op := wire.OutPoint{ @@ -4617,7 +4759,7 @@ func genHtlcSigValidationJobs(chanState *channeldb.OpenChannel, // Make sure there are more signatures left. if i >= len(htlcSigs) { - return nil, fmt.Errorf("not enough HTLC " + + return nil, nil, fmt.Errorf("not enough HTLC " + "signatures") } @@ -4633,15 +4775,16 @@ func genHtlcSigValidationJobs(chanState *channeldb.OpenChannel, // is valid. sig, err = htlcSigs[i].ToSignature() if err != nil { - return nil, err + return nil, nil, err } htlc.sig = sig // Otherwise, if this is an outgoing HTLC, then we'll need to // generate a timeout transaction so we can verify the // signature presented. + //nolint:lll case localCommitmentView.outgoingHTLCIndex[outputIndex] != nil: - htlc := localCommitmentView.outgoingHTLCIndex[outputIndex] + htlc = localCommitmentView.outgoingHTLCIndex[outputIndex] htlcIndex = htlc.HtlcIndex @@ -4712,7 +4855,7 @@ func genHtlcSigValidationJobs(chanState *channeldb.OpenChannel, // Make sure there are more signatures left. if i >= len(htlcSigs) { - return nil, fmt.Errorf("not enough HTLC " + + return nil, nil, fmt.Errorf("not enough HTLC " + "signatures") } @@ -4728,7 +4871,7 @@ func genHtlcSigValidationJobs(chanState *channeldb.OpenChannel, // is valid. sig, err = htlcSigs[i].ToSignature() if err != nil { - return nil, err + return nil, nil, err } htlc.sig = sig @@ -4744,17 +4887,40 @@ func genHtlcSigValidationJobs(chanState *channeldb.OpenChannel, SigHash: sigHash, }) + if len(auxHtlcSigs) > i { + auxSig := auxHtlcSigs[i] + auxVerifyJob := NewAuxVerifyJob( + auxSig, *keyRing, incoming, + newAuxHtlcDescriptor(htlc), + localCommitmentView.customBlob, auxLeaf, + ) + + if htlc.CustomRecords == nil { + htlc.CustomRecords = make(lnwire.CustomRecords) + } + + // As this HTLC has a custom signature associated with + // it, store it in the custom records map so we can + // write to disk later. + sigType := htlcCustomSigType.TypeVal() + htlc.CustomRecords[uint64(sigType)] = auxSig.UnwrapOr( + nil, + ) + + auxVerifyJobs = append(auxVerifyJobs, auxVerifyJob) + } + i++ } // If we received a number of HTLC signatures that doesn't match our // commitment, we'll return an error now. if len(htlcSigs) != i { - return nil, fmt.Errorf("number of htlc sig mismatch. "+ + return nil, nil, fmt.Errorf("number of htlc sig mismatch. "+ "Expected %v sigs, got %v", i, len(htlcSigs)) } - return verifyJobs, nil + return verifyJobs, auxVerifyJobs, nil } // InvalidCommitSigError is a struct that implements the error interface to @@ -4913,6 +5079,11 @@ func (lc *LightningChannel) ReceiveNewCommitment(commitSigs *CommitSigs) error { localCommitmentView.ourBalance, localCommitmentView.theirBalance, lnutils.SpewLogClosure(localCommitmentView.txn)) + var auxSigBlob fn.Option[tlv.Blob] + if commitSigs.AuxSigBlob != nil { + auxSigBlob = fn.Some(commitSigs.AuxSigBlob) + } + // As an optimization, we'll generate a series of jobs for the worker // pool to verify each of the HTLC signatures presented. Once // generated, we'll submit these jobs to the worker pool. @@ -4920,9 +5091,10 @@ func (lc *LightningChannel) ReceiveNewCommitment(commitSigs *CommitSigs) error { if lc.channelState.ChanType.HasLeaseExpiration() { leaseExpiry = lc.channelState.ThawHeight } - verifyJobs, err := genHtlcSigValidationJobs( + verifyJobs, auxVerifyJobs, err := genHtlcSigValidationJobs( lc.channelState, localCommitmentView, keyRing, - commitSigs.HtlcSigs, leaseExpiry, lc.leafStore, + commitSigs.HtlcSigs, leaseExpiry, lc.leafStore, lc.auxSigner, + auxSigBlob, ) if err != nil { return err @@ -5075,6 +5247,18 @@ func (lc *LightningChannel) ReceiveNewCommitment(commitSigs *CommitSigs) error { } } + // Now that we know all the normal sigs are valid, we'll also verify + // the aux jobs, if any exist. + err = fn.MapOptionZ(lc.auxSigner, func(a AuxSigner) error { + return a.VerifySecondLevelSigs( + NewAuxChanState(lc.channelState), localCommitTx, + auxVerifyJobs, + ) + }) + if err != nil { + return fmt.Errorf("unable to validate aux sigs: %w", err) + } + // The signature checks out, so we can now add the new commitment to // our local commitment chain. For regular channels, we can just // serialize the ECDSA sig. For taproot channels, we'll serialize the diff --git a/lnwallet/channel_test.go b/lnwallet/channel_test.go index d97a920424..24bda0b019 100644 --- a/lnwallet/channel_test.go +++ b/lnwallet/channel_test.go @@ -27,6 +27,7 @@ import ( "github.com/lightningnetwork/lnd/lnwallet/chainfee" "github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/tlv" + "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" ) @@ -701,6 +702,68 @@ func testCommitHTLCSigTieBreak(t *testing.T, restart bool) { require.NoError(t, err, "unable to receive bob's commitment") } +// TestCommitHTLCSigCustomRecordSize asserts that custom records produced for +// a commitment_signed message are properly limited in size. +func TestCommitHTLCSigCustomRecordSize(t *testing.T) { + aliceChannel, bobChannel, err := CreateTestChannels( + t, channeldb.SimpleTaprootFeatureBit| + channeldb.TapscriptRootBit, + ) + require.NoError(t, err, "unable to create test channels") + + const ( + htlcAmt = lnwire.MilliSatoshi(20000000) + numHtlcs = 2 + ) + + largeRecords := lnwire.CustomRecords{ + lnwire.MinCustomRecordsTlvType: bytes.Repeat([]byte{0}, 65_500), + } + largeBlob, err := largeRecords.Serialize() + require.NoError(t, err) + + aliceChannel.auxSigner.WhenSome(func(a AuxSigner) { + mockSigner, ok := a.(*MockAuxSigner) + require.True(t, ok, "expected MockAuxSigner") + + // Replace the default PackSigs implementation to return a + // large custom records blob. + mockSigner.ExpectedCalls = fn.Filter(func(c *mock.Call) bool { + return c.Method != "PackSigs" + }, mockSigner.ExpectedCalls) + mockSigner.On("PackSigs", mock.Anything). + Return(fn.Ok(fn.Some(largeBlob))) + }) + + // Add HTLCs with identical payment hashes and amounts, but descending + // CLTV values. We will expect the signatures to appear in the reverse + // order that the HTLCs are added due to the commitment sorting. + for i := 0; i < numHtlcs; i++ { + var ( + preimage lntypes.Preimage + hash = preimage.Hash() + ) + + htlc := &lnwire.UpdateAddHTLC{ + ID: uint64(i), + PaymentHash: hash, + Amount: htlcAmt, + Expiry: uint32(numHtlcs - i), + } + + if _, err := aliceChannel.AddHTLC(htlc, nil); err != nil { + t.Fatalf("alice unable to add htlc: %v", err) + } + if _, err := bobChannel.ReceiveHTLC(htlc); err != nil { + t.Fatalf("bob unable to receive htlc: %v", err) + } + } + + // We expect an error because of the large custom records blob. + _, err = aliceChannel.SignNextCommitment() + require.ErrorContains(t, err, "exceeds max allowed size") +} + // TestCooperativeChannelClosure checks that the coop close process finishes // with an agreement from both parties, and that the final balances of the // close tx check out. @@ -3043,6 +3106,10 @@ func restartChannel(channelOld *LightningChannel) (*LightningChannel, error) { return channelNew, nil } +// testChanSyncOweCommitment tests that if Bob restarts (and then Alice) before +// he receives Alice's CommitSig message, then Alice concludes that she needs +// to re-send the CommitDiff. After the diff has been sent, both nodes should +// resynchronize and be able to complete the dangling commit. func testChanSyncOweCommitment(t *testing.T, chanType channeldb.ChannelType) { // Create a test channel which will be used for the duration of this // unittest. The channel will be funded evenly with Alice having 5 BTC, @@ -3207,8 +3274,10 @@ func testChanSyncOweCommitment(t *testing.T, chanType channeldb.ChannelType) { len(commitSigMsg.HtlcSigs)) } for i, htlcSig := range commitSigMsg.HtlcSigs { - if !bytes.Equal(htlcSig.RawBytes(), - aliceNewCommit.HtlcSigs[i].RawBytes()) { + if !bytes.Equal( + htlcSig.RawBytes(), + aliceNewCommit.HtlcSigs[i].RawBytes(), + ) { t.Fatalf("htlc sig msgs don't match: "+ "expected %v got %v", @@ -3379,6 +3448,100 @@ func TestChanSyncOweCommitment(t *testing.T) { } } +type testSigBlob struct { + BlobInt tlv.RecordT[tlv.TlvType65634, uint16] +} + +// TestChanSyncOweCommitmentAuxSigner tests that when one party owes a +// signature after a channel reest, if an aux signer is present, then the +// signature message sent includes the additional aux sigs as extra data. +func TestChanSyncOweCommitmentAuxSigner(t *testing.T) { + t.Parallel() + + // Create a test channel which will be used for the duration of this + // unittest. The channel will be funded evenly with Alice having 5 BTC, + // and Bob having 5 BTC. + chanType := channeldb.SingleFunderTweaklessBit | + channeldb.AnchorOutputsBit | channeldb.SimpleTaprootFeatureBit | + channeldb.TapscriptRootBit + + aliceChannel, bobChannel, err := CreateTestChannels(t, chanType) + require.NoError(t, err, "unable to create test channels") + + // We'll now manually attach an aux signer to Alice's channel. + auxSigner := &MockAuxSigner{} + aliceChannel.auxSigner = fn.Some[AuxSigner](auxSigner) + + var fakeOnionBlob [lnwire.OnionPacketSize]byte + copy( + fakeOnionBlob[:], + bytes.Repeat([]byte{0x05}, lnwire.OnionPacketSize), + ) + + // To kick things off, we'll have Alice send a single HTLC to Bob. + htlcAmt := lnwire.NewMSatFromSatoshis(20000) + var bobPreimage [32]byte + copy(bobPreimage[:], bytes.Repeat([]byte{0}, 32)) + rHash := sha256.Sum256(bobPreimage[:]) + h := &lnwire.UpdateAddHTLC{ + PaymentHash: rHash, + Amount: htlcAmt, + Expiry: uint32(10), + OnionBlob: fakeOnionBlob, + } + + _, err = aliceChannel.AddHTLC(h, nil) + require.NoError(t, err, "unable to recv bob's htlc: %v", err) + + // We'll set up the mock to expect calls to PackSigs and also + // SubmitSubmitSecondLevelSigBatch. + var sigBlobBuf bytes.Buffer + sigBlob := testSigBlob{ + BlobInt: tlv.NewPrimitiveRecord[tlv.TlvType65634, uint16](5), + } + tlvStream, err := tlv.NewStream(sigBlob.BlobInt.Record()) + require.NoError(t, err, "unable to create tlv stream") + require.NoError(t, tlvStream.Encode(&sigBlobBuf)) + + auxSigner.On( + "SubmitSecondLevelSigBatch", mock.Anything, mock.Anything, + mock.Anything, + ).Return(nil).Twice() + auxSigner.On( + "PackSigs", mock.Anything, + ).Return( + fn.Ok(fn.Some(sigBlobBuf.Bytes())), nil, + ) + + _, err = aliceChannel.SignNextCommitment() + require.NoError(t, err, "unable to sign commitment") + + _, err = aliceChannel.GenMusigNonces() + require.NoError(t, err, "unable to generate musig nonces") + + // Next we'll simulate a restart, by having Bob send over a chan sync + // message to Alice. + bobSyncMsg, err := bobChannel.channelState.ChanSyncMsg() + require.NoError(t, err, "unable to produce chan sync msg") + + aliceMsgsToSend, _, _, err := aliceChannel.ProcessChanSyncMsg( + bobSyncMsg, + ) + require.NoError(t, err) + require.Len(t, aliceMsgsToSend, 2) + + // The first message should be an update add HTLC. + require.IsType(t, &lnwire.UpdateAddHTLC{}, aliceMsgsToSend[0]) + + // The second should be a commit sig message. + sigMsg, ok := aliceMsgsToSend[1].(*lnwire.CommitSig) + require.True(t, ok) + require.True(t, sigMsg.PartialSig.IsSome()) + + // The signature should have the CustomRecords field set. + require.NotEmpty(t, sigMsg.CustomRecords) +} + func testChanSyncOweCommitmentPendingRemote(t *testing.T, chanType channeldb.ChannelType, ) { @@ -3388,7 +3551,10 @@ func testChanSyncOweCommitmentPendingRemote(t *testing.T, require.NoError(t, err, "unable to create test channels") var fakeOnionBlob [lnwire.OnionPacketSize]byte - copy(fakeOnionBlob[:], bytes.Repeat([]byte{0x05}, lnwire.OnionPacketSize)) + copy( + fakeOnionBlob[:], + bytes.Repeat([]byte{0x05}, lnwire.OnionPacketSize), + ) // We'll start off the scenario where Bob send two htlcs to Alice in a // single state update. @@ -3427,7 +3593,9 @@ func testChanSyncOweCommitmentPendingRemote(t *testing.T, // Next, Alice settles the HTLCs from Bob in distinct state updates. for i := 0; i < numHtlcs; i++ { - err = aliceChannel.SettleHTLC(preimages[i], uint64(i), nil, nil, nil) + err = aliceChannel.SettleHTLC( + preimages[i], uint64(i), nil, nil, nil, + ) if err != nil { t.Fatalf("unable to settle htlc: %v", err) } @@ -3710,7 +3878,7 @@ func testChanSyncOweRevocation(t *testing.T, chanType channeldb.ChannelType) { } // TestChanSyncOweRevocation tests that if Bob restarts (and then Alice) before -// he receiver's Alice's RevokeAndAck message, then Alice concludes that she +// he received Alice's RevokeAndAck message, then Alice concludes that she // needs to re-send the RevokeAndAck. After the revocation has been sent, both // nodes should be able to successfully complete another state transition. func TestChanSyncOweRevocation(t *testing.T) { diff --git a/lnwallet/config.go b/lnwallet/config.go index 24961f38ed..425fe15dad 100644 --- a/lnwallet/config.go +++ b/lnwallet/config.go @@ -67,4 +67,8 @@ type Config struct { // AuxLeafStore is an optional store that can be used to store auxiliary // leaves for certain custom channel types. AuxLeafStore fn.Option[AuxLeafStore] + + // AuxSigner is an optional signer that can be used to sign auxiliary + // leaves for certain custom channel types. + AuxSigner fn.Option[AuxSigner] } diff --git a/lnwallet/mock.go b/lnwallet/mock.go index 591ec285ab..6e48f5b442 100644 --- a/lnwallet/mock.go +++ b/lnwallet/mock.go @@ -21,6 +21,7 @@ import ( "github.com/lightningnetwork/lnd/fn" "github.com/lightningnetwork/lnd/lnwallet/chainfee" "github.com/lightningnetwork/lnd/tlv" + "github.com/stretchr/testify/mock" ) var ( @@ -433,3 +434,54 @@ func (*MockAuxLeafStore) ApplyHtlcView( return fn.Ok(fn.None[tlv.Blob]()) } + +// MockAuxSigner is a mock implementation of the AuxSigner interface. +type MockAuxSigner struct { + mock.Mock +} + +// SubmitSecondLevelSigBatch takes a batch of aux sign jobs and +// processes them asynchronously. +func (a *MockAuxSigner) SubmitSecondLevelSigBatch(chanState AuxChanState, + tx *wire.MsgTx, jobs []AuxSigJob) error { + + args := a.Called(chanState, tx, jobs) + + // While we return, we'll also send back an instant response for the + // set of jobs. + for _, sigJob := range jobs { + sigJob.Resp <- AuxSigJobResp{} + } + + return args.Error(0) +} + +// PackSigs takes a series of aux signatures and packs them into a +// single blob that can be sent alongside the CommitSig messages. +func (a *MockAuxSigner) PackSigs( + sigs []fn.Option[tlv.Blob]) fn.Result[fn.Option[tlv.Blob]] { + + args := a.Called(sigs) + + return args.Get(0).(fn.Result[fn.Option[tlv.Blob]]) +} + +// UnpackSigs takes a packed blob of signatures and returns the +// original signatures for each HTLC, keyed by HTLC index. +func (a *MockAuxSigner) UnpackSigs( + sigs fn.Option[tlv.Blob]) fn.Result[[]fn.Option[tlv.Blob]] { + + args := a.Called(sigs) + + return args.Get(0).(fn.Result[[]fn.Option[tlv.Blob]]) +} + +// VerifySecondLevelSigs attempts to synchronously verify a batch of aux +// sig jobs. +func (a *MockAuxSigner) VerifySecondLevelSigs(chanState AuxChanState, + tx *wire.MsgTx, jobs []AuxVerifyJob) error { + + args := a.Called(chanState, tx, jobs) + + return args.Error(0) +} diff --git a/lnwallet/test_utils.go b/lnwallet/test_utils.go index a9f71f24c1..d4f0d05aef 100644 --- a/lnwallet/test_utils.go +++ b/lnwallet/test_utils.go @@ -1,6 +1,7 @@ package lnwallet import ( + "bytes" "crypto/rand" "encoding/binary" "encoding/hex" @@ -21,6 +22,8 @@ import ( "github.com/lightningnetwork/lnd/lnwallet/chainfee" "github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/shachain" + "github.com/lightningnetwork/lnd/tlv" + "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" ) @@ -369,9 +372,13 @@ func CreateTestChannels(t *testing.T, chanType channeldb.ChannelType, // TODO(roasbeef): make mock version of pre-image store + auxSigner := NewDefaultAuxSignerMock(t) + alicePool := NewSigPool(1, aliceSigner) channelAlice, err := NewLightningChannel( aliceSigner, aliceChannelState, alicePool, + WithLeafStore(&MockAuxLeafStore{}), + WithAuxSigner(auxSigner), ) if err != nil { return nil, nil, err @@ -386,6 +393,8 @@ func CreateTestChannels(t *testing.T, chanType channeldb.ChannelType, bobPool := NewSigPool(1, bobSigner) channelBob, err := NewLightningChannel( bobSigner, bobChannelState, bobPool, + WithLeafStore(&MockAuxLeafStore{}), + WithAuxSigner(auxSigner), ) if err != nil { return nil, nil, err @@ -586,3 +595,38 @@ func ForceStateTransition(chanA, chanB *LightningChannel) error { return nil } + +func NewDefaultAuxSignerMock(t *testing.T) *MockAuxSigner { + auxSigner := &MockAuxSigner{} + + type testSigBlob struct { + BlobInt tlv.RecordT[tlv.TlvType65634, uint16] + } + + var sigBlobBuf bytes.Buffer + sigBlob := testSigBlob{ + BlobInt: tlv.NewPrimitiveRecord[tlv.TlvType65634, uint16](5), + } + tlvStream, err := tlv.NewStream(sigBlob.BlobInt.Record()) + require.NoError(t, err, "unable to create tlv stream") + require.NoError(t, tlvStream.Encode(&sigBlobBuf)) + + auxSigner.On( + "SubmitSecondLevelSigBatch", mock.Anything, mock.Anything, + mock.Anything, + ).Return(nil) + auxSigner.On( + "PackSigs", mock.Anything, + ).Return(fn.Ok(fn.Some(sigBlobBuf.Bytes()))) + auxSigner.On( + "UnpackSigs", mock.Anything, + ).Return(fn.Ok([]fn.Option[tlv.Blob]{ + fn.Some(sigBlobBuf.Bytes()), + })) + auxSigner.On( + "VerifySecondLevelSigs", mock.Anything, mock.Anything, + mock.Anything, + ).Return(nil) + + return auxSigner +} diff --git a/lnwallet/transactions_test.go b/lnwallet/transactions_test.go index 3588acfebf..8786c2d5dc 100644 --- a/lnwallet/transactions_test.go +++ b/lnwallet/transactions_test.go @@ -1018,9 +1018,12 @@ func createTestChannelsForVectors(tc *testContext, chanType channeldb.ChannelTyp tc.remotePaymentBasepointSecret, remoteDummy1, remoteDummy2, }, nil) + auxSigner := NewDefaultAuxSignerMock(t) remotePool := NewSigPool(1, remoteSigner) channelRemote, err := NewLightningChannel( remoteSigner, remoteChannelState, remotePool, + WithLeafStore(&MockAuxLeafStore{}), + WithAuxSigner(auxSigner), ) require.NoError(t, err) require.NoError(t, remotePool.Start()) @@ -1028,6 +1031,8 @@ func createTestChannelsForVectors(tc *testContext, chanType channeldb.ChannelTyp localPool := NewSigPool(1, localSigner) channelLocal, err := NewLightningChannel( localSigner, localChannelState, localPool, + WithLeafStore(&MockAuxLeafStore{}), + WithAuxSigner(auxSigner), ) require.NoError(t, err) require.NoError(t, localPool.Start()) diff --git a/lnwallet/wallet.go b/lnwallet/wallet.go index 8a1a8fb9b6..39060f5a13 100644 --- a/lnwallet/wallet.go +++ b/lnwallet/wallet.go @@ -2607,6 +2607,9 @@ func (l *LightningWallet) ValidateChannel(channelState *channeldb.OpenChannel, l.Cfg.AuxLeafStore.WhenSome(func(s AuxLeafStore) { chanOpts = append(chanOpts, WithLeafStore(s)) }) + l.Cfg.AuxSigner.WhenSome(func(s AuxSigner) { + chanOpts = append(chanOpts, WithAuxSigner(s)) + }) // First, we'll obtain a fully signed commitment transaction so we can // pass into it on the chanvalidate package for verification. diff --git a/peer/brontide.go b/peer/brontide.go index 3223e7f4b7..2e4646a78f 100644 --- a/peer/brontide.go +++ b/peer/brontide.go @@ -376,6 +376,10 @@ type Config struct { // leaves for certain custom channel types. AuxLeafStore fn.Option[lnwallet.AuxLeafStore] + // AuxSigner is an optional signer that can be used to sign auxiliary + // leaves for certain custom channel types. + AuxSigner fn.Option[lnwallet.AuxSigner] + // PongBuf is a slice we'll reuse instead of allocating memory on the // heap. Since only reads will occur and no writes, there is no need // for any synchronization primitives. As a result, it's safe to share @@ -952,6 +956,9 @@ func (p *Brontide) loadActiveChannels(chans []*channeldb.OpenChannel) ( p.cfg.AuxLeafStore.WhenSome(func(s lnwallet.AuxLeafStore) { chanOpts = append(chanOpts, lnwallet.WithLeafStore(s)) }) + p.cfg.AuxSigner.WhenSome(func(s lnwallet.AuxSigner) { + chanOpts = append(chanOpts, lnwallet.WithAuxSigner(s)) + }) lnChan, err := lnwallet.NewLightningChannel( p.cfg.Signer, dbChan, p.cfg.SigPool, chanOpts..., ) @@ -4164,6 +4171,9 @@ func (p *Brontide) addActiveChannel(c *lnpeer.NewChannel) error { p.cfg.AuxLeafStore.WhenSome(func(s lnwallet.AuxLeafStore) { chanOpts = append(chanOpts, lnwallet.WithLeafStore(s)) }) + p.cfg.AuxSigner.WhenSome(func(s lnwallet.AuxSigner) { + chanOpts = append(chanOpts, lnwallet.WithAuxSigner(s)) + }) // If not already active, we'll add this channel to the set of active // channels, so we can look it up later easily according to its channel diff --git a/peer/test_utils.go b/peer/test_utils.go index e0ae29be8b..948a22c4e3 100644 --- a/peer/test_utils.go +++ b/peer/test_utils.go @@ -304,6 +304,8 @@ func createTestPeerWithChannel(t *testing.T, updateChan func(a, alicePool := lnwallet.NewSigPool(1, aliceSigner) channelAlice, err := lnwallet.NewLightningChannel( aliceSigner, aliceChannelState, alicePool, + lnwallet.WithLeafStore(&lnwallet.MockAuxLeafStore{}), + lnwallet.WithAuxSigner(&lnwallet.MockAuxSigner{}), ) if err != nil { return nil, err @@ -316,6 +318,8 @@ func createTestPeerWithChannel(t *testing.T, updateChan func(a, bobPool := lnwallet.NewSigPool(1, bobSigner) channelBob, err := lnwallet.NewLightningChannel( bobSigner, bobChannelState, bobPool, + lnwallet.WithLeafStore(&lnwallet.MockAuxLeafStore{}), + lnwallet.WithAuxSigner(&lnwallet.MockAuxSigner{}), ) if err != nil { return nil, err diff --git a/server.go b/server.go index a50b12507b..ac96c520de 100644 --- a/server.go +++ b/server.go @@ -1285,6 +1285,7 @@ func newServer(cfg *Config, listenAddrs []net.Addr, return &pc.Incoming }, AuxLeafStore: implCfg.AuxLeafStore, + AuxSigner: implCfg.AuxSigner, }, dbs.ChanStateDB) // Select the configuration and funding parameters for Bitcoin. @@ -1533,6 +1534,7 @@ func newServer(cfg *Config, listenAddrs []net.Addr, AliasManager: s.aliasMgr, IsSweeperOutpoint: s.sweeper.IsSweeperOutpoint, AuxFundingController: implCfg.AuxFundingController, + AuxSigner: implCfg.AuxSigner, }) if err != nil { return nil, err @@ -4059,6 +4061,7 @@ func (s *server) peerConnected(conn net.Conn, connReq *connmgr.ConnReq, MaxFeeExposure: thresholdMSats, Quit: s.quit, AuxLeafStore: s.implCfg.AuxLeafStore, + AuxSigner: s.implCfg.AuxSigner, MsgRouter: s.implCfg.MsgRouter, } From 9c80088abda9a2ac34b79087376d72d98b6ae897 Mon Sep 17 00:00:00 2001 From: Jonathan Harvey-Buschel Date: Sun, 8 Sep 2024 17:00:13 -0400 Subject: [PATCH 113/218] lnwallet: sort sig jobs before submission To make sure we attempt to read the results of the sig batches in the same order they're processed, we sort them _before_ submitting them to the batch processor. Otherwise it might happen that we try to read on a result channel that was never sent on because we aborted due to an error. We also use slices.SortFunc now which doesn't use reflection and might be slightly faster. --- lnwallet/channel.go | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/lnwallet/channel.go b/lnwallet/channel.go index b344b543b6..0bd1938a7e 100644 --- a/lnwallet/channel.go +++ b/lnwallet/channel.go @@ -6,7 +6,7 @@ import ( "errors" "fmt" "math" - "sort" + "slices" "sync" "github.com/btcsuite/btcd/blockchain" @@ -3963,6 +3963,17 @@ func (lc *LightningChannel) SignNextCommitment() (*NewCommitState, error) { if err != nil { return nil, err } + + // We'll need to send over the signatures to the remote party in the + // order as they appear on the commitment transaction after BIP 69 + // sorting. + slices.SortFunc(sigBatch, func(i, j SignJob) int { + return int(i.OutputIndex - j.OutputIndex) + }) + slices.SortFunc(auxSigBatch, func(i, j AuxSigJob) int { + return int(i.OutputIndex - j.OutputIndex) + }) + lc.sigPool.SubmitSignBatch(sigBatch) err = fn.MapOptionZ(lc.auxSigner, func(a AuxSigner) error { @@ -4013,18 +4024,8 @@ func (lc *LightningChannel) SignNextCommitment() (*NewCommitState, error) { } } - // We'll need to send over the signatures to the remote party in the - // order as they appear on the commitment transaction after BIP 69 - // sorting. - sort.Slice(sigBatch, func(i, j int) bool { - return sigBatch[i].OutputIndex < sigBatch[j].OutputIndex - }) - sort.Slice(auxSigBatch, func(i, j int) bool { - return auxSigBatch[i].OutputIndex < auxSigBatch[j].OutputIndex - }) - - // With the jobs sorted, we'll now iterate through all the responses to - // gather each of the signatures in order. + // Iterate through all the responses to gather each of the signatures + // in the order they were submitted. htlcSigs = make([]lnwire.Sig, 0, len(sigBatch)) auxSigs := make([]fn.Option[tlv.Blob], 0, len(auxSigBatch)) for i := range sigBatch { From 23ef68a2f76c56ec8b2f9b358e0b1ce93e81b04c Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Fri, 17 May 2024 15:19:05 +0200 Subject: [PATCH 114/218] lnrpc+rpcserver: add and populate custom channel data --- lnrpc/lightning.pb.go | 49 ++++++++++++++++++++++---- lnrpc/lightning.proto | 16 +++++++++ lnrpc/lightning.swagger.json | 15 ++++++++ lntest/harness_assertion.go | 5 +++ rpcserver.go | 68 ++++++++++++++++++++++++++++++++++++ 5 files changed, 147 insertions(+), 6 deletions(-) diff --git a/lnrpc/lightning.pb.go b/lnrpc/lightning.pb.go index 8ff18e4b11..26ab31dd93 100644 --- a/lnrpc/lightning.pb.go +++ b/lnrpc/lightning.pb.go @@ -4703,6 +4703,8 @@ type Channel struct { // useful information. This is only ever stored locally and in no way impacts // the channel's operation. Memo string `protobuf:"bytes,36,opt,name=memo,proto3" json:"memo,omitempty"` + // Custom channel data that might be populated in custom channels. + CustomChannelData []byte `protobuf:"bytes,37,opt,name=custom_channel_data,json=customChannelData,proto3" json:"custom_channel_data,omitempty"` } func (x *Channel) Reset() { @@ -4993,6 +4995,13 @@ func (x *Channel) GetMemo() string { return "" } +func (x *Channel) GetCustomChannelData() []byte { + if x != nil { + return x.CustomChannelData + } + return nil +} + type ListChannelsRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -9505,6 +9514,9 @@ type ChannelBalanceResponse struct { PendingOpenLocalBalance *Amount `protobuf:"bytes,7,opt,name=pending_open_local_balance,json=pendingOpenLocalBalance,proto3" json:"pending_open_local_balance,omitempty"` // Sum of channels pending remote balances. PendingOpenRemoteBalance *Amount `protobuf:"bytes,8,opt,name=pending_open_remote_balance,json=pendingOpenRemoteBalance,proto3" json:"pending_open_remote_balance,omitempty"` + // Custom channel data that might be populated if there are custom channels + // present. + CustomChannelData []byte `protobuf:"bytes,9,opt,name=custom_channel_data,json=customChannelData,proto3" json:"custom_channel_data,omitempty"` } func (x *ChannelBalanceResponse) Reset() { @@ -9597,6 +9609,13 @@ func (x *ChannelBalanceResponse) GetPendingOpenRemoteBalance() *Amount { return nil } +func (x *ChannelBalanceResponse) GetCustomChannelData() []byte { + if x != nil { + return x.CustomChannelData + } + return nil +} + type QueryRoutesRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -17642,6 +17661,8 @@ type PendingChannelsResponse_PendingChannel struct { // useful information. This is only ever stored locally and in no way // impacts the channel's operation. Memo string `protobuf:"bytes,13,opt,name=memo,proto3" json:"memo,omitempty"` + // Custom channel data that might be populated in custom channels. + CustomChannelData []byte `protobuf:"bytes,34,opt,name=custom_channel_data,json=customChannelData,proto3" json:"custom_channel_data,omitempty"` } func (x *PendingChannelsResponse_PendingChannel) Reset() { @@ -17767,6 +17788,13 @@ func (x *PendingChannelsResponse_PendingChannel) GetMemo() string { return "" } +func (x *PendingChannelsResponse_PendingChannel) GetCustomChannelData() []byte { + if x != nil { + return x.CustomChannelData + } + return nil +} + type PendingChannelsResponse_PendingOpenChannel struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -18652,7 +18680,7 @@ var file_lightning_proto_rawDesc = []byte{ 0x61, 0x74, 0x12, 0x2c, 0x0a, 0x12, 0x6d, 0x61, 0x78, 0x5f, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x5f, 0x68, 0x74, 0x6c, 0x63, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x10, 0x6d, 0x61, 0x78, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x48, 0x74, 0x6c, 0x63, 0x73, - 0x22, 0xad, 0x0b, 0x0a, 0x07, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x12, 0x16, 0x0a, 0x06, + 0x22, 0xdd, 0x0b, 0x0a, 0x07, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x12, 0x16, 0x0a, 0x06, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x5f, 0x70, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x72, 0x65, 0x6d, @@ -18743,6 +18771,9 @@ var file_lightning_proto_rawDesc = []byte{ 0x69, 0x61, 0x73, 0x18, 0x23, 0x20, 0x01, 0x28, 0x04, 0x42, 0x02, 0x30, 0x01, 0x52, 0x0d, 0x70, 0x65, 0x65, 0x72, 0x53, 0x63, 0x69, 0x64, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x6d, 0x65, 0x6d, 0x6f, 0x18, 0x24, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6d, 0x65, 0x6d, 0x6f, + 0x12, 0x2e, 0x0a, 0x13, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x6e, + 0x65, 0x6c, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x18, 0x25, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x11, 0x63, + 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x44, 0x61, 0x74, 0x61, 0x22, 0xdf, 0x01, 0x0a, 0x13, 0x4c, 0x69, 0x73, 0x74, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x6f, 0x6e, 0x6c, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x61, @@ -19326,7 +19357,7 @@ var file_lightning_proto_rawDesc = []byte{ 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x24, 0x0a, 0x0e, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x5f, 0x72, 0x61, 0x77, 0x5f, 0x74, 0x78, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x52, 0x61, - 0x77, 0x54, 0x78, 0x22, 0xe1, 0x13, 0x0a, 0x17, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x43, + 0x77, 0x54, 0x78, 0x22, 0x91, 0x14, 0x0a, 0x17, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2e, 0x0a, 0x13, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x6c, 0x69, 0x6d, 0x62, 0x6f, 0x5f, 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x11, 0x74, 0x6f, @@ -19358,7 +19389,7 @@ var file_lightning_proto_rawDesc = []byte{ 0x6c, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x57, 0x61, 0x69, 0x74, 0x69, 0x6e, 0x67, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x14, 0x77, 0x61, 0x69, 0x74, 0x69, 0x6e, 0x67, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x43, 0x68, 0x61, 0x6e, - 0x6e, 0x65, 0x6c, 0x73, 0x1a, 0xb3, 0x04, 0x0a, 0x0e, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, + 0x6e, 0x65, 0x6c, 0x73, 0x1a, 0xe3, 0x04, 0x0a, 0x0e, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x12, 0x26, 0x0a, 0x0f, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x5f, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x70, 0x75, 0x62, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x4e, 0x6f, 0x64, 0x65, 0x50, 0x75, 0x62, 0x12, @@ -19393,7 +19424,10 @@ var file_lightning_proto_rawDesc = []byte{ 0x6e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x46, 0x6c, 0x61, 0x67, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6d, 0x65, 0x6d, 0x6f, 0x18, 0x0d, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6d, 0x65, 0x6d, 0x6f, 0x1a, 0xf9, 0x01, 0x0a, 0x12, 0x50, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6d, 0x65, 0x6d, 0x6f, 0x12, 0x2e, 0x0a, 0x13, 0x63, 0x75, + 0x73, 0x74, 0x6f, 0x6d, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x5f, 0x64, 0x61, 0x74, + 0x61, 0x18, 0x22, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x11, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x43, + 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x44, 0x61, 0x74, 0x61, 0x1a, 0xf9, 0x01, 0x0a, 0x12, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x4f, 0x70, 0x65, 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x12, 0x47, 0x0a, 0x07, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x65, 0x6e, 0x64, 0x69, @@ -19571,7 +19605,7 @@ var file_lightning_proto_rawDesc = []byte{ 0x04, 0x52, 0x03, 0x73, 0x61, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x04, 0x6d, 0x73, 0x61, 0x74, 0x22, 0x17, 0x0a, 0x15, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x22, 0x80, 0x04, 0x0a, 0x16, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x42, + 0x65, 0x73, 0x74, 0x22, 0xb0, 0x04, 0x0a, 0x16, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1c, 0x0a, 0x07, 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x42, 0x02, 0x18, 0x01, 0x52, 0x07, 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x12, 0x34, 0x0a, 0x14, @@ -19603,7 +19637,10 @@ var file_lightning_proto_rawDesc = []byte{ 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x18, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x4f, 0x70, 0x65, 0x6e, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x42, - 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x22, 0x9a, 0x07, 0x0a, 0x12, 0x51, 0x75, 0x65, 0x72, 0x79, + 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x12, 0x2e, 0x0a, 0x13, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, + 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x18, 0x09, 0x20, + 0x01, 0x28, 0x0c, 0x52, 0x11, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x43, 0x68, 0x61, 0x6e, 0x6e, + 0x65, 0x6c, 0x44, 0x61, 0x74, 0x61, 0x22, 0x9a, 0x07, 0x0a, 0x12, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x17, 0x0a, 0x07, 0x70, 0x75, 0x62, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x70, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x6d, 0x74, 0x18, 0x02, 0x20, diff --git a/lnrpc/lightning.proto b/lnrpc/lightning.proto index 6d975c8c3e..b44b04caa0 100644 --- a/lnrpc/lightning.proto +++ b/lnrpc/lightning.proto @@ -1592,6 +1592,11 @@ message Channel { the channel's operation. */ string memo = 36; + + /* + Custom channel data that might be populated in custom channels. + */ + bytes custom_channel_data = 37; } message ListChannelsRequest { @@ -2709,6 +2714,11 @@ message PendingChannelsResponse { impacts the channel's operation. */ string memo = 13; + + /* + Custom channel data that might be populated in custom channels. + */ + bytes custom_channel_data = 34; } message PendingOpenChannel { @@ -2968,6 +2978,12 @@ message ChannelBalanceResponse { // Sum of channels pending remote balances. Amount pending_open_remote_balance = 8; + + /* + Custom channel data that might be populated if there are custom channels + present. + */ + bytes custom_channel_data = 9; } message QueryRoutesRequest { diff --git a/lnrpc/lightning.swagger.json b/lnrpc/lightning.swagger.json index fe204e12c2..0e69fe1fdf 100644 --- a/lnrpc/lightning.swagger.json +++ b/lnrpc/lightning.swagger.json @@ -3127,6 +3127,11 @@ "memo": { "type": "string", "description": "An optional note-to-self to go along with the channel containing some\nuseful information. This is only ever stored locally and in no way\nimpacts the channel's operation." + }, + "custom_channel_data": { + "type": "string", + "format": "byte", + "description": "Custom channel data that might be populated in custom channels." } } }, @@ -3849,6 +3854,11 @@ "memo": { "type": "string", "description": "An optional note-to-self to go along with the channel containing some\nuseful information. This is only ever stored locally and in no way impacts\nthe channel's operation." + }, + "custom_channel_data": { + "type": "string", + "format": "byte", + "description": "Custom channel data that might be populated in custom channels." } } }, @@ -4052,6 +4062,11 @@ "pending_open_remote_balance": { "$ref": "#/definitions/lnrpcAmount", "description": "Sum of channels pending remote balances." + }, + "custom_channel_data": { + "type": "string", + "format": "byte", + "description": "Custom channel data that might be populated if there are custom channels\npresent." } } }, diff --git a/lntest/harness_assertion.go b/lntest/harness_assertion.go index 9538c48e71..add5b91f92 100644 --- a/lntest/harness_assertion.go +++ b/lntest/harness_assertion.go @@ -964,6 +964,11 @@ func (h *HarnessTest) AssertChannelBalanceResp(hn *node.HarnessNode, expected *lnrpc.ChannelBalanceResponse) { resp := hn.RPC.ChannelBalance() + + // Ignore custom channel data of both expected and actual responses. + expected.CustomChannelData = nil + resp.CustomChannelData = nil + require.True(h, proto.Equal(expected, resp), "balance is incorrect "+ "got: %v, want: %v", resp, expected) } diff --git a/rpcserver.go b/rpcserver.go index cfbdbdd103..6b9be6a91b 100644 --- a/rpcserver.go +++ b/rpcserver.go @@ -3536,6 +3536,7 @@ func (r *rpcServer) ChannelBalance(ctx context.Context, unsettledRemoteBalance lnwire.MilliSatoshi pendingOpenLocalBalance lnwire.MilliSatoshi pendingOpenRemoteBalance lnwire.MilliSatoshi + customDataBuf bytes.Buffer ) openChannels, err := r.server.chanStateDB.FetchAllOpenChannels() @@ -3543,6 +3544,12 @@ func (r *rpcServer) ChannelBalance(ctx context.Context, return nil, err } + // Encode the number of open channels to the custom data buffer. + err = wire.WriteVarInt(&customDataBuf, 0, uint64(len(openChannels))) + if err != nil { + return nil, err + } + for _, channel := range openChannels { c := channel.LocalCommitment localBalance += c.LocalBalance @@ -3556,6 +3563,13 @@ func (r *rpcServer) ChannelBalance(ctx context.Context, unsettledRemoteBalance += htlc.Amt } } + + // Encode the custom data for this open channel. + openChanData := channel.LocalCommitment.CustomBlob.UnwrapOr(nil) + err = wire.WriteVarBytes(&customDataBuf, 0, openChanData) + if err != nil { + return nil, err + } } pendingChannels, err := r.server.chanStateDB.FetchPendingChannels() @@ -3563,10 +3577,23 @@ func (r *rpcServer) ChannelBalance(ctx context.Context, return nil, err } + // Encode the number of pending channels to the custom data buffer. + err = wire.WriteVarInt(&customDataBuf, 0, uint64(len(pendingChannels))) + if err != nil { + return nil, err + } + for _, channel := range pendingChannels { c := channel.LocalCommitment pendingOpenLocalBalance += c.LocalBalance pendingOpenRemoteBalance += c.RemoteBalance + + // Encode the custom data for this pending channel. + openChanData := channel.LocalCommitment.CustomBlob.UnwrapOr(nil) + err = wire.WriteVarBytes(&customDataBuf, 0, openChanData) + if err != nil { + return nil, err + } } rpcsLog.Debugf("[channelbalance] local_balance=%v remote_balance=%v "+ @@ -3601,6 +3628,7 @@ func (r *rpcServer) ChannelBalance(ctx context.Context, Sat: uint64(pendingOpenRemoteBalance.ToSatoshis()), Msat: uint64(pendingOpenRemoteBalance), }, + CustomChannelData: customDataBuf.Bytes(), // Deprecated fields. Balance: int64(localBalance.ToSatoshis()), @@ -3661,6 +3689,12 @@ func (r *rpcServer) fetchPendingOpenChannels() (pendingOpenChannels, error) { pendingChan.BroadcastHeight() fundingExpiryBlocks := int32(maxFundingHeight) - currentHeight + customChanBytes, err := encodeCustomChanData(pendingChan) + if err != nil { + return nil, fmt.Errorf("unable to encode open chan "+ + "data: %w", err) + } + result[i] = &lnrpc.PendingChannelsResponse_PendingOpenChannel{ Channel: &lnrpc.PendingChannelsResponse_PendingChannel{ RemoteNodePub: hex.EncodeToString(pub), @@ -3674,6 +3708,7 @@ func (r *rpcServer) fetchPendingOpenChannels() (pendingOpenChannels, error) { CommitmentType: rpcCommitmentType(pendingChan.ChanType), Private: isPrivate(pendingChan), Memo: string(pendingChan.Memo), + CustomChannelData: customChanBytes, }, CommitWeight: commitWeight, CommitFee: int64(localCommitment.CommitFee), @@ -4404,6 +4439,30 @@ func isPrivate(dbChannel *channeldb.OpenChannel) bool { return dbChannel.ChannelFlags&lnwire.FFAnnounceChannel != 1 } +// encodeCustomChanData encodes the custom channel data for the open channel. +// It encodes that data as a pair of var bytes blobs. +func encodeCustomChanData(lnChan *channeldb.OpenChannel) ([]byte, error) { + customOpenChanData := lnChan.CustomBlob.UnwrapOr(nil) + customLocalCommitData := lnChan.LocalCommitment.CustomBlob.UnwrapOr(nil) + + // We'll encode our custom channel data as two blobs. The first is a + // set of var bytes encoding of the open chan data, the second is an + // encoding of the local commitment data. + var customChanDataBuf bytes.Buffer + err := wire.WriteVarBytes(&customChanDataBuf, 0, customOpenChanData) + if err != nil { + return nil, fmt.Errorf("unable to encode open chan "+ + "data: %w", err) + } + err = wire.WriteVarBytes(&customChanDataBuf, 0, customLocalCommitData) + if err != nil { + return nil, fmt.Errorf("unable to encode local commit "+ + "data: %w", err) + } + + return customChanDataBuf.Bytes(), nil +} + // createRPCOpenChannel creates an *lnrpc.Channel from the *channeldb.Channel. func createRPCOpenChannel(r *rpcServer, dbChannel *channeldb.OpenChannel, isActive, peerAliasLookup bool) (*lnrpc.Channel, error) { @@ -4458,6 +4517,14 @@ func createRPCOpenChannel(r *rpcServer, dbChannel *channeldb.OpenChannel, // is returned and peerScidAlias will be an empty ShortChannelID. peerScidAlias, _ := r.server.aliasMgr.GetPeerAlias(chanID) + // Finally we'll attempt to encode the custom channel data if any + // exists. + customChanBytes, err := encodeCustomChanData(dbChannel) + if err != nil { + return nil, fmt.Errorf("unable to encode open chan data: %w", + err) + } + channel := &lnrpc.Channel{ Active: isActive, Private: isPrivate(dbChannel), @@ -4490,6 +4557,7 @@ func createRPCOpenChannel(r *rpcServer, dbChannel *channeldb.OpenChannel, ZeroConf: dbChannel.IsZeroConf(), ZeroConfConfirmedScid: dbChannel.ZeroConfRealScid().ToUint64(), Memo: string(dbChannel.Memo), + CustomChannelData: customChanBytes, // TODO: remove the following deprecated fields CsvDelay: uint32(dbChannel.LocalChanCfg.CsvDelay), LocalChanReserveSat: int64(dbChannel.LocalChanCfg.ChanReserve), From f9debb148b8485591192a39d944a1c830bfe1f93 Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Fri, 17 May 2024 15:19:28 +0200 Subject: [PATCH 115/218] lnd: add aux data parser This commit adds an optional data parser that can inspect and in-place format custom data of certain RPC messages. We don't add an implementation of the interface itself, as that will be provided by external components when packaging up lnd as a bundle with other software. --- config_builder.go | 4 ++ lnrpc/routerrpc/router_backend.go | 15 ++++++- rpcserver.go | 62 +++++++++++++++++++++++++++- rpcserver_test.go | 67 ++++++++++++++++++++++++++++++- 4 files changed, 143 insertions(+), 5 deletions(-) diff --git a/config_builder.go b/config_builder.go index ca32ac4cda..ee4064035f 100644 --- a/config_builder.go +++ b/config_builder.go @@ -178,6 +178,10 @@ type AuxComponents struct { // AuxSigner is an optional signer that can be used to sign auxiliary // leaves for certain custom channel types. AuxSigner fn.Option[lnwallet.AuxSigner] + + // AuxDataParser is an optional data parser that can be used to parse + // auxiliary data for certain custom channel types. + AuxDataParser fn.Option[AuxDataParser] } // DefaultWalletImpl is the default implementation of our normal, btcwallet diff --git a/lnrpc/routerrpc/router_backend.go b/lnrpc/routerrpc/router_backend.go index 6694e8b4f6..4ef60a71da 100644 --- a/lnrpc/routerrpc/router_backend.go +++ b/lnrpc/routerrpc/router_backend.go @@ -25,6 +25,7 @@ import ( "github.com/lightningnetwork/lnd/routing/route" "github.com/lightningnetwork/lnd/subscribe" "github.com/lightningnetwork/lnd/zpay32" + "google.golang.org/protobuf/proto" ) const ( @@ -104,6 +105,10 @@ type RouterBackend struct { // TODO(yy): remove this config after the new status code is fully // deployed to the network(v0.20.0). UseStatusInitiated bool + + // ParseCustomChannelData is a function that can be used to parse custom + // channel data from the first hop of a route. + ParseCustomChannelData func(message proto.Message) error } // MissionControl defines the mission control dependencies of routerrpc. @@ -596,8 +601,14 @@ func (r *RouterBackend) MarshallRoute(route *route.Route) (*lnrpc.Route, error) resp.CustomChannelData = customData - // TODO(guggero): Feed the route into the custom data parser - // (part 3 of the mega PR series). + // Allow the aux data parser to parse the custom records into + // a human-readable JSON (if available). + if r.ParseCustomChannelData != nil { + err := r.ParseCustomChannelData(resp) + if err != nil { + return nil, err + } + } } incomingAmt := route.TotalAmount diff --git a/rpcserver.go b/rpcserver.go index 6b9be6a91b..841dffe8ff 100644 --- a/rpcserver.go +++ b/rpcserver.go @@ -87,6 +87,7 @@ import ( "google.golang.org/grpc" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" + "google.golang.org/protobuf/proto" "gopkg.in/macaroon-bakery.v2/bakery" ) @@ -579,6 +580,17 @@ func MainRPCServerPermissions() map[string][]bakery.Op { } } +// AuxDataParser is an interface that is used to parse auxiliary custom data +// within RPC messages. This is used to transform binary blobs to human-readable +// JSON representations. +type AuxDataParser interface { + // InlineParseCustomData replaces any custom data binary blob in the + // given RPC message with its corresponding JSON formatted data. This + // transforms the binary (likely TLV encoded) data to a human-readable + // JSON representation (still as byte slice). + InlineParseCustomData(msg proto.Message) error +} + // rpcServer is a gRPC, RPC front end to the lnd daemon. // TODO(roasbeef): pagination support for the list-style calls type rpcServer struct { @@ -731,6 +743,20 @@ func (r *rpcServer) addDeps(s *server, macService *macaroons.Service, }, SetChannelAuto: s.chanStatusMgr.RequestAuto, UseStatusInitiated: subServerCgs.RouterRPC.UseStatusInitiated, + ParseCustomChannelData: func(msg proto.Message) error { + err = fn.MapOptionZ( + r.server.implCfg.AuxDataParser, + func(parser AuxDataParser) error { + return parser.InlineParseCustomData(msg) + }, + ) + if err != nil { + return fmt.Errorf("error parsing custom data: "+ + "%w", err) + } + + return nil + }, } genInvoiceFeatures := func() *lnwire.FeatureVector { @@ -3603,7 +3629,7 @@ func (r *rpcServer) ChannelBalance(ctx context.Context, unsettledRemoteBalance, pendingOpenLocalBalance, pendingOpenRemoteBalance) - return &lnrpc.ChannelBalanceResponse{ + resp := &lnrpc.ChannelBalanceResponse{ LocalBalance: &lnrpc.Amount{ Sat: uint64(localBalance.ToSatoshis()), Msat: uint64(localBalance), @@ -3633,7 +3659,19 @@ func (r *rpcServer) ChannelBalance(ctx context.Context, // Deprecated fields. Balance: int64(localBalance.ToSatoshis()), PendingOpenBalance: int64(pendingOpenLocalBalance.ToSatoshis()), - }, nil + } + + err = fn.MapOptionZ( + r.server.implCfg.AuxDataParser, + func(parser AuxDataParser) error { + return parser.InlineParseCustomData(resp) + }, + ) + if err != nil { + return nil, fmt.Errorf("error parsing custom data: %w", err) + } + + return resp, nil } type ( @@ -4075,6 +4113,16 @@ func (r *rpcServer) PendingChannels(ctx context.Context, resp.WaitingCloseChannels = waitingCloseChannels resp.TotalLimboBalance += limbo + err = fn.MapOptionZ( + r.server.implCfg.AuxDataParser, + func(parser AuxDataParser) error { + return parser.InlineParseCustomData(resp) + }, + ) + if err != nil { + return nil, fmt.Errorf("error parsing custom data: %w", err) + } + return resp, nil } @@ -4389,6 +4437,16 @@ func (r *rpcServer) ListChannels(ctx context.Context, resp.Channels = append(resp.Channels, channel) } + err = fn.MapOptionZ( + r.server.implCfg.AuxDataParser, + func(parser AuxDataParser) error { + return parser.InlineParseCustomData(resp) + }, + ) + if err != nil { + return nil, fmt.Errorf("error parsing custom data: %w", err) + } + return resp, nil } diff --git a/rpcserver_test.go b/rpcserver_test.go index ca70ad9df7..9dd3c3f86f 100644 --- a/rpcserver_test.go +++ b/rpcserver_test.go @@ -1,14 +1,79 @@ package lnd import ( + "fmt" "testing" + "github.com/lightningnetwork/lnd/channeldb" + "github.com/lightningnetwork/lnd/fn" + "github.com/lightningnetwork/lnd/lnrpc" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "google.golang.org/protobuf/proto" ) func TestGetAllPermissions(t *testing.T) { perms := GetAllPermissions() - // Currently there are there are 16 entity:action pairs in use. + // Currently there are 16 entity:action pairs in use. assert.Equal(t, len(perms), 16) } + +// mockDataParser is a mock implementation of the AuxDataParser interface. +type mockDataParser struct { +} + +// InlineParseCustomData replaces any custom data binary blob in the given RPC +// message with its corresponding JSON formatted data. This transforms the +// binary (likely TLV encoded) data to a human-readable JSON representation +// (still as byte slice). +func (m *mockDataParser) InlineParseCustomData(msg proto.Message) error { + switch m := msg.(type) { + case *lnrpc.ChannelBalanceResponse: + m.CustomChannelData = []byte(`{"foo": "bar"}`) + + return nil + + default: + return fmt.Errorf("mock only supports ChannelBalanceResponse") + } +} + +func TestAuxDataParser(t *testing.T) { + // We create an empty channeldb, so we can fetch some channels. + cdb, err := channeldb.Open(t.TempDir()) + require.NoError(t, err) + t.Cleanup(func() { + require.NoError(t, cdb.Close()) + }) + + r := &rpcServer{ + server: &server{ + chanStateDB: cdb.ChannelStateDB(), + implCfg: &ImplementationCfg{ + AuxComponents: AuxComponents{ + AuxDataParser: fn.Some[AuxDataParser]( + &mockDataParser{}, + ), + }, + }, + }, + } + + // With the aux data parser in place, we should get a formatted JSON + // in the custom channel data field. + resp, err := r.ChannelBalance(nil, &lnrpc.ChannelBalanceRequest{}) + require.NoError(t, err) + require.NotNil(t, resp) + require.Equal(t, []byte(`{"foo": "bar"}`), resp.CustomChannelData) + + // If we don't supply the aux data parser, we should get the raw binary + // data. Which in this case is just two VarInt fields (1 byte each) that + // represent the value of 0 (zero active and zero pending channels). + r.server.implCfg.AuxComponents.AuxDataParser = fn.None[AuxDataParser]() + + resp, err = r.ChannelBalance(nil, &lnrpc.ChannelBalanceRequest{}) + require.NoError(t, err) + require.NotNil(t, resp) + require.Equal(t, []byte{0x00, 0x00}, resp.CustomChannelData) +} From b35dae72a58e55aca581fbc816d6766197565709 Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Mon, 27 May 2024 13:36:01 +0200 Subject: [PATCH 116/218] channeldb: add NextHeight, fix formatting --- lnwallet/channel.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lnwallet/channel.go b/lnwallet/channel.go index 0bd1938a7e..c78742d139 100644 --- a/lnwallet/channel.go +++ b/lnwallet/channel.go @@ -4544,6 +4544,7 @@ func (lc *LightningChannel) computeView(view *HtlcView, // need this to determine which HTLCs are dust, and also the final fee // rate. view.FeePerKw = commitChain.tip().feePerKw + view.NextHeight = nextHeight // We evaluate the view at this stage, meaning settled and failed HTLCs // will remove their corresponding added HTLCs. The resulting filtered @@ -5992,8 +5993,9 @@ func (lc *LightningChannel) ReceiveHTLC(htlc *lnwire.UpdateAddHTLC) (uint64, defer lc.Unlock() if htlc.ID != lc.updateLogs.Remote.htlcCounter { - return 0, fmt.Errorf("ID %d on HTLC add does not match expected next "+ - "ID %d", htlc.ID, lc.updateLogs.Remote.htlcCounter) + return 0, fmt.Errorf("ID %d on HTLC add does not match "+ + "expected next ID %d", htlc.ID, + lc.updateLogs.Remote.htlcCounter) } pd := &paymentDescriptor{ From c3627545be8c5bea6f0486d1a3d37ca94fb5647f Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Thu, 23 May 2024 13:26:32 +0200 Subject: [PATCH 117/218] htlcswitch: override amount check on custom records --- htlcswitch/link.go | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/htlcswitch/link.go b/htlcswitch/link.go index c92c431712..7f0b3eb8cc 100644 --- a/htlcswitch/link.go +++ b/htlcswitch/link.go @@ -3793,7 +3793,18 @@ func (l *channelLink) processExitHop(add lnwire.UpdateAddHTLC, // As we're the exit hop, we'll double check the hop-payload included in // the HTLC to ensure that it was crafted correctly by the sender and // is compatible with the HTLC we were extended. - if add.Amount < fwdInfo.AmountToForward { + // + // For a special case, if the fwdInfo doesn't have any blinded path + // information, and the incoming HTLC had special extra data, then + // we'll skip this amount check. The invoice acceptor will make sure we + // reject the HTLC if it's not containing the correct amount after + // examining the custom data. + hasBlindedPath := fwdInfo.NextBlinding.IsSome() + customHTLC := len(add.CustomRecords) > 0 && !hasBlindedPath + log.Tracef("Exit hop has_blinded_path=%v custom_htlc_bypass=%v", + hasBlindedPath, customHTLC) + + if !customHTLC && add.Amount < fwdInfo.AmountToForward { l.log.Errorf("onion payload of incoming htlc(%x) has "+ "incompatible value: expected <=%v, got %v", add.PaymentHash, add.Amount, fwdInfo.AmountToForward) From acf0d0ebe82b3fda5330f62bfbc55e22d9dc5eab Mon Sep 17 00:00:00 2001 From: ffranr Date: Tue, 23 Apr 2024 14:31:38 +0100 Subject: [PATCH 118/218] invoices: add invoice htlc interceptor service This commit introduces a new invoice htlc interceptor service that intercepts invoice HTLCs during their settlement phase. It forwards HTLCs to a subscribed client to determine their settlement outcomes. This commit also introduces an interface to facilitate integrating the interceptor with other packages. --- invoices/interface.go | 68 ++++++++ invoices/mock.go | 31 ++++ invoices/modification_interceptor.go | 179 ++++++++++++++++++++++ invoices/modification_interceptor_test.go | 107 +++++++++++++ 4 files changed, 385 insertions(+) create mode 100644 invoices/modification_interceptor.go create mode 100644 invoices/modification_interceptor_test.go diff --git a/invoices/interface.go b/invoices/interface.go index f48aa37b60..4f5e08e32e 100644 --- a/invoices/interface.go +++ b/invoices/interface.go @@ -207,3 +207,71 @@ type InvoiceUpdater interface { // Finalize finalizes the update before it is written to the database. Finalize(updateType UpdateType) error } + +// HtlcModifyRequest is the request that is passed to the client via callback +// during a HTLC interceptor session. The request contains the invoice that the +// given HTLC is attempting to settle. +type HtlcModifyRequest struct { + // WireCustomRecords are the custom records that were parsed from the + // HTLC wire message. These are the records of the current HTLC to be + // accepted/settled. All previously accepted/settled HTLCs for the same + // invoice are present in the Invoice field below. + WireCustomRecords lnwire.CustomRecords + + // ExitHtlcCircuitKey is the circuit key that identifies the HTLC which + // is involved in the invoice settlement. + ExitHtlcCircuitKey CircuitKey + + // ExitHtlcAmt is the amount of the HTLC which is involved in the + // invoice settlement. + ExitHtlcAmt lnwire.MilliSatoshi + + // ExitHtlcExpiry is the absolute expiry height of the HTLC which is + // involved in the invoice settlement. + ExitHtlcExpiry uint32 + + // CurrentHeight is the current block height. + CurrentHeight uint32 + + // Invoice is the invoice that is being intercepted. The HTLCs within + // the invoice are only those previously accepted/settled for the same + // invoice. + Invoice Invoice +} + +// HtlcModifyResponse is the response that the client should send back to the +// interceptor after processing the HTLC modify request. +type HtlcModifyResponse struct { + // AmountPaid is the amount that the client has decided the HTLC is + // actually worth. This might be different from the amount that the + // HTLC was originally sent with, in case additional value is carried + // along with it (which might be the case in custom channels). + AmountPaid lnwire.MilliSatoshi +} + +// HtlcModifyCallback is a function that is called when an invoice is +// intercepted by the invoice interceptor. +type HtlcModifyCallback func(HtlcModifyRequest) (*HtlcModifyResponse, error) + +// HtlcModifier is an interface that allows an intercept client to register +// itself as a modifier of HTLCs that are settling an invoice. The client can +// then modify the HTLCs based on the invoice and the HTLC that is settling it. +type HtlcModifier interface { + // RegisterInterceptor sets the client callback function that will be + // called when an invoice is intercepted. If a callback is already set, + // an error is returned. The returned function must be used to reset the + // callback to nil once the client is done or disconnects. The read-only + // channel closes when the server stops. + RegisterInterceptor(HtlcModifyCallback) (func(), <-chan struct{}, error) +} + +// HtlcInterceptor is an interface that allows the invoice registry to let +// clients intercept invoices before they are settled. +type HtlcInterceptor interface { + // Intercept generates a new intercept session for the given invoice. + // The call blocks until the client has responded to the request or an + // error occurs. The response callback is only called if a session was + // created in the first place, which is only the case if a client is + // registered. + Intercept(HtlcModifyRequest, func(HtlcModifyResponse)) error +} diff --git a/invoices/mock.go b/invoices/mock.go index 25c81a35d7..5d929c2274 100644 --- a/invoices/mock.go +++ b/invoices/mock.go @@ -83,3 +83,34 @@ func (m *MockInvoiceDB) DeleteCanceledInvoices(ctx context.Context) error { return args.Error(0) } + +// MockHtlcModifier is a mock implementation of the HtlcModifier interface. +type MockHtlcModifier struct { +} + +// Intercept generates a new intercept session for the given invoice. +// The call blocks until the client has responded to the request or an +// error occurs. The response callback is only called if a session was +// created in the first place, which is only the case if a client is +// registered. +func (m *MockHtlcModifier) Intercept( + _ HtlcModifyRequest, _ func(HtlcModifyResponse)) error { + + return nil +} + +// RegisterInterceptor sets the client callback function that will be +// called when an invoice is intercepted. If a callback is already set, +// an error is returned. The returned function must be used to reset the +// callback to nil once the client is done or disconnects. The read-only channel +// closes when the server stops. +func (m *MockHtlcModifier) RegisterInterceptor(HtlcModifyCallback) (func(), + <-chan struct{}, error) { + + return func() {}, make(chan struct{}), nil +} + +// Ensure that MockHtlcModifier implements the HtlcInterceptor and HtlcModifier +// interfaces. +var _ HtlcInterceptor = (*MockHtlcModifier)(nil) +var _ HtlcModifier = (*MockHtlcModifier)(nil) diff --git a/invoices/modification_interceptor.go b/invoices/modification_interceptor.go new file mode 100644 index 0000000000..e2d9a90513 --- /dev/null +++ b/invoices/modification_interceptor.go @@ -0,0 +1,179 @@ +package invoices + +import ( + "errors" + "sync/atomic" + + "github.com/lightningnetwork/lnd/fn" +) + +var ( + // ErrInterceptorClientAlreadyConnected is an error that is returned + // when a client tries to connect to the interceptor service while + // another client is already connected. + ErrInterceptorClientAlreadyConnected = errors.New( + "interceptor client already connected", + ) + + // ErrInterceptorClientDisconnected is an error that is returned when + // the client disconnects during an interceptor session. + ErrInterceptorClientDisconnected = errors.New( + "interceptor client disconnected", + ) +) + +// safeCallback is a wrapper around a callback function that is safe for +// concurrent access. +type safeCallback struct { + // callback is the actual callback function that is called when an + // invoice is intercepted. This might be nil if no client is currently + // connected. + callback atomic.Pointer[HtlcModifyCallback] +} + +// Set atomically sets the callback function. If a callback is already set, an +// error is returned. The returned function can be used to reset the callback to +// nil once the client is done. +func (s *safeCallback) Set(callback HtlcModifyCallback) (func(), error) { + if !s.callback.CompareAndSwap(nil, &callback) { + return nil, ErrInterceptorClientAlreadyConnected + } + + return func() { + s.callback.Store(nil) + }, nil +} + +// IsConnected returns true if a client is currently connected. +func (s *safeCallback) IsConnected() bool { + return s.callback.Load() != nil +} + +// Exec executes the callback function if it is set. If the callback is not set, +// an error is returned. +func (s *safeCallback) Exec(req HtlcModifyRequest) (*HtlcModifyResponse, + error) { + + callback := s.callback.Load() + if callback == nil { + return nil, ErrInterceptorClientDisconnected + } + + return (*callback)(req) +} + +// HtlcModificationInterceptor is a service that intercepts HTLCs that aim to +// settle an invoice, enabling a subscribed client to modify certain aspects of +// those HTLCs. +type HtlcModificationInterceptor struct { + // callback is the wrapped client callback function that is called when + // an invoice is intercepted. This function gives the client the ability + // to determine how the invoice should be settled. + callback *safeCallback + + // quit is a channel that is closed when the interceptor is stopped. + quit chan struct{} +} + +// NewHtlcModificationInterceptor creates a new HtlcModificationInterceptor. +func NewHtlcModificationInterceptor() *HtlcModificationInterceptor { + return &HtlcModificationInterceptor{ + callback: &safeCallback{}, + } +} + +// Intercept generates a new intercept session for the given invoice. The call +// blocks until the client has responded to the request or an error occurs. The +// response callback is only called if a session was created in the first place, +// which is only the case if a client is registered. +func (s *HtlcModificationInterceptor) Intercept(clientRequest HtlcModifyRequest, + responseCallback func(HtlcModifyResponse)) error { + + // If there is no client callback set we will not handle the invoice + // further. + if !s.callback.IsConnected() { + log.Debugf("Not intercepting invoice with circuit key %v, no "+ + "intercept client connected", + clientRequest.ExitHtlcCircuitKey) + + return nil + } + + // We'll block until the client has responded to the request or an error + // occurs. + var ( + responseChan = make(chan *HtlcModifyResponse, 1) + errChan = make(chan error, 1) + ) + + // The callback function will block at the client's discretion. We will + // therefore execute it in a separate goroutine. We don't need a wait + // group because we wait for the response directly below. The caller + // needs to make sure they don't block indefinitely, by selecting on the + // quit channel they receive when registering the callback. + go func() { + log.Debugf("Waiting for client response from invoice HTLC "+ + "interceptor session with circuit key %v", + clientRequest.ExitHtlcCircuitKey) + + // By this point, we've already checked that the client callback + // is set. However, if the client disconnected since that check + // then Exec will return an error. + result, err := s.callback.Exec(clientRequest) + if err != nil { + _ = fn.SendOrQuit(errChan, err, s.quit) + + return + } + + _ = fn.SendOrQuit(responseChan, result, s.quit) + }() + + // Wait for the client to respond or an error to occur. + select { + case response := <-responseChan: + log.Debugf("Received invoice HTLC interceptor response: %v", + response) + + responseCallback(*response) + + return nil + + case err := <-errChan: + log.Errorf("Error from invoice HTLC interceptor session: %v", + err) + + return err + + case <-s.quit: + return ErrInterceptorClientDisconnected + } +} + +// RegisterInterceptor sets the client callback function that will be called +// when an invoice is intercepted. If a callback is already set, an error is +// returned. The returned function must be used to reset the callback to nil +// once the client is done or disconnects. +func (s *HtlcModificationInterceptor) RegisterInterceptor( + callback HtlcModifyCallback) (func(), <-chan struct{}, error) { + + done, err := s.callback.Set(callback) + return done, s.quit, err +} + +// Start starts the service. +func (s *HtlcModificationInterceptor) Start() error { + return nil +} + +// Stop stops the service. +func (s *HtlcModificationInterceptor) Stop() error { + close(s.quit) + + return nil +} + +// Ensure that HtlcModificationInterceptor implements the HtlcInterceptor and +// HtlcModifier interfaces. +var _ HtlcInterceptor = (*HtlcModificationInterceptor)(nil) +var _ HtlcModifier = (*HtlcModificationInterceptor)(nil) diff --git a/invoices/modification_interceptor_test.go b/invoices/modification_interceptor_test.go new file mode 100644 index 0000000000..286a390ff3 --- /dev/null +++ b/invoices/modification_interceptor_test.go @@ -0,0 +1,107 @@ +package invoices + +import ( + "fmt" + "testing" + "time" + + "github.com/lightningnetwork/lnd/lnwire" + "github.com/stretchr/testify/require" +) + +var ( + defaultTimeout = 50 * time.Millisecond +) + +// TestHtlcModificationInterceptor tests the basic functionality of the HTLC +// modification interceptor. +func TestHtlcModificationInterceptor(t *testing.T) { + interceptor := NewHtlcModificationInterceptor() + request := HtlcModifyRequest{ + WireCustomRecords: lnwire.CustomRecords{ + lnwire.MinCustomRecordsTlvType: []byte{1, 2, 3}, + }, + ExitHtlcCircuitKey: CircuitKey{ + ChanID: lnwire.NewShortChanIDFromInt(1), + HtlcID: 1, + }, + ExitHtlcAmt: 1234, + } + expectedResponse := HtlcModifyResponse{ + AmountPaid: 345, + } + interceptCallbackCalled := make(chan HtlcModifyRequest, 1) + successInterceptCallback := func( + req HtlcModifyRequest) (*HtlcModifyResponse, error) { + + interceptCallbackCalled <- req + + return &expectedResponse, nil + } + errorInterceptCallback := func( + req HtlcModifyRequest) (*HtlcModifyResponse, error) { + + interceptCallbackCalled <- req + + return nil, fmt.Errorf("something went wrong") + } + responseCallbackCalled := make(chan HtlcModifyResponse, 1) + responseCallback := func(resp HtlcModifyResponse) { + responseCallbackCalled <- resp + } + + // Create a session without setting a callback first. + err := interceptor.Intercept(request, responseCallback) + require.NoError(t, err) + + // Set the callback and create a new session. + done, _, err := interceptor.RegisterInterceptor( + successInterceptCallback, + ) + require.NoError(t, err) + + err = interceptor.Intercept(request, responseCallback) + require.NoError(t, err) + + // The intercept callback should be called now. + select { + case req := <-interceptCallbackCalled: + require.Equal(t, request, req) + + case <-time.After(defaultTimeout): + t.Fatal("intercept callback not called") + } + + // And the result should make it back to the response callback. + select { + case resp := <-responseCallbackCalled: + require.Equal(t, expectedResponse, resp) + + case <-time.After(defaultTimeout): + t.Fatal("response callback not called") + } + + // If we try to set a new callback without first returning the previous + // one, we should get an error. + _, _, err = interceptor.RegisterInterceptor(successInterceptCallback) + require.ErrorIs(t, err, ErrInterceptorClientAlreadyConnected) + + // Reset the callback, then try to set a new one. + done() + done2, _, err := interceptor.RegisterInterceptor(errorInterceptCallback) + require.NoError(t, err) + defer done2() + + // We should now get an error when intercepting. + err = interceptor.Intercept(request, responseCallback) + require.ErrorContains(t, err, "something went wrong") + + // The success callback should not be called. + select { + case resp := <-responseCallbackCalled: + t.Fatalf("unexpected response: %v", resp) + + case <-time.After(defaultTimeout): + // Expected. + } +} From 4d9422a06473efc20c4aa6000213c0f931321813 Mon Sep 17 00:00:00 2001 From: ffranr Date: Tue, 23 Apr 2024 14:33:31 +0100 Subject: [PATCH 119/218] invoices: integrate settlement interceptor with invoice registry This commit updates the invoice registry to utilize the settlement interceptor during the invoice settlement routine. It allows the interceptor to capture the invoice, providing interception clients an opportunity to determine the settlement outcome. --- htlcswitch/mock.go | 2 ++ invoices/invoiceregistry.go | 60 +++++++++++++++++++++++++++++--- invoices/invoiceregistry_test.go | 10 ++++++ invoices/test_utils_test.go | 1 + 4 files changed, 69 insertions(+), 4 deletions(-) diff --git a/htlcswitch/mock.go b/htlcswitch/mock.go index 6d27a5be99..aea0013741 100644 --- a/htlcswitch/mock.go +++ b/htlcswitch/mock.go @@ -1014,6 +1014,7 @@ func newMockRegistry(minDelta uint32) *mockInvoiceRegistry { panic(err) } + modifierMock := &invoices.MockHtlcModifier{} registry := invoices.NewRegistry( cdb, invoices.NewInvoiceExpiryWatcher( @@ -1022,6 +1023,7 @@ func newMockRegistry(minDelta uint32) *mockInvoiceRegistry { ), &invoices.RegistryConfig{ FinalCltvRejectDelta: 5, + HtlcInterceptor: modifierMock, }, ) registry.Start() diff --git a/invoices/invoiceregistry.go b/invoices/invoiceregistry.go index 7b3caa34a4..c7f4cdb7b4 100644 --- a/invoices/invoiceregistry.go +++ b/invoices/invoiceregistry.go @@ -74,6 +74,10 @@ type RegistryConfig struct { // KeysendHoldTime indicates for how long we want to accept and hold // spontaneous keysend payments. KeysendHoldTime time.Duration + + // HtlcInterceptor is an interface that allows the invoice registry to + // let clients intercept invoices before they are settled. + HtlcInterceptor HtlcInterceptor } // htlcReleaseEvent describes an htlc auto-release event. It is used to release @@ -1019,13 +1023,61 @@ func (i *InvoiceRegistry) notifyExitHopHtlcLocked( ctx *invoiceUpdateCtx, hodlChan chan<- interface{}) ( HtlcResolution, invoiceExpiry, error) { + invoiceRef := ctx.invoiceRef() + setID := (*SetID)(ctx.setID()) + + // We need to look up the current state of the invoice in order to send + // the previously accepted/settled HTLCs to the interceptor. + existingInvoice, err := i.idb.LookupInvoice( + context.Background(), invoiceRef, + ) + switch { + case errors.Is(err, ErrInvoiceNotFound) || + errors.Is(err, ErrNoInvoicesCreated): + + // If the invoice was not found, return a failure resolution + // with an invoice not found result. + return NewFailResolution( + ctx.circuitKey, ctx.currentHeight, + ResultInvoiceNotFound, + ), nil, nil + + case err != nil: + ctx.log(err.Error()) + return nil, nil, err + } + + // Provide the invoice to the settlement interceptor to allow + // the interceptor's client an opportunity to manipulate the + // settlement process. + err = i.cfg.HtlcInterceptor.Intercept(HtlcModifyRequest{ + ExitHtlcCircuitKey: ctx.circuitKey, + ExitHtlcAmt: ctx.amtPaid, + ExitHtlcExpiry: ctx.expiry, + CurrentHeight: uint32(ctx.currentHeight), + Invoice: existingInvoice, + }, func(resp HtlcModifyResponse) { + log.Debugf("Received invoice HTLC interceptor response: %v", + resp) + + if resp.AmountPaid != 0 { + ctx.amtPaid = resp.AmountPaid + } + }) + if err != nil { + err := fmt.Errorf("error during invoice HTLC interception: %w", + err) + ctx.log(err.Error()) + + return nil, nil, err + } + // We'll attempt to settle an invoice matching this rHash on disk (if // one exists). The callback will update the invoice state and/or htlcs. var ( resolution HtlcResolution updateSubscribers bool ) - callback := func(inv *Invoice) (*InvoiceUpdateDesc, error) { updateDesc, res, err := updateInvoice(ctx, inv) if err != nil { @@ -1042,8 +1094,6 @@ func (i *InvoiceRegistry) notifyExitHopHtlcLocked( return updateDesc, nil } - invoiceRef := ctx.invoiceRef() - setID := (*SetID)(ctx.setID()) invoice, err := i.idb.UpdateInvoice( context.Background(), invoiceRef, setID, callback, ) @@ -1080,6 +1130,8 @@ func (i *InvoiceRegistry) notifyExitHopHtlcLocked( var invoiceToExpire invoiceExpiry + log.Tracef("Settlement resolution: %T %v", resolution, resolution) + switch res := resolution.(type) { case *HtlcFailResolution: // Inspect latest htlc state on the invoice. If it is found, @@ -1212,7 +1264,7 @@ func (i *InvoiceRegistry) notifyExitHopHtlcLocked( } // Now that the links have been notified of any state changes to their - // HTLCs, we'll go ahead and notify any clients wiaiting on the invoice + // HTLCs, we'll go ahead and notify any clients waiting on the invoice // state changes. if updateSubscribers { // We'll add a setID onto the notification, but only if this is diff --git a/invoices/invoiceregistry_test.go b/invoices/invoiceregistry_test.go index b0c0195227..b7b3d574a7 100644 --- a/invoices/invoiceregistry_test.go +++ b/invoices/invoiceregistry_test.go @@ -23,6 +23,12 @@ import ( "github.com/stretchr/testify/require" ) +var ( + // htlcModifierMock is a mock implementation of the invoice HtlcModifier + // interface. + htlcModifierMock = &invpkg.MockHtlcModifier{} +) + // TestInvoiceRegistry is a master test which encompasses all tests using an // InvoiceDB instance. The purpose of this test is to be able to run all tests // with a custom DB instance, so that we can test the same logic with different @@ -517,6 +523,7 @@ func testSettleHoldInvoice(t *testing.T, cfg := invpkg.RegistryConfig{ FinalCltvRejectDelta: testFinalCltvRejectDelta, Clock: clock, + HtlcInterceptor: htlcModifierMock, } expiryWatcher := invpkg.NewInvoiceExpiryWatcher( @@ -683,6 +690,7 @@ func testCancelHoldInvoice(t *testing.T, cfg := invpkg.RegistryConfig{ FinalCltvRejectDelta: testFinalCltvRejectDelta, Clock: testClock, + HtlcInterceptor: htlcModifierMock, } expiryWatcher := invpkg.NewInvoiceExpiryWatcher( cfg.Clock, 0, uint32(testCurrentHeight), nil, newMockNotifier(), @@ -1200,6 +1208,7 @@ func testInvoiceExpiryWithRegistry(t *testing.T, cfg := invpkg.RegistryConfig{ FinalCltvRejectDelta: testFinalCltvRejectDelta, Clock: testClock, + HtlcInterceptor: htlcModifierMock, } expiryWatcher := invpkg.NewInvoiceExpiryWatcher( @@ -1310,6 +1319,7 @@ func testOldInvoiceRemovalOnStart(t *testing.T, FinalCltvRejectDelta: testFinalCltvRejectDelta, Clock: testClock, GcCanceledInvoicesOnStartup: true, + HtlcInterceptor: htlcModifierMock, } expiryWatcher := invpkg.NewInvoiceExpiryWatcher( diff --git a/invoices/test_utils_test.go b/invoices/test_utils_test.go index ed7bfccddc..fe69cad6f1 100644 --- a/invoices/test_utils_test.go +++ b/invoices/test_utils_test.go @@ -153,6 +153,7 @@ func defaultRegistryConfig() invpkg.RegistryConfig { return invpkg.RegistryConfig{ FinalCltvRejectDelta: testFinalCltvRejectDelta, HtlcHoldDuration: 30 * time.Second, + HtlcInterceptor: &invpkg.MockHtlcModifier{}, } } From f903e16c47cec7dc4b1f89b4a6a14d20125adf22 Mon Sep 17 00:00:00 2001 From: ffranr Date: Tue, 23 Apr 2024 14:38:47 +0100 Subject: [PATCH 120/218] lnd: initialize invoice settlement interceptor at server startup This commit initiates the invoice settlement interceptor during the main server startup, assigning it a handle within the server. --- server.go | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/server.go b/server.go index ac96c520de..b336036f0b 100644 --- a/server.go +++ b/server.go @@ -263,6 +263,8 @@ type server struct { invoices *invoices.InvoiceRegistry + invoiceHtlcModifier *invoices.HtlcModificationInterceptor + channelNotifier *channelnotifier.ChannelNotifier peerNotifier *peernotifier.PeerNotifier @@ -561,6 +563,7 @@ func newServer(cfg *Config, listenAddrs []net.Addr, return nil, err } + invoiceHtlcModifier := invoices.NewHtlcModificationInterceptor() registryConfig := invoices.RegistryConfig{ FinalCltvRejectDelta: lncfg.DefaultFinalCltvRejectDelta, HtlcHoldDuration: invoices.DefaultHtlcHoldDuration, @@ -570,6 +573,7 @@ func newServer(cfg *Config, listenAddrs []net.Addr, GcCanceledInvoicesOnStartup: cfg.GcCanceledInvoicesOnStartup, GcCanceledInvoicesOnTheFly: cfg.GcCanceledInvoicesOnTheFly, KeysendHoldTime: cfg.KeysendHoldTime, + HtlcInterceptor: invoiceHtlcModifier, } s := &server{ @@ -618,6 +622,8 @@ func newServer(cfg *Config, listenAddrs []net.Addr, peerConnectedListeners: make(map[string][]chan<- lnpeer.Peer), peerDisconnectedListeners: make(map[string][]chan<- struct{}), + invoiceHtlcModifier: invoiceHtlcModifier, + customMessageServer: subscribe.NewServer(), tlsManager: tlsManager, @@ -2089,6 +2095,12 @@ func (s *server) Start() error { return } + cleanup = cleanup.add(s.invoiceHtlcModifier.Stop) + if err := s.invoiceHtlcModifier.Start(); err != nil { + startErr = err + return + } + cleanup = cleanup.add(s.chainArb.Stop) if err := s.chainArb.Start(); err != nil { startErr = err From 7285dfa66a9c338663571f5272f5e8c375bfb6ea Mon Sep 17 00:00:00 2001 From: ffranr Date: Tue, 23 Apr 2024 14:48:42 +0100 Subject: [PATCH 121/218] invoicesrpc: add HTLC modifier to invoices RPC server This commit integrates the HTLC modifier service into the invoices RPC server. --- lnd.go | 1 + lnrpc/invoicesrpc/config_active.go | 5 +++++ rpcserver.go | 5 +++-- subrpcserver_config.go | 6 +++++- 4 files changed, 14 insertions(+), 3 deletions(-) diff --git a/lnd.go b/lnd.go index da7747e91d..b69383a893 100644 --- a/lnd.go +++ b/lnd.go @@ -638,6 +638,7 @@ func Main(cfg *Config, lisCfg ListenerCfg, implCfg *ImplementationCfg, err = rpcServer.addDeps( server, interceptorChain.MacaroonService(), cfg.SubRPCServers, atplManager, server.invoices, tower, multiAcceptor, + server.invoiceHtlcModifier, ) if err != nil { return mkErr("unable to add deps to RPC server: %v", err) diff --git a/lnrpc/invoicesrpc/config_active.go b/lnrpc/invoicesrpc/config_active.go index a5b29c32a2..b781be8756 100644 --- a/lnrpc/invoicesrpc/config_active.go +++ b/lnrpc/invoicesrpc/config_active.go @@ -30,6 +30,11 @@ type Config struct { // created by the daemon. InvoiceRegistry *invoices.InvoiceRegistry + // HtlcModifier is a service which intercepts invoice HTLCs during the + // settlement phase, enabling a subscribed client to modify certain + // aspects of those HTLCs. + HtlcModifier invoices.HtlcModifier + // IsChannelActive is used to generate valid hop hints. IsChannelActive func(chanID lnwire.ChannelID) bool diff --git a/rpcserver.go b/rpcserver.go index 841dffe8ff..ef9d893124 100644 --- a/rpcserver.go +++ b/rpcserver.go @@ -684,7 +684,8 @@ func newRPCServer(cfg *Config, interceptorChain *rpcperms.InterceptorChain, func (r *rpcServer) addDeps(s *server, macService *macaroons.Service, subServerCgs *subRPCServerConfigs, atpl *autopilot.Manager, invoiceRegistry *invoices.InvoiceRegistry, tower *watchtower.Standalone, - chanPredicate chanacceptor.MultiplexAcceptor) error { + chanPredicate chanacceptor.MultiplexAcceptor, + invoiceHtlcModifier *invoices.HtlcModificationInterceptor) error { // Set up router rpc backend. selfNode, err := s.graphDB.SourceNode() @@ -787,7 +788,7 @@ func (r *rpcServer) addDeps(s *server, macService *macaroons.Service, s.sweeper, tower, s.towerClientMgr, r.cfg.net.ResolveTCPAddr, genInvoiceFeatures, genAmpInvoiceFeatures, s.getNodeAnnouncement, s.updateAndBrodcastSelfNode, parseAddr, - rpcsLog, s.aliasMgr, + rpcsLog, s.aliasMgr, invoiceHtlcModifier, ) if err != nil { return err diff --git a/subrpcserver_config.go b/subrpcserver_config.go index 5127068b65..6f4c813367 100644 --- a/subrpcserver_config.go +++ b/subrpcserver_config.go @@ -122,7 +122,8 @@ func (s *subRPCServerConfigs) PopulateDependencies(cfg *Config, updateNodeAnnouncement func(features *lnwire.RawFeatureVector, modifiers ...netann.NodeAnnModifier) error, parseAddr func(addr string) (net.Addr, error), - rpcLogger btclog.Logger, aliasMgr *aliasmgr.Manager) error { + rpcLogger btclog.Logger, aliasMgr *aliasmgr.Manager, + invoiceHtlcModifier *invoices.HtlcModificationInterceptor) error { // First, we'll use reflect to obtain a version of the config struct // that allows us to programmatically inspect its fields. @@ -238,6 +239,9 @@ func (s *subRPCServerConfigs) PopulateDependencies(cfg *Config, subCfgValue.FieldByName("InvoiceRegistry").Set( reflect.ValueOf(invoiceRegistry), ) + subCfgValue.FieldByName("HtlcModifier").Set( + reflect.ValueOf(invoiceHtlcModifier), + ) subCfgValue.FieldByName("IsChannelActive").Set( reflect.ValueOf(htlcSwitch.HasActiveLink), ) From e417250f6d88c4a67978bafdda0ca9b4195f6d3e Mon Sep 17 00:00:00 2001 From: ffranr Date: Tue, 23 Apr 2024 14:58:29 +0100 Subject: [PATCH 122/218] invoicesrpc: add `HtlcModifier` RPC endpoint and modifier RPC server This commit introduces a singleton invoice HTLC modifier RPC server and an endpoint to activate it. The server interfaces with the internal invoice HTLC modifier interpreter, handling the marshalling between RPC types and internal formats. --- lnrpc/invoicesrpc/htlc_modifier.go | 86 +++++ lnrpc/invoicesrpc/invoices.pb.go | 416 ++++++++++++++++++++---- lnrpc/invoicesrpc/invoices.pb.gw.go | 83 +++++ lnrpc/invoicesrpc/invoices.proto | 53 ++- lnrpc/invoicesrpc/invoices.swagger.json | 111 ++++++- lnrpc/invoicesrpc/invoices.yaml | 3 + lnrpc/invoicesrpc/invoices_grpc.pb.go | 80 ++++- lnrpc/invoicesrpc/invoices_server.go | 40 +++ 8 files changed, 813 insertions(+), 59 deletions(-) create mode 100644 lnrpc/invoicesrpc/htlc_modifier.go diff --git a/lnrpc/invoicesrpc/htlc_modifier.go b/lnrpc/invoicesrpc/htlc_modifier.go new file mode 100644 index 0000000000..ce61af4680 --- /dev/null +++ b/lnrpc/invoicesrpc/htlc_modifier.go @@ -0,0 +1,86 @@ +package invoicesrpc + +import ( + "fmt" + + "github.com/btcsuite/btcd/chaincfg" + "github.com/lightningnetwork/lnd/invoices" + "github.com/lightningnetwork/lnd/lnwire" +) + +// htlcModifier is a helper struct that handles the lifecycle of an RPC invoice +// HTLC modifier server instance. +// +// This struct handles passing send and receive RPC messages between the client +// and the invoice service. +type htlcModifier struct { + // chainParams is required to properly marshall an invoice for RPC. + chainParams *chaincfg.Params + + // serverStream is a bidirectional RPC server stream to send invoices to + // a client and receive accept responses from the client. + serverStream Invoices_HtlcModifierServer +} + +// newHtlcModifier creates a new RPC invoice HTLC modifier handler. +func newHtlcModifier(params *chaincfg.Params, + serverStream Invoices_HtlcModifierServer) *htlcModifier { + + return &htlcModifier{ + chainParams: params, + serverStream: serverStream, + } +} + +// onIntercept is called when an invoice HTLC is intercepted by the invoice HTLC +// modifier. This method sends the invoice and the current HTLC to the client. +func (r *htlcModifier) onIntercept( + req invoices.HtlcModifyRequest) (*invoices.HtlcModifyResponse, error) { + + // Convert the circuit key to an RPC circuit key. + rpcCircuitKey := &CircuitKey{ + ChanId: req.ExitHtlcCircuitKey.ChanID.ToUint64(), + HtlcId: req.ExitHtlcCircuitKey.HtlcID, + } + + // Convert the invoice to an RPC invoice. + rpcInvoice, err := CreateRPCInvoice(&req.Invoice, r.chainParams) + if err != nil { + return nil, err + } + + // Send the modification request to the client. + err = r.serverStream.Send(&HtlcModifyRequest{ + Invoice: rpcInvoice, + ExitHtlcCircuitKey: rpcCircuitKey, + ExitHtlcAmt: uint64(req.ExitHtlcAmt), + ExitHtlcExpiry: req.ExitHtlcExpiry, + CurrentHeight: req.CurrentHeight, + ExitHtlcWireCustomRecords: req.WireCustomRecords, + }) + if err != nil { + return nil, err + } + + // Then wait for the client to respond. + resp, err := r.serverStream.Recv() + if err != nil { + return nil, err + } + + if resp.CircuitKey == nil { + return nil, fmt.Errorf("missing circuit key") + } + + log.Tracef("Resolving invoice HTLC modifier response %v", resp) + + // Pass the resolution to the modifier. + var amtPaid lnwire.MilliSatoshi + if resp.AmtPaid != nil { + amtPaid = lnwire.MilliSatoshi(*resp.AmtPaid) + } + + return &invoices.HtlcModifyResponse{ + AmountPaid: amtPaid, + }, nil +} diff --git a/lnrpc/invoicesrpc/invoices.pb.go b/lnrpc/invoicesrpc/invoices.pb.go index 27cf424d30..c23d3a6694 100644 --- a/lnrpc/invoicesrpc/invoices.pb.go +++ b/lnrpc/invoicesrpc/invoices.pb.go @@ -617,6 +617,219 @@ func (*LookupInvoiceMsg_PaymentAddr) isLookupInvoiceMsg_InvoiceRef() {} func (*LookupInvoiceMsg_SetId) isLookupInvoiceMsg_InvoiceRef() {} +// CircuitKey is a unique identifier for an HTLC. +type CircuitKey struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The id of the channel that the is part of this circuit. + ChanId uint64 `protobuf:"varint,1,opt,name=chan_id,json=chanId,proto3" json:"chan_id,omitempty"` + // The index of the incoming htlc in the incoming channel. + HtlcId uint64 `protobuf:"varint,2,opt,name=htlc_id,json=htlcId,proto3" json:"htlc_id,omitempty"` +} + +func (x *CircuitKey) Reset() { + *x = CircuitKey{} + if protoimpl.UnsafeEnabled { + mi := &file_invoicesrpc_invoices_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *CircuitKey) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CircuitKey) ProtoMessage() {} + +func (x *CircuitKey) ProtoReflect() protoreflect.Message { + mi := &file_invoicesrpc_invoices_proto_msgTypes[8] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use CircuitKey.ProtoReflect.Descriptor instead. +func (*CircuitKey) Descriptor() ([]byte, []int) { + return file_invoicesrpc_invoices_proto_rawDescGZIP(), []int{8} +} + +func (x *CircuitKey) GetChanId() uint64 { + if x != nil { + return x.ChanId + } + return 0 +} + +func (x *CircuitKey) GetHtlcId() uint64 { + if x != nil { + return x.HtlcId + } + return 0 +} + +type HtlcModifyRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The invoice the intercepted HTLC is attempting to settle. The HTLCs in + // the invoice are only HTLCs that have already been accepted or settled, + // not including the current intercepted HTLC. + Invoice *lnrpc.Invoice `protobuf:"bytes,1,opt,name=invoice,proto3" json:"invoice,omitempty"` + // The unique identifier of the HTLC of this intercepted HTLC. + ExitHtlcCircuitKey *CircuitKey `protobuf:"bytes,2,opt,name=exit_htlc_circuit_key,json=exitHtlcCircuitKey,proto3" json:"exit_htlc_circuit_key,omitempty"` + // The amount in milli-satoshi that the exit HTLC is attempting to pay. + ExitHtlcAmt uint64 `protobuf:"varint,3,opt,name=exit_htlc_amt,json=exitHtlcAmt,proto3" json:"exit_htlc_amt,omitempty"` + // The absolute expiry height of the exit HTLC. + ExitHtlcExpiry uint32 `protobuf:"varint,4,opt,name=exit_htlc_expiry,json=exitHtlcExpiry,proto3" json:"exit_htlc_expiry,omitempty"` + // The current block height. + CurrentHeight uint32 `protobuf:"varint,5,opt,name=current_height,json=currentHeight,proto3" json:"current_height,omitempty"` + // The wire message custom records of the exit HTLC. + ExitHtlcWireCustomRecords map[uint64][]byte `protobuf:"bytes,6,rep,name=exit_htlc_wire_custom_records,json=exitHtlcWireCustomRecords,proto3" json:"exit_htlc_wire_custom_records,omitempty" protobuf_key:"varint,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` +} + +func (x *HtlcModifyRequest) Reset() { + *x = HtlcModifyRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_invoicesrpc_invoices_proto_msgTypes[9] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *HtlcModifyRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*HtlcModifyRequest) ProtoMessage() {} + +func (x *HtlcModifyRequest) ProtoReflect() protoreflect.Message { + mi := &file_invoicesrpc_invoices_proto_msgTypes[9] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use HtlcModifyRequest.ProtoReflect.Descriptor instead. +func (*HtlcModifyRequest) Descriptor() ([]byte, []int) { + return file_invoicesrpc_invoices_proto_rawDescGZIP(), []int{9} +} + +func (x *HtlcModifyRequest) GetInvoice() *lnrpc.Invoice { + if x != nil { + return x.Invoice + } + return nil +} + +func (x *HtlcModifyRequest) GetExitHtlcCircuitKey() *CircuitKey { + if x != nil { + return x.ExitHtlcCircuitKey + } + return nil +} + +func (x *HtlcModifyRequest) GetExitHtlcAmt() uint64 { + if x != nil { + return x.ExitHtlcAmt + } + return 0 +} + +func (x *HtlcModifyRequest) GetExitHtlcExpiry() uint32 { + if x != nil { + return x.ExitHtlcExpiry + } + return 0 +} + +func (x *HtlcModifyRequest) GetCurrentHeight() uint32 { + if x != nil { + return x.CurrentHeight + } + return 0 +} + +func (x *HtlcModifyRequest) GetExitHtlcWireCustomRecords() map[uint64][]byte { + if x != nil { + return x.ExitHtlcWireCustomRecords + } + return nil +} + +type HtlcModifyResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The circuit key of the HTLC that the client wants to modify. + CircuitKey *CircuitKey `protobuf:"bytes,1,opt,name=circuit_key,json=circuitKey,proto3" json:"circuit_key,omitempty"` + // The modified amount in milli-satoshi that the exit HTLC is paying. This + // value can be different from the actual on-chain HTLC amount, in case the + // HTLC carries other valuable items, as can be the case with custom channel + // types. + AmtPaid *uint64 `protobuf:"varint,2,opt,name=amt_paid,json=amtPaid,proto3,oneof" json:"amt_paid,omitempty"` +} + +func (x *HtlcModifyResponse) Reset() { + *x = HtlcModifyResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_invoicesrpc_invoices_proto_msgTypes[10] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *HtlcModifyResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*HtlcModifyResponse) ProtoMessage() {} + +func (x *HtlcModifyResponse) ProtoReflect() protoreflect.Message { + mi := &file_invoicesrpc_invoices_proto_msgTypes[10] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use HtlcModifyResponse.ProtoReflect.Descriptor instead. +func (*HtlcModifyResponse) Descriptor() ([]byte, []int) { + return file_invoicesrpc_invoices_proto_rawDescGZIP(), []int{10} +} + +func (x *HtlcModifyResponse) GetCircuitKey() *CircuitKey { + if x != nil { + return x.CircuitKey + } + return nil +} + +func (x *HtlcModifyResponse) GetAmtPaid() uint64 { + if x != nil && x.AmtPaid != nil { + return *x.AmtPaid + } + return 0 +} + var File_invoicesrpc_invoices_proto protoreflect.FileDescriptor var file_invoicesrpc_invoices_proto_rawDesc = []byte{ @@ -678,41 +891,87 @@ var file_invoicesrpc_invoices_proto_rawDesc = []byte{ 0x73, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x4d, 0x6f, 0x64, 0x69, 0x66, 0x69, 0x65, 0x72, 0x52, 0x0e, 0x6c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x4d, 0x6f, 0x64, 0x69, 0x66, 0x69, 0x65, 0x72, 0x42, 0x0d, 0x0a, 0x0b, 0x69, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x5f, 0x72, - 0x65, 0x66, 0x2a, 0x44, 0x0a, 0x0e, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x4d, 0x6f, 0x64, 0x69, - 0x66, 0x69, 0x65, 0x72, 0x12, 0x0b, 0x0a, 0x07, 0x44, 0x45, 0x46, 0x41, 0x55, 0x4c, 0x54, 0x10, - 0x00, 0x12, 0x11, 0x0a, 0x0d, 0x48, 0x54, 0x4c, 0x43, 0x5f, 0x53, 0x45, 0x54, 0x5f, 0x4f, 0x4e, - 0x4c, 0x59, 0x10, 0x01, 0x12, 0x12, 0x0a, 0x0e, 0x48, 0x54, 0x4c, 0x43, 0x5f, 0x53, 0x45, 0x54, - 0x5f, 0x42, 0x4c, 0x41, 0x4e, 0x4b, 0x10, 0x02, 0x32, 0x9b, 0x03, 0x0a, 0x08, 0x49, 0x6e, 0x76, - 0x6f, 0x69, 0x63, 0x65, 0x73, 0x12, 0x56, 0x0a, 0x16, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, - 0x62, 0x65, 0x53, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x12, - 0x2a, 0x2e, 0x69, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x73, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x75, - 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x53, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x49, 0x6e, 0x76, - 0x6f, 0x69, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x0e, 0x2e, 0x6c, 0x6e, - 0x72, 0x70, 0x63, 0x2e, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x30, 0x01, 0x12, 0x4e, 0x0a, - 0x0d, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x12, 0x1d, - 0x2e, 0x69, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x73, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x61, 0x6e, - 0x63, 0x65, 0x6c, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x4d, 0x73, 0x67, 0x1a, 0x1e, 0x2e, - 0x69, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x73, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x61, 0x6e, 0x63, - 0x65, 0x6c, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x12, 0x55, 0x0a, - 0x0e, 0x41, 0x64, 0x64, 0x48, 0x6f, 0x6c, 0x64, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x12, - 0x22, 0x2e, 0x69, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x73, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x64, - 0x64, 0x48, 0x6f, 0x6c, 0x64, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x69, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x73, 0x72, 0x70, - 0x63, 0x2e, 0x41, 0x64, 0x64, 0x48, 0x6f, 0x6c, 0x64, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, - 0x52, 0x65, 0x73, 0x70, 0x12, 0x4e, 0x0a, 0x0d, 0x53, 0x65, 0x74, 0x74, 0x6c, 0x65, 0x49, 0x6e, - 0x76, 0x6f, 0x69, 0x63, 0x65, 0x12, 0x1d, 0x2e, 0x69, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x73, - 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x74, 0x74, 0x6c, 0x65, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, - 0x65, 0x4d, 0x73, 0x67, 0x1a, 0x1e, 0x2e, 0x69, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x73, 0x72, - 0x70, 0x63, 0x2e, 0x53, 0x65, 0x74, 0x74, 0x6c, 0x65, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, - 0x52, 0x65, 0x73, 0x70, 0x12, 0x40, 0x0a, 0x0f, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x49, 0x6e, - 0x76, 0x6f, 0x69, 0x63, 0x65, 0x56, 0x32, 0x12, 0x1d, 0x2e, 0x69, 0x6e, 0x76, 0x6f, 0x69, 0x63, - 0x65, 0x73, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x49, 0x6e, 0x76, 0x6f, - 0x69, 0x63, 0x65, 0x4d, 0x73, 0x67, 0x1a, 0x0e, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x49, - 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x42, 0x33, 0x5a, 0x31, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, - 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x6e, 0x69, 0x6e, 0x67, 0x6e, 0x65, - 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x2f, 0x6c, 0x6e, 0x64, 0x2f, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2f, - 0x69, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x73, 0x72, 0x70, 0x63, 0x62, 0x06, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x33, + 0x65, 0x66, 0x22, 0x3e, 0x0a, 0x0a, 0x43, 0x69, 0x72, 0x63, 0x75, 0x69, 0x74, 0x4b, 0x65, 0x79, + 0x12, 0x17, 0x0a, 0x07, 0x63, 0x68, 0x61, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x04, 0x52, 0x06, 0x63, 0x68, 0x61, 0x6e, 0x49, 0x64, 0x12, 0x17, 0x0a, 0x07, 0x68, 0x74, 0x6c, + 0x63, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x68, 0x74, 0x6c, 0x63, + 0x49, 0x64, 0x22, 0xcd, 0x03, 0x0a, 0x11, 0x48, 0x74, 0x6c, 0x63, 0x4d, 0x6f, 0x64, 0x69, 0x66, + 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x28, 0x0a, 0x07, 0x69, 0x6e, 0x76, 0x6f, + 0x69, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x6c, 0x6e, 0x72, 0x70, + 0x63, 0x2e, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x52, 0x07, 0x69, 0x6e, 0x76, 0x6f, 0x69, + 0x63, 0x65, 0x12, 0x4a, 0x0a, 0x15, 0x65, 0x78, 0x69, 0x74, 0x5f, 0x68, 0x74, 0x6c, 0x63, 0x5f, + 0x63, 0x69, 0x72, 0x63, 0x75, 0x69, 0x74, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x17, 0x2e, 0x69, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x73, 0x72, 0x70, 0x63, 0x2e, + 0x43, 0x69, 0x72, 0x63, 0x75, 0x69, 0x74, 0x4b, 0x65, 0x79, 0x52, 0x12, 0x65, 0x78, 0x69, 0x74, + 0x48, 0x74, 0x6c, 0x63, 0x43, 0x69, 0x72, 0x63, 0x75, 0x69, 0x74, 0x4b, 0x65, 0x79, 0x12, 0x22, + 0x0a, 0x0d, 0x65, 0x78, 0x69, 0x74, 0x5f, 0x68, 0x74, 0x6c, 0x63, 0x5f, 0x61, 0x6d, 0x74, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x65, 0x78, 0x69, 0x74, 0x48, 0x74, 0x6c, 0x63, 0x41, + 0x6d, 0x74, 0x12, 0x28, 0x0a, 0x10, 0x65, 0x78, 0x69, 0x74, 0x5f, 0x68, 0x74, 0x6c, 0x63, 0x5f, + 0x65, 0x78, 0x70, 0x69, 0x72, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0e, 0x65, 0x78, + 0x69, 0x74, 0x48, 0x74, 0x6c, 0x63, 0x45, 0x78, 0x70, 0x69, 0x72, 0x79, 0x12, 0x25, 0x0a, 0x0e, + 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x05, + 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0d, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x48, 0x65, 0x69, + 0x67, 0x68, 0x74, 0x12, 0x7f, 0x0a, 0x1d, 0x65, 0x78, 0x69, 0x74, 0x5f, 0x68, 0x74, 0x6c, 0x63, + 0x5f, 0x77, 0x69, 0x72, 0x65, 0x5f, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5f, 0x72, 0x65, 0x63, + 0x6f, 0x72, 0x64, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x3d, 0x2e, 0x69, 0x6e, 0x76, + 0x6f, 0x69, 0x63, 0x65, 0x73, 0x72, 0x70, 0x63, 0x2e, 0x48, 0x74, 0x6c, 0x63, 0x4d, 0x6f, 0x64, + 0x69, 0x66, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x45, 0x78, 0x69, 0x74, 0x48, + 0x74, 0x6c, 0x63, 0x57, 0x69, 0x72, 0x65, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x52, 0x65, 0x63, + 0x6f, 0x72, 0x64, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x19, 0x65, 0x78, 0x69, 0x74, 0x48, + 0x74, 0x6c, 0x63, 0x57, 0x69, 0x72, 0x65, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x52, 0x65, 0x63, + 0x6f, 0x72, 0x64, 0x73, 0x1a, 0x4c, 0x0a, 0x1e, 0x45, 0x78, 0x69, 0x74, 0x48, 0x74, 0x6c, 0x63, + 0x57, 0x69, 0x72, 0x65, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, + 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x04, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, + 0x38, 0x01, 0x22, 0x7b, 0x0a, 0x12, 0x48, 0x74, 0x6c, 0x63, 0x4d, 0x6f, 0x64, 0x69, 0x66, 0x79, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x38, 0x0a, 0x0b, 0x63, 0x69, 0x72, 0x63, + 0x75, 0x69, 0x74, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, + 0x69, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x73, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x69, 0x72, 0x63, + 0x75, 0x69, 0x74, 0x4b, 0x65, 0x79, 0x52, 0x0a, 0x63, 0x69, 0x72, 0x63, 0x75, 0x69, 0x74, 0x4b, + 0x65, 0x79, 0x12, 0x1e, 0x0a, 0x08, 0x61, 0x6d, 0x74, 0x5f, 0x70, 0x61, 0x69, 0x64, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x04, 0x48, 0x00, 0x52, 0x07, 0x61, 0x6d, 0x74, 0x50, 0x61, 0x69, 0x64, 0x88, + 0x01, 0x01, 0x42, 0x0b, 0x0a, 0x09, 0x5f, 0x61, 0x6d, 0x74, 0x5f, 0x70, 0x61, 0x69, 0x64, 0x2a, + 0x44, 0x0a, 0x0e, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x4d, 0x6f, 0x64, 0x69, 0x66, 0x69, 0x65, + 0x72, 0x12, 0x0b, 0x0a, 0x07, 0x44, 0x45, 0x46, 0x41, 0x55, 0x4c, 0x54, 0x10, 0x00, 0x12, 0x11, + 0x0a, 0x0d, 0x48, 0x54, 0x4c, 0x43, 0x5f, 0x53, 0x45, 0x54, 0x5f, 0x4f, 0x4e, 0x4c, 0x59, 0x10, + 0x01, 0x12, 0x12, 0x0a, 0x0e, 0x48, 0x54, 0x4c, 0x43, 0x5f, 0x53, 0x45, 0x54, 0x5f, 0x42, 0x4c, + 0x41, 0x4e, 0x4b, 0x10, 0x02, 0x32, 0xf0, 0x03, 0x0a, 0x08, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, + 0x65, 0x73, 0x12, 0x56, 0x0a, 0x16, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x53, + 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x12, 0x2a, 0x2e, 0x69, + 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x73, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x75, 0x62, 0x73, 0x63, + 0x72, 0x69, 0x62, 0x65, 0x53, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, + 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x0e, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, + 0x2e, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x30, 0x01, 0x12, 0x4e, 0x0a, 0x0d, 0x43, 0x61, + 0x6e, 0x63, 0x65, 0x6c, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x12, 0x1d, 0x2e, 0x69, 0x6e, + 0x76, 0x6f, 0x69, 0x63, 0x65, 0x73, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, + 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x4d, 0x73, 0x67, 0x1a, 0x1e, 0x2e, 0x69, 0x6e, 0x76, + 0x6f, 0x69, 0x63, 0x65, 0x73, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x49, + 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x12, 0x55, 0x0a, 0x0e, 0x41, 0x64, + 0x64, 0x48, 0x6f, 0x6c, 0x64, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x12, 0x22, 0x2e, 0x69, + 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x73, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x64, 0x64, 0x48, 0x6f, + 0x6c, 0x64, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x1f, 0x2e, 0x69, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x73, 0x72, 0x70, 0x63, 0x2e, 0x41, + 0x64, 0x64, 0x48, 0x6f, 0x6c, 0x64, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x52, 0x65, 0x73, + 0x70, 0x12, 0x4e, 0x0a, 0x0d, 0x53, 0x65, 0x74, 0x74, 0x6c, 0x65, 0x49, 0x6e, 0x76, 0x6f, 0x69, + 0x63, 0x65, 0x12, 0x1d, 0x2e, 0x69, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x73, 0x72, 0x70, 0x63, + 0x2e, 0x53, 0x65, 0x74, 0x74, 0x6c, 0x65, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x4d, 0x73, + 0x67, 0x1a, 0x1e, 0x2e, 0x69, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x73, 0x72, 0x70, 0x63, 0x2e, + 0x53, 0x65, 0x74, 0x74, 0x6c, 0x65, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x52, 0x65, 0x73, + 0x70, 0x12, 0x40, 0x0a, 0x0f, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x49, 0x6e, 0x76, 0x6f, 0x69, + 0x63, 0x65, 0x56, 0x32, 0x12, 0x1d, 0x2e, 0x69, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x73, 0x72, + 0x70, 0x63, 0x2e, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, + 0x4d, 0x73, 0x67, 0x1a, 0x0e, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x49, 0x6e, 0x76, 0x6f, + 0x69, 0x63, 0x65, 0x12, 0x53, 0x0a, 0x0c, 0x48, 0x74, 0x6c, 0x63, 0x4d, 0x6f, 0x64, 0x69, 0x66, + 0x69, 0x65, 0x72, 0x12, 0x1f, 0x2e, 0x69, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x73, 0x72, 0x70, + 0x63, 0x2e, 0x48, 0x74, 0x6c, 0x63, 0x4d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x1a, 0x1e, 0x2e, 0x69, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x73, 0x72, + 0x70, 0x63, 0x2e, 0x48, 0x74, 0x6c, 0x63, 0x4d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x28, 0x01, 0x30, 0x01, 0x42, 0x33, 0x5a, 0x31, 0x67, 0x69, 0x74, 0x68, + 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x6e, 0x69, 0x6e, 0x67, + 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x2f, 0x6c, 0x6e, 0x64, 0x2f, 0x6c, 0x6e, 0x72, 0x70, + 0x63, 0x2f, 0x69, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x73, 0x72, 0x70, 0x63, 0x62, 0x06, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -728,7 +987,7 @@ func file_invoicesrpc_invoices_proto_rawDescGZIP() []byte { } var file_invoicesrpc_invoices_proto_enumTypes = make([]protoimpl.EnumInfo, 1) -var file_invoicesrpc_invoices_proto_msgTypes = make([]protoimpl.MessageInfo, 8) +var file_invoicesrpc_invoices_proto_msgTypes = make([]protoimpl.MessageInfo, 12) var file_invoicesrpc_invoices_proto_goTypes = []interface{}{ (LookupModifier)(0), // 0: invoicesrpc.LookupModifier (*CancelInvoiceMsg)(nil), // 1: invoicesrpc.CancelInvoiceMsg @@ -739,27 +998,37 @@ var file_invoicesrpc_invoices_proto_goTypes = []interface{}{ (*SettleInvoiceResp)(nil), // 6: invoicesrpc.SettleInvoiceResp (*SubscribeSingleInvoiceRequest)(nil), // 7: invoicesrpc.SubscribeSingleInvoiceRequest (*LookupInvoiceMsg)(nil), // 8: invoicesrpc.LookupInvoiceMsg - (*lnrpc.RouteHint)(nil), // 9: lnrpc.RouteHint - (*lnrpc.Invoice)(nil), // 10: lnrpc.Invoice + (*CircuitKey)(nil), // 9: invoicesrpc.CircuitKey + (*HtlcModifyRequest)(nil), // 10: invoicesrpc.HtlcModifyRequest + (*HtlcModifyResponse)(nil), // 11: invoicesrpc.HtlcModifyResponse + nil, // 12: invoicesrpc.HtlcModifyRequest.ExitHtlcWireCustomRecordsEntry + (*lnrpc.RouteHint)(nil), // 13: lnrpc.RouteHint + (*lnrpc.Invoice)(nil), // 14: lnrpc.Invoice } var file_invoicesrpc_invoices_proto_depIdxs = []int32{ - 9, // 0: invoicesrpc.AddHoldInvoiceRequest.route_hints:type_name -> lnrpc.RouteHint + 13, // 0: invoicesrpc.AddHoldInvoiceRequest.route_hints:type_name -> lnrpc.RouteHint 0, // 1: invoicesrpc.LookupInvoiceMsg.lookup_modifier:type_name -> invoicesrpc.LookupModifier - 7, // 2: invoicesrpc.Invoices.SubscribeSingleInvoice:input_type -> invoicesrpc.SubscribeSingleInvoiceRequest - 1, // 3: invoicesrpc.Invoices.CancelInvoice:input_type -> invoicesrpc.CancelInvoiceMsg - 3, // 4: invoicesrpc.Invoices.AddHoldInvoice:input_type -> invoicesrpc.AddHoldInvoiceRequest - 5, // 5: invoicesrpc.Invoices.SettleInvoice:input_type -> invoicesrpc.SettleInvoiceMsg - 8, // 6: invoicesrpc.Invoices.LookupInvoiceV2:input_type -> invoicesrpc.LookupInvoiceMsg - 10, // 7: invoicesrpc.Invoices.SubscribeSingleInvoice:output_type -> lnrpc.Invoice - 2, // 8: invoicesrpc.Invoices.CancelInvoice:output_type -> invoicesrpc.CancelInvoiceResp - 4, // 9: invoicesrpc.Invoices.AddHoldInvoice:output_type -> invoicesrpc.AddHoldInvoiceResp - 6, // 10: invoicesrpc.Invoices.SettleInvoice:output_type -> invoicesrpc.SettleInvoiceResp - 10, // 11: invoicesrpc.Invoices.LookupInvoiceV2:output_type -> lnrpc.Invoice - 7, // [7:12] is the sub-list for method output_type - 2, // [2:7] is the sub-list for method input_type - 2, // [2:2] is the sub-list for extension type_name - 2, // [2:2] is the sub-list for extension extendee - 0, // [0:2] is the sub-list for field type_name + 14, // 2: invoicesrpc.HtlcModifyRequest.invoice:type_name -> lnrpc.Invoice + 9, // 3: invoicesrpc.HtlcModifyRequest.exit_htlc_circuit_key:type_name -> invoicesrpc.CircuitKey + 12, // 4: invoicesrpc.HtlcModifyRequest.exit_htlc_wire_custom_records:type_name -> invoicesrpc.HtlcModifyRequest.ExitHtlcWireCustomRecordsEntry + 9, // 5: invoicesrpc.HtlcModifyResponse.circuit_key:type_name -> invoicesrpc.CircuitKey + 7, // 6: invoicesrpc.Invoices.SubscribeSingleInvoice:input_type -> invoicesrpc.SubscribeSingleInvoiceRequest + 1, // 7: invoicesrpc.Invoices.CancelInvoice:input_type -> invoicesrpc.CancelInvoiceMsg + 3, // 8: invoicesrpc.Invoices.AddHoldInvoice:input_type -> invoicesrpc.AddHoldInvoiceRequest + 5, // 9: invoicesrpc.Invoices.SettleInvoice:input_type -> invoicesrpc.SettleInvoiceMsg + 8, // 10: invoicesrpc.Invoices.LookupInvoiceV2:input_type -> invoicesrpc.LookupInvoiceMsg + 11, // 11: invoicesrpc.Invoices.HtlcModifier:input_type -> invoicesrpc.HtlcModifyResponse + 14, // 12: invoicesrpc.Invoices.SubscribeSingleInvoice:output_type -> lnrpc.Invoice + 2, // 13: invoicesrpc.Invoices.CancelInvoice:output_type -> invoicesrpc.CancelInvoiceResp + 4, // 14: invoicesrpc.Invoices.AddHoldInvoice:output_type -> invoicesrpc.AddHoldInvoiceResp + 6, // 15: invoicesrpc.Invoices.SettleInvoice:output_type -> invoicesrpc.SettleInvoiceResp + 14, // 16: invoicesrpc.Invoices.LookupInvoiceV2:output_type -> lnrpc.Invoice + 10, // 17: invoicesrpc.Invoices.HtlcModifier:output_type -> invoicesrpc.HtlcModifyRequest + 12, // [12:18] is the sub-list for method output_type + 6, // [6:12] is the sub-list for method input_type + 6, // [6:6] is the sub-list for extension type_name + 6, // [6:6] is the sub-list for extension extendee + 0, // [0:6] is the sub-list for field type_name } func init() { file_invoicesrpc_invoices_proto_init() } @@ -864,19 +1133,56 @@ func file_invoicesrpc_invoices_proto_init() { return nil } } + file_invoicesrpc_invoices_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*CircuitKey); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_invoicesrpc_invoices_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*HtlcModifyRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_invoicesrpc_invoices_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*HtlcModifyResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } } file_invoicesrpc_invoices_proto_msgTypes[7].OneofWrappers = []interface{}{ (*LookupInvoiceMsg_PaymentHash)(nil), (*LookupInvoiceMsg_PaymentAddr)(nil), (*LookupInvoiceMsg_SetId)(nil), } + file_invoicesrpc_invoices_proto_msgTypes[10].OneofWrappers = []interface{}{} type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_invoicesrpc_invoices_proto_rawDesc, NumEnums: 1, - NumMessages: 8, + NumMessages: 12, NumExtensions: 0, NumServices: 1, }, diff --git a/lnrpc/invoicesrpc/invoices.pb.gw.go b/lnrpc/invoicesrpc/invoices.pb.gw.go index 575f4849eb..530f2a347a 100644 --- a/lnrpc/invoicesrpc/invoices.pb.gw.go +++ b/lnrpc/invoicesrpc/invoices.pb.gw.go @@ -203,6 +203,58 @@ func local_request_Invoices_LookupInvoiceV2_0(ctx context.Context, marshaler run } +func request_Invoices_HtlcModifier_0(ctx context.Context, marshaler runtime.Marshaler, client InvoicesClient, req *http.Request, pathParams map[string]string) (Invoices_HtlcModifierClient, runtime.ServerMetadata, error) { + var metadata runtime.ServerMetadata + stream, err := client.HtlcModifier(ctx) + if err != nil { + grpclog.Infof("Failed to start streaming: %v", err) + return nil, metadata, err + } + dec := marshaler.NewDecoder(req.Body) + handleSend := func() error { + var protoReq HtlcModifyResponse + err := dec.Decode(&protoReq) + if err == io.EOF { + return err + } + if err != nil { + grpclog.Infof("Failed to decode request: %v", err) + return err + } + if err := stream.Send(&protoReq); err != nil { + grpclog.Infof("Failed to send request: %v", err) + return err + } + return nil + } + if err := handleSend(); err != nil { + if cerr := stream.CloseSend(); cerr != nil { + grpclog.Infof("Failed to terminate client stream: %v", cerr) + } + if err == io.EOF { + return stream, metadata, nil + } + return nil, metadata, err + } + go func() { + for { + if err := handleSend(); err != nil { + break + } + } + if err := stream.CloseSend(); err != nil { + grpclog.Infof("Failed to terminate client stream: %v", err) + } + }() + header, err := stream.Header() + if err != nil { + grpclog.Infof("Failed to get header from client: %v", err) + return nil, metadata, err + } + metadata.HeaderMD = header + return stream, metadata, nil +} + // RegisterInvoicesHandlerServer registers the http handlers for service Invoices to "mux". // UnaryRPC :call InvoicesServer directly. // StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906. @@ -308,6 +360,13 @@ func RegisterInvoicesHandlerServer(ctx context.Context, mux *runtime.ServeMux, s }) + mux.Handle("POST", pattern_Invoices_HtlcModifier_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + err := status.Error(codes.Unimplemented, "streaming calls are not yet supported in the in-process transport") + _, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + }) + return nil } @@ -449,6 +508,26 @@ func RegisterInvoicesHandlerClient(ctx context.Context, mux *runtime.ServeMux, c }) + mux.Handle("POST", pattern_Invoices_HtlcModifier_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateContext(ctx, mux, req, "/invoicesrpc.Invoices/HtlcModifier", runtime.WithHTTPPathPattern("/v2/invoices/htlcmodifier")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_Invoices_HtlcModifier_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Invoices_HtlcModifier_0(ctx, mux, outboundMarshaler, w, req, func() (proto.Message, error) { return resp.Recv() }, mux.GetForwardResponseOptions()...) + + }) + return nil } @@ -462,6 +541,8 @@ var ( pattern_Invoices_SettleInvoice_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v2", "invoices", "settle"}, "")) pattern_Invoices_LookupInvoiceV2_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v2", "invoices", "lookup"}, "")) + + pattern_Invoices_HtlcModifier_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v2", "invoices", "htlcmodifier"}, "")) ) var ( @@ -474,4 +555,6 @@ var ( forward_Invoices_SettleInvoice_0 = runtime.ForwardResponseMessage forward_Invoices_LookupInvoiceV2_0 = runtime.ForwardResponseMessage + + forward_Invoices_HtlcModifier_0 = runtime.ForwardResponseStream ) diff --git a/lnrpc/invoicesrpc/invoices.proto b/lnrpc/invoicesrpc/invoices.proto index 7afffba4e3..539645f4af 100644 --- a/lnrpc/invoicesrpc/invoices.proto +++ b/lnrpc/invoicesrpc/invoices.proto @@ -55,10 +55,19 @@ service Invoices { rpc SettleInvoice (SettleInvoiceMsg) returns (SettleInvoiceResp); /* - LookupInvoiceV2 attempts to look up at invoice. An invoice can be refrenced + LookupInvoiceV2 attempts to look up at invoice. An invoice can be referenced using either its payment hash, payment address, or set ID. */ rpc LookupInvoiceV2 (LookupInvoiceMsg) returns (lnrpc.Invoice); + + /* + HtlcModifier is a bidirectional streaming RPC that allows a client to + intercept and modify the HTLCs that attempt to settle the given invoice. The + server will send HTLCs of invoices to the client and the client can modify + some aspects of the HTLC in order to pass the invoice acceptance tests. + */ + rpc HtlcModifier (stream HtlcModifyResponse) + returns (stream HtlcModifyRequest); } message CancelInvoiceMsg { @@ -192,3 +201,45 @@ message LookupInvoiceMsg { LookupModifier lookup_modifier = 4; } + +// CircuitKey is a unique identifier for an HTLC. +message CircuitKey { + // The id of the channel that the is part of this circuit. + uint64 chan_id = 1; + + // The index of the incoming htlc in the incoming channel. + uint64 htlc_id = 2; +} + +message HtlcModifyRequest { + // The invoice the intercepted HTLC is attempting to settle. The HTLCs in + // the invoice are only HTLCs that have already been accepted or settled, + // not including the current intercepted HTLC. + lnrpc.Invoice invoice = 1; + + // The unique identifier of the HTLC of this intercepted HTLC. + CircuitKey exit_htlc_circuit_key = 2; + + // The amount in milli-satoshi that the exit HTLC is attempting to pay. + uint64 exit_htlc_amt = 3; + + // The absolute expiry height of the exit HTLC. + uint32 exit_htlc_expiry = 4; + + // The current block height. + uint32 current_height = 5; + + // The wire message custom records of the exit HTLC. + map exit_htlc_wire_custom_records = 6; +} + +message HtlcModifyResponse { + // The circuit key of the HTLC that the client wants to modify. + CircuitKey circuit_key = 1; + + // The modified amount in milli-satoshi that the exit HTLC is paying. This + // value can be different from the actual on-chain HTLC amount, in case the + // HTLC carries other valuable items, as can be the case with custom channel + // types. + optional uint64 amt_paid = 2; +} diff --git a/lnrpc/invoicesrpc/invoices.swagger.json b/lnrpc/invoicesrpc/invoices.swagger.json index 43fdf2b87d..a51b102fbd 100644 --- a/lnrpc/invoicesrpc/invoices.swagger.json +++ b/lnrpc/invoicesrpc/invoices.swagger.json @@ -82,9 +82,52 @@ ] } }, + "/v2/invoices/htlcmodifier": { + "post": { + "summary": "HtlcModifier is a bidirectional streaming RPC that allows a client to\nintercept and modify the HTLCs that attempt to settle the given invoice. The\nserver will send HTLCs of invoices to the client and the client can modify\nsome aspects of the HTLC in order to pass the invoice acceptance tests.", + "operationId": "Invoices_HtlcModifier", + "responses": { + "200": { + "description": "A successful response.(streaming responses)", + "schema": { + "type": "object", + "properties": { + "result": { + "$ref": "#/definitions/invoicesrpcHtlcModifyRequest" + }, + "error": { + "$ref": "#/definitions/rpcStatus" + } + }, + "title": "Stream result of invoicesrpcHtlcModifyRequest" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + } + }, + "parameters": [ + { + "name": "body", + "description": " (streaming inputs)", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/invoicesrpcHtlcModifyResponse" + } + } + ], + "tags": [ + "Invoices" + ] + } + }, "/v2/invoices/lookup": { "get": { - "summary": "LookupInvoiceV2 attempts to look up at invoice. An invoice can be refrenced\nusing either its payment hash, payment address, or set ID.", + "summary": "LookupInvoiceV2 attempts to look up at invoice. An invoice can be referenced\nusing either its payment hash, payment address, or set ID.", "operationId": "Invoices_LookupInvoiceV2", "responses": { "200": { @@ -317,6 +360,72 @@ "invoicesrpcCancelInvoiceResp": { "type": "object" }, + "invoicesrpcCircuitKey": { + "type": "object", + "properties": { + "chan_id": { + "type": "string", + "format": "uint64", + "description": "The id of the channel that the is part of this circuit." + }, + "htlc_id": { + "type": "string", + "format": "uint64", + "description": "The index of the incoming htlc in the incoming channel." + } + }, + "description": "CircuitKey is a unique identifier for an HTLC." + }, + "invoicesrpcHtlcModifyRequest": { + "type": "object", + "properties": { + "invoice": { + "$ref": "#/definitions/lnrpcInvoice", + "description": "The invoice the intercepted HTLC is attempting to settle. The HTLCs in\nthe invoice are only HTLCs that have already been accepted or settled,\nnot including the current intercepted HTLC." + }, + "exit_htlc_circuit_key": { + "$ref": "#/definitions/invoicesrpcCircuitKey", + "description": "The unique identifier of the HTLC of this intercepted HTLC." + }, + "exit_htlc_amt": { + "type": "string", + "format": "uint64", + "description": "The amount in milli-satoshi that the exit HTLC is attempting to pay." + }, + "exit_htlc_expiry": { + "type": "integer", + "format": "int64", + "description": "The absolute expiry height of the exit HTLC." + }, + "current_height": { + "type": "integer", + "format": "int64", + "description": "The current block height." + }, + "exit_htlc_wire_custom_records": { + "type": "object", + "additionalProperties": { + "type": "string", + "format": "byte" + }, + "description": "The wire message custom records of the exit HTLC." + } + } + }, + "invoicesrpcHtlcModifyResponse": { + "type": "object", + "properties": { + "circuit_key": { + "$ref": "#/definitions/invoicesrpcCircuitKey", + "description": "The circuit key of the HTLC that the client wants to modify." + }, + "amt_paid": { + "type": "string", + "format": "uint64", + "description": "The modified amount in milli-satoshi that the exit HTLC is paying. This\nvalue can be different from the actual on-chain HTLC amount, in case the\nHTLC carries other valuable items, as can be the case with custom channel\ntypes." + } + } + }, "invoicesrpcLookupModifier": { "type": "string", "enum": [ diff --git a/lnrpc/invoicesrpc/invoices.yaml b/lnrpc/invoicesrpc/invoices.yaml index ef62d7f031..66d53153e9 100644 --- a/lnrpc/invoicesrpc/invoices.yaml +++ b/lnrpc/invoicesrpc/invoices.yaml @@ -16,3 +16,6 @@ http: body: "*" - selector: invoicesrpc.Invoices.LookupInvoiceV2 get: "/v2/invoices/lookup" + - selector: invoicesrpc.Invoices.HtlcModifier + post: "/v2/invoices/htlcmodifier" + body: "*" \ No newline at end of file diff --git a/lnrpc/invoicesrpc/invoices_grpc.pb.go b/lnrpc/invoicesrpc/invoices_grpc.pb.go index bc2f24ac10..a8e6d187e9 100644 --- a/lnrpc/invoicesrpc/invoices_grpc.pb.go +++ b/lnrpc/invoicesrpc/invoices_grpc.pb.go @@ -36,9 +36,14 @@ type InvoicesClient interface { // SettleInvoice settles an accepted invoice. If the invoice is already // settled, this call will succeed. SettleInvoice(ctx context.Context, in *SettleInvoiceMsg, opts ...grpc.CallOption) (*SettleInvoiceResp, error) - // LookupInvoiceV2 attempts to look up at invoice. An invoice can be refrenced + // LookupInvoiceV2 attempts to look up at invoice. An invoice can be referenced // using either its payment hash, payment address, or set ID. LookupInvoiceV2(ctx context.Context, in *LookupInvoiceMsg, opts ...grpc.CallOption) (*lnrpc.Invoice, error) + // HtlcModifier is a bidirectional streaming RPC that allows a client to + // intercept and modify the HTLCs that attempt to settle the given invoice. The + // server will send HTLCs of invoices to the client and the client can modify + // some aspects of the HTLC in order to pass the invoice acceptance tests. + HtlcModifier(ctx context.Context, opts ...grpc.CallOption) (Invoices_HtlcModifierClient, error) } type invoicesClient struct { @@ -117,6 +122,37 @@ func (c *invoicesClient) LookupInvoiceV2(ctx context.Context, in *LookupInvoiceM return out, nil } +func (c *invoicesClient) HtlcModifier(ctx context.Context, opts ...grpc.CallOption) (Invoices_HtlcModifierClient, error) { + stream, err := c.cc.NewStream(ctx, &Invoices_ServiceDesc.Streams[1], "/invoicesrpc.Invoices/HtlcModifier", opts...) + if err != nil { + return nil, err + } + x := &invoicesHtlcModifierClient{stream} + return x, nil +} + +type Invoices_HtlcModifierClient interface { + Send(*HtlcModifyResponse) error + Recv() (*HtlcModifyRequest, error) + grpc.ClientStream +} + +type invoicesHtlcModifierClient struct { + grpc.ClientStream +} + +func (x *invoicesHtlcModifierClient) Send(m *HtlcModifyResponse) error { + return x.ClientStream.SendMsg(m) +} + +func (x *invoicesHtlcModifierClient) Recv() (*HtlcModifyRequest, error) { + m := new(HtlcModifyRequest) + if err := x.ClientStream.RecvMsg(m); err != nil { + return nil, err + } + return m, nil +} + // InvoicesServer is the server API for Invoices service. // All implementations must embed UnimplementedInvoicesServer // for forward compatibility @@ -138,9 +174,14 @@ type InvoicesServer interface { // SettleInvoice settles an accepted invoice. If the invoice is already // settled, this call will succeed. SettleInvoice(context.Context, *SettleInvoiceMsg) (*SettleInvoiceResp, error) - // LookupInvoiceV2 attempts to look up at invoice. An invoice can be refrenced + // LookupInvoiceV2 attempts to look up at invoice. An invoice can be referenced // using either its payment hash, payment address, or set ID. LookupInvoiceV2(context.Context, *LookupInvoiceMsg) (*lnrpc.Invoice, error) + // HtlcModifier is a bidirectional streaming RPC that allows a client to + // intercept and modify the HTLCs that attempt to settle the given invoice. The + // server will send HTLCs of invoices to the client and the client can modify + // some aspects of the HTLC in order to pass the invoice acceptance tests. + HtlcModifier(Invoices_HtlcModifierServer) error mustEmbedUnimplementedInvoicesServer() } @@ -163,6 +204,9 @@ func (UnimplementedInvoicesServer) SettleInvoice(context.Context, *SettleInvoice func (UnimplementedInvoicesServer) LookupInvoiceV2(context.Context, *LookupInvoiceMsg) (*lnrpc.Invoice, error) { return nil, status.Errorf(codes.Unimplemented, "method LookupInvoiceV2 not implemented") } +func (UnimplementedInvoicesServer) HtlcModifier(Invoices_HtlcModifierServer) error { + return status.Errorf(codes.Unimplemented, "method HtlcModifier not implemented") +} func (UnimplementedInvoicesServer) mustEmbedUnimplementedInvoicesServer() {} // UnsafeInvoicesServer may be embedded to opt out of forward compatibility for this service. @@ -269,6 +313,32 @@ func _Invoices_LookupInvoiceV2_Handler(srv interface{}, ctx context.Context, dec return interceptor(ctx, in, info, handler) } +func _Invoices_HtlcModifier_Handler(srv interface{}, stream grpc.ServerStream) error { + return srv.(InvoicesServer).HtlcModifier(&invoicesHtlcModifierServer{stream}) +} + +type Invoices_HtlcModifierServer interface { + Send(*HtlcModifyRequest) error + Recv() (*HtlcModifyResponse, error) + grpc.ServerStream +} + +type invoicesHtlcModifierServer struct { + grpc.ServerStream +} + +func (x *invoicesHtlcModifierServer) Send(m *HtlcModifyRequest) error { + return x.ServerStream.SendMsg(m) +} + +func (x *invoicesHtlcModifierServer) Recv() (*HtlcModifyResponse, error) { + m := new(HtlcModifyResponse) + if err := x.ServerStream.RecvMsg(m); err != nil { + return nil, err + } + return m, nil +} + // Invoices_ServiceDesc is the grpc.ServiceDesc for Invoices service. // It's only intended for direct use with grpc.RegisterService, // and not to be introspected or modified (even as a copy) @@ -299,6 +369,12 @@ var Invoices_ServiceDesc = grpc.ServiceDesc{ Handler: _Invoices_SubscribeSingleInvoice_Handler, ServerStreams: true, }, + { + StreamName: "HtlcModifier", + Handler: _Invoices_HtlcModifier_Handler, + ServerStreams: true, + ClientStreams: true, + }, }, Metadata: "invoicesrpc/invoices.proto", } diff --git a/lnrpc/invoicesrpc/invoices_server.go b/lnrpc/invoicesrpc/invoices_server.go index 23b8722caf..d18ab09d29 100644 --- a/lnrpc/invoicesrpc/invoices_server.go +++ b/lnrpc/invoicesrpc/invoices_server.go @@ -30,6 +30,9 @@ const ( ) var ( + // ErrServerShuttingDown is returned when the server is shutting down. + ErrServerShuttingDown = errors.New("server shutting down") + // macaroonOps are the set of capabilities that our minted macaroon (if // it doesn't already exist) will have. macaroonOps = []bakery.Op{ @@ -65,6 +68,10 @@ var ( Entity: "invoices", Action: "write", }}, + "/invoicesrpc.Invoices/HtlcModifier": {{ + Entity: "invoices", + Action: "write", + }}, } // DefaultInvoicesMacFilename is the default name of the invoices @@ -446,3 +453,36 @@ func (s *Server) LookupInvoiceV2(ctx context.Context, return CreateRPCInvoice(&invoice, s.cfg.ChainParams) } + +// HtlcModifier is a bidirectional streaming RPC that allows a client to +// intercept and modify the HTLCs that attempt to settle the given invoice. The +// server will send HTLCs of invoices to the client and the client can modify +// some aspects of the HTLC in order to pass the invoice acceptance tests. +func (s *Server) HtlcModifier( + modifierServer Invoices_HtlcModifierServer) error { + + modifier := newHtlcModifier(s.cfg.ChainParams, modifierServer) + reset, modifierQuit, err := s.cfg.HtlcModifier.RegisterInterceptor( + modifier.onIntercept, + ) + if err != nil { + return fmt.Errorf("cannot register interceptor: %w", err) + } + + defer reset() + + log.Debugf("Invoice HTLC modifier client connected") + + for { + select { + case <-modifierServer.Context().Done(): + return modifierServer.Context().Err() + + case <-modifierQuit: + return ErrServerShuttingDown + + case <-s.quit: + return ErrServerShuttingDown + } + } +} From ce7c053de04f54b955b69ea5703a2cea87e93660 Mon Sep 17 00:00:00 2001 From: ffranr Date: Tue, 30 Apr 2024 17:51:43 +0100 Subject: [PATCH 123/218] lntest: add `HtlcModifier` support to node RPC harness This commit enhances the itest LND node harness to include support for the new `HtlcModifier` RPC endpoint. At the same time we move another method to the correct file. --- lntest/harness.go | 39 +++++++++++++++++++++++++++++++++++++-- lntest/rpc/invoices.go | 24 ++++++++++++------------ lntest/rpc/router.go | 16 ++++++++++++++++ 3 files changed, 65 insertions(+), 14 deletions(-) diff --git a/lntest/harness.go b/lntest/harness.go index e8996e9a64..79c1f61ff7 100644 --- a/lntest/harness.go +++ b/lntest/harness.go @@ -16,6 +16,7 @@ import ( "github.com/lightningnetwork/lnd/fn" "github.com/lightningnetwork/lnd/kvdb/etcd" "github.com/lightningnetwork/lnd/lnrpc" + "github.com/lightningnetwork/lnd/lnrpc/invoicesrpc" "github.com/lightningnetwork/lnd/lnrpc/routerrpc" "github.com/lightningnetwork/lnd/lnrpc/walletrpc" "github.com/lightningnetwork/lnd/lntest/miner" @@ -2071,8 +2072,42 @@ func (h *HarnessTest) ReceiveHtlcInterceptor( require.Fail(h, "timeout", "timeout intercepting htlc") case err := <-errChan: - require.Failf(h, "err from stream", - "received err from stream: %v", err) + require.Failf(h, "err from HTLC interceptor stream", + "received err from HTLC interceptor stream: %v", err) + + case updateMsg := <-chanMsg: + return updateMsg + } + + return nil +} + +// ReceiveInvoiceHtlcModification waits until a message is received on the +// invoice HTLC modifier stream or the timeout is reached. +func (h *HarnessTest) ReceiveInvoiceHtlcModification( + stream rpc.InvoiceHtlcModifierClient) *invoicesrpc.HtlcModifyRequest { + + chanMsg := make(chan *invoicesrpc.HtlcModifyRequest) + errChan := make(chan error) + go func() { + // Consume one message. This will block until the message is + // received. + resp, err := stream.Recv() + if err != nil { + errChan <- err + return + } + chanMsg <- resp + }() + + select { + case <-time.After(DefaultTimeout): + require.Fail(h, "timeout", "timeout invoice HTLC modifier") + + case err := <-errChan: + require.Failf(h, "err from invoice HTLC modifier stream", + "received err from invoice HTLC modifier stream: %v", + err) case updateMsg := <-chanMsg: return updateMsg diff --git a/lntest/rpc/invoices.go b/lntest/rpc/invoices.go index 5b771968e8..a5c560897f 100644 --- a/lntest/rpc/invoices.go +++ b/lntest/rpc/invoices.go @@ -5,7 +5,6 @@ import ( "github.com/lightningnetwork/lnd/lnrpc" "github.com/lightningnetwork/lnd/lnrpc/invoicesrpc" - "github.com/lightningnetwork/lnd/lnrpc/routerrpc" ) // ===================== @@ -84,18 +83,19 @@ func (h *HarnessRPC) SubscribeSingleInvoice(rHash []byte) SingleInvoiceClient { return client } -type TrackPaymentClient routerrpc.Router_TrackPaymentV2Client +type InvoiceHtlcModifierClient invoicesrpc.Invoices_HtlcModifierClient -// TrackPaymentV2 creates a subscription client for given invoice and -// asserts its creation. -func (h *HarnessRPC) TrackPaymentV2(payHash []byte) TrackPaymentClient { - req := &routerrpc.TrackPaymentRequest{PaymentHash: payHash} +// InvoiceHtlcModifier makes an RPC call to the node's RouterClient and asserts. +func (h *HarnessRPC) InvoiceHtlcModifier() (InvoiceHtlcModifierClient, + context.CancelFunc) { - // TrackPaymentV2 needs to have the context alive for the entire test - // case as the returned client will be used for send and receive events - // stream. Thus we use runCtx here instead of a timeout context. - client, err := h.Router.TrackPaymentV2(h.runCtx, req) - h.NoError(err, "TrackPaymentV2") + // InvoiceHtlcModifier needs to have the context alive for the entire + // test case as the returned client will be used for send and receive + // events stream. Therefore, we use cancel context here instead of a + // timeout context. + ctxt, cancel := context.WithCancel(h.runCtx) + resp, err := h.Invoice.HtlcModifier(ctxt) + h.NoError(err, "InvoiceHtlcModifier") - return client + return resp, cancel } diff --git a/lntest/rpc/router.go b/lntest/rpc/router.go index 7c3f2e7c46..707c43a11d 100644 --- a/lntest/rpc/router.go +++ b/lntest/rpc/router.go @@ -267,3 +267,19 @@ func (h *HarnessRPC) TrackPayments( return resp } + +type TrackPaymentClient routerrpc.Router_TrackPaymentV2Client + +// TrackPaymentV2 creates a subscription client for given invoice and +// asserts its creation. +func (h *HarnessRPC) TrackPaymentV2(payHash []byte) TrackPaymentClient { + req := &routerrpc.TrackPaymentRequest{PaymentHash: payHash} + + // TrackPaymentV2 needs to have the context alive for the entire test + // case as the returned client will be used for send and receive events + // stream. Thus we use runCtx here instead of a timeout context. + client, err := h.Router.TrackPaymentV2(h.runCtx, req) + h.NoError(err, "TrackPaymentV2") + + return client +} From 109e27cfb2b8135c0dafdb38a69f2f86d43031cf Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Thu, 12 Sep 2024 17:45:26 +0200 Subject: [PATCH 124/218] lnwire: add MergedCopy method to CustomRecords --- lnwire/custom_records.go | 16 +++++++++++ lnwire/custom_records_test.go | 52 +++++++++++++++++++++++++++++++++++ 2 files changed, 68 insertions(+) diff --git a/lnwire/custom_records.go b/lnwire/custom_records.go index f0f59185e9..8177cbe821 100644 --- a/lnwire/custom_records.go +++ b/lnwire/custom_records.go @@ -89,6 +89,22 @@ func (c CustomRecords) Copy() CustomRecords { return customRecords } +// MergedCopy creates a copy of the records and merges them with the given +// records. If the same key is present in both sets, the value from the other +// records will be used. +func (c CustomRecords) MergedCopy(other CustomRecords) CustomRecords { + copiedRecords := make(CustomRecords, len(c)) + for k, v := range c { + copiedRecords[k] = v + } + + for k, v := range other { + copiedRecords[k] = v + } + + return copiedRecords +} + // ExtendRecordProducers extends the given records slice with the custom // records. The resultant records slice will be sorted if the given records // slice contains TLV types greater than or equal to MinCustomRecordsTlvType. diff --git a/lnwire/custom_records_test.go b/lnwire/custom_records_test.go index 1d30e21000..8ff6af10ba 100644 --- a/lnwire/custom_records_test.go +++ b/lnwire/custom_records_test.go @@ -6,6 +6,7 @@ import ( "github.com/lightningnetwork/lnd/fn" "github.com/lightningnetwork/lnd/tlv" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -194,3 +195,54 @@ func serializeRecordProducers(t *testing.T, return b.Bytes() } + +func TestCustomRecordsMergedCopy(t *testing.T) { + tests := []struct { + name string + c CustomRecords + other CustomRecords + want CustomRecords + }{ + { + name: "nil records", + want: make(CustomRecords), + }, + { + name: "empty records", + c: make(CustomRecords), + other: make(CustomRecords), + want: make(CustomRecords), + }, + { + name: "distinct records", + c: CustomRecords{ + 1: {1, 2, 3}, + }, + other: CustomRecords{ + 2: {4, 5, 6}, + }, + want: CustomRecords{ + 1: {1, 2, 3}, + 2: {4, 5, 6}, + }, + }, + { + name: "same records, different values", + c: CustomRecords{ + 1: {1, 2, 3}, + }, + other: CustomRecords{ + 1: {4, 5, 6}, + }, + want: CustomRecords{ + 1: {4, 5, 6}, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result := tt.c.MergedCopy(tt.other) + assert.Equal(t, tt.want, result) + }) + } +} From c1aa44c476d0dc249b144be4e9d6e4612b6c6efe Mon Sep 17 00:00:00 2001 From: ffranr Date: Wed, 8 May 2024 18:17:05 +0100 Subject: [PATCH 125/218] multi: pass `UpdateAddHtlc` message custom records to invoice modifier --- .../htlc_incoming_contest_resolver.go | 2 +- contractcourt/interfaces.go | 1 + contractcourt/mock_registry_test.go | 1 + htlcswitch/interfaces.go | 1 + htlcswitch/link.go | 2 +- htlcswitch/mock.go | 5 +- invoices/invoiceregistry.go | 3 + invoices/invoiceregistry_test.go | 99 +-- invoices/invoices.go | 6 + invoices/update.go | 48 +- invoices/update_invoice_test.go | 667 ++++++++++-------- 11 files changed, 481 insertions(+), 354 deletions(-) diff --git a/contractcourt/htlc_incoming_contest_resolver.go b/contractcourt/htlc_incoming_contest_resolver.go index b104f8d70a..6bda4e398b 100644 --- a/contractcourt/htlc_incoming_contest_resolver.go +++ b/contractcourt/htlc_incoming_contest_resolver.go @@ -308,7 +308,7 @@ func (h *htlcIncomingContestResolver) Resolve( resolution, err := h.Registry.NotifyExitHopHtlc( h.htlc.RHash, h.htlc.Amt, h.htlcExpiry, currentHeight, - circuitKey, hodlQueue.ChanIn(), payload, + circuitKey, hodlQueue.ChanIn(), nil, payload, ) if err != nil { return nil, err diff --git a/contractcourt/interfaces.go b/contractcourt/interfaces.go index 0d53b07b66..90ad2b1c87 100644 --- a/contractcourt/interfaces.go +++ b/contractcourt/interfaces.go @@ -30,6 +30,7 @@ type Registry interface { NotifyExitHopHtlc(payHash lntypes.Hash, paidAmount lnwire.MilliSatoshi, expiry uint32, currentHeight int32, circuitKey models.CircuitKey, hodlChan chan<- interface{}, + wireCustomRecords lnwire.CustomRecords, payload invoices.Payload) (invoices.HtlcResolution, error) // HodlUnsubscribeAll unsubscribes from all htlc resolutions. diff --git a/contractcourt/mock_registry_test.go b/contractcourt/mock_registry_test.go index 7acac67ec5..5c75185623 100644 --- a/contractcourt/mock_registry_test.go +++ b/contractcourt/mock_registry_test.go @@ -26,6 +26,7 @@ type mockRegistry struct { func (r *mockRegistry) NotifyExitHopHtlc(payHash lntypes.Hash, paidAmount lnwire.MilliSatoshi, expiry uint32, currentHeight int32, circuitKey models.CircuitKey, hodlChan chan<- interface{}, + wireCustomRecords lnwire.CustomRecords, payload invoices.Payload) (invoices.HtlcResolution, error) { r.notifyChan <- notifyExitHopData{ diff --git a/htlcswitch/interfaces.go b/htlcswitch/interfaces.go index 4e6384b978..72143bc45a 100644 --- a/htlcswitch/interfaces.go +++ b/htlcswitch/interfaces.go @@ -33,6 +33,7 @@ type InvoiceDatabase interface { NotifyExitHopHtlc(payHash lntypes.Hash, paidAmount lnwire.MilliSatoshi, expiry uint32, currentHeight int32, circuitKey models.CircuitKey, hodlChan chan<- interface{}, + wireCustomRecords lnwire.CustomRecords, payload invoices.Payload) (invoices.HtlcResolution, error) // CancelInvoice attempts to cancel the invoice corresponding to the diff --git a/htlcswitch/link.go b/htlcswitch/link.go index 7f0b3eb8cc..0fb970d59b 100644 --- a/htlcswitch/link.go +++ b/htlcswitch/link.go @@ -3845,7 +3845,7 @@ func (l *channelLink) processExitHop(add lnwire.UpdateAddHTLC, event, err := l.cfg.Registry.NotifyExitHopHtlc( invoiceHash, add.Amount, add.Expiry, int32(heightNow), - circuitKey, l.hodlQueue.ChanIn(), payload, + circuitKey, l.hodlQueue.ChanIn(), add.CustomRecords, payload, ) if err != nil { return err diff --git a/htlcswitch/mock.go b/htlcswitch/mock.go index aea0013741..750bdf784f 100644 --- a/htlcswitch/mock.go +++ b/htlcswitch/mock.go @@ -1049,11 +1049,12 @@ func (i *mockInvoiceRegistry) SettleHodlInvoice( func (i *mockInvoiceRegistry) NotifyExitHopHtlc(rhash lntypes.Hash, amt lnwire.MilliSatoshi, expiry uint32, currentHeight int32, circuitKey models.CircuitKey, hodlChan chan<- interface{}, + wireCustomRecords lnwire.CustomRecords, payload invoices.Payload) (invoices.HtlcResolution, error) { event, err := i.registry.NotifyExitHopHtlc( - rhash, amt, expiry, currentHeight, circuitKey, hodlChan, - payload, + rhash, amt, expiry, currentHeight, circuitKey, + hodlChan, wireCustomRecords, payload, ) if err != nil { return nil, err diff --git a/invoices/invoiceregistry.go b/invoices/invoiceregistry.go index c7f4cdb7b4..517e237ca6 100644 --- a/invoices/invoiceregistry.go +++ b/invoices/invoiceregistry.go @@ -918,6 +918,7 @@ func (i *InvoiceRegistry) processAMP(ctx invoiceUpdateCtx) error { func (i *InvoiceRegistry) NotifyExitHopHtlc(rHash lntypes.Hash, amtPaid lnwire.MilliSatoshi, expiry uint32, currentHeight int32, circuitKey CircuitKey, hodlChan chan<- interface{}, + wireCustomRecords lnwire.CustomRecords, payload Payload) (HtlcResolution, error) { // Create the update context containing the relevant details of the @@ -929,6 +930,7 @@ func (i *InvoiceRegistry) NotifyExitHopHtlc(rHash lntypes.Hash, expiry: expiry, currentHeight: currentHeight, finalCltvRejectDelta: i.cfg.FinalCltvRejectDelta, + wireCustomRecords: wireCustomRecords, customRecords: payload.CustomRecords(), mpp: payload.MultiPath(), amp: payload.AMPRecord(), @@ -1051,6 +1053,7 @@ func (i *InvoiceRegistry) notifyExitHopHtlcLocked( // the interceptor's client an opportunity to manipulate the // settlement process. err = i.cfg.HtlcInterceptor.Intercept(HtlcModifyRequest{ + WireCustomRecords: ctx.wireCustomRecords, ExitHtlcCircuitKey: ctx.circuitKey, ExitHtlcAmt: ctx.amtPaid, ExitHtlcExpiry: ctx.expiry, diff --git a/invoices/invoiceregistry_test.go b/invoices/invoiceregistry_test.go index b7b3d574a7..3deb6405c0 100644 --- a/invoices/invoiceregistry_test.go +++ b/invoices/invoiceregistry_test.go @@ -245,7 +245,8 @@ func testSettleInvoice(t *testing.T, resolution, err := ctx.registry.NotifyExitHopHtlc( testInvoicePaymentHash, testInvoice.Terms.Value, uint32(testCurrentHeight)+testInvoiceCltvDelta-1, - testCurrentHeight, getCircuitKey(10), hodlChan, testPayload, + testCurrentHeight, getCircuitKey(10), hodlChan, + nil, testPayload, ) if err != nil { t.Fatal(err) @@ -261,7 +262,7 @@ func testSettleInvoice(t *testing.T, resolution, err = ctx.registry.NotifyExitHopHtlc( testInvoicePaymentHash, amtPaid, testHtlcExpiry, testCurrentHeight, getCircuitKey(0), hodlChan, - testPayload, + nil, testPayload, ) if err != nil { t.Fatal(err) @@ -302,7 +303,8 @@ func testSettleInvoice(t *testing.T, // behaviour after a restart. resolution, err = ctx.registry.NotifyExitHopHtlc( testInvoicePaymentHash, amtPaid, testHtlcExpiry, - testCurrentHeight, getCircuitKey(0), hodlChan, testPayload, + testCurrentHeight, getCircuitKey(0), hodlChan, + nil, testPayload, ) require.NoError(t, err, "unexpected NotifyExitHopHtlc error") require.NotNil(t, resolution) @@ -316,7 +318,8 @@ func testSettleInvoice(t *testing.T, // paid invoice that may open up a probe vector. resolution, err = ctx.registry.NotifyExitHopHtlc( testInvoicePaymentHash, amtPaid+600, testHtlcExpiry, - testCurrentHeight, getCircuitKey(1), hodlChan, testPayload, + testCurrentHeight, getCircuitKey(1), hodlChan, + nil, testPayload, ) require.NoError(t, err, "unexpected NotifyExitHopHtlc error") require.NotNil(t, resolution) @@ -331,7 +334,8 @@ func testSettleInvoice(t *testing.T, // would have failed if it were the first payment. resolution, err = ctx.registry.NotifyExitHopHtlc( testInvoicePaymentHash, amtPaid-600, testHtlcExpiry, - testCurrentHeight, getCircuitKey(2), hodlChan, testPayload, + testCurrentHeight, getCircuitKey(2), hodlChan, + nil, testPayload, ) require.NoError(t, err, "unexpected NotifyExitHopHtlc error") require.NotNil(t, resolution) @@ -468,7 +472,8 @@ func testCancelInvoiceImpl(t *testing.T, gc bool, hodlChan := make(chan interface{}) resolution, err := ctx.registry.NotifyExitHopHtlc( testInvoicePaymentHash, testInvoiceAmount, testHtlcExpiry, - testCurrentHeight, getCircuitKey(0), hodlChan, testPayload, + testCurrentHeight, getCircuitKey(0), hodlChan, + nil, testPayload, ) if err != nil { t.Fatal("expected settlement of a canceled invoice to succeed") @@ -577,7 +582,8 @@ func testSettleHoldInvoice(t *testing.T, // should be possible. resolution, err := registry.NotifyExitHopHtlc( testInvoicePaymentHash, amtPaid, testHtlcExpiry, - testCurrentHeight, getCircuitKey(0), hodlChan, testPayload, + testCurrentHeight, getCircuitKey(0), hodlChan, + nil, testPayload, ) if err != nil { t.Fatalf("expected settle to succeed but got %v", err) @@ -589,7 +595,8 @@ func testSettleHoldInvoice(t *testing.T, // Test idempotency. resolution, err = registry.NotifyExitHopHtlc( testInvoicePaymentHash, amtPaid, testHtlcExpiry, - testCurrentHeight, getCircuitKey(0), hodlChan, testPayload, + testCurrentHeight, getCircuitKey(0), hodlChan, + nil, testPayload, ) if err != nil { t.Fatalf("expected settle to succeed but got %v", err) @@ -602,7 +609,8 @@ func testSettleHoldInvoice(t *testing.T, // is a replay. resolution, err = registry.NotifyExitHopHtlc( testInvoicePaymentHash, amtPaid, testHtlcExpiry, - testCurrentHeight+10, getCircuitKey(0), hodlChan, testPayload, + testCurrentHeight+10, getCircuitKey(0), hodlChan, + nil, testPayload, ) if err != nil { t.Fatalf("expected settle to succeed but got %v", err) @@ -615,7 +623,7 @@ func testSettleHoldInvoice(t *testing.T, // requirement. It should be rejected. resolution, err = registry.NotifyExitHopHtlc( testInvoicePaymentHash, amtPaid, 1, testCurrentHeight, - getCircuitKey(1), hodlChan, testPayload, + getCircuitKey(1), hodlChan, nil, testPayload, ) if err != nil { t.Fatalf("expected settle to succeed but got %v", err) @@ -719,7 +727,8 @@ func testCancelHoldInvoice(t *testing.T, // should be possible. resolution, err := registry.NotifyExitHopHtlc( testInvoicePaymentHash, amtPaid, testHtlcExpiry, - testCurrentHeight, getCircuitKey(0), hodlChan, testPayload, + testCurrentHeight, getCircuitKey(0), hodlChan, + nil, testPayload, ) if err != nil { t.Fatalf("expected settle to succeed but got %v", err) @@ -741,7 +750,8 @@ func testCancelHoldInvoice(t *testing.T, // accept height. resolution, err = registry.NotifyExitHopHtlc( testInvoicePaymentHash, amtPaid, testHtlcExpiry, - testCurrentHeight+1, getCircuitKey(0), hodlChan, testPayload, + testCurrentHeight+1, getCircuitKey(0), hodlChan, + nil, testPayload, ) if err != nil { t.Fatalf("expected settle to succeed but got %v", err) @@ -770,7 +780,7 @@ func testUnknownInvoice(t *testing.T, amt := lnwire.MilliSatoshi(100000) resolution, err := ctx.registry.NotifyExitHopHtlc( testInvoicePaymentHash, amt, testHtlcExpiry, testCurrentHeight, - getCircuitKey(0), hodlChan, testPayload, + getCircuitKey(0), hodlChan, nil, testPayload, ) if err != nil { t.Fatal("unexpected error") @@ -831,7 +841,7 @@ func testKeySendImpl(t *testing.T, keySendEnabled bool, resolution, err := ctx.registry.NotifyExitHopHtlc( hash, amt, expiry, testCurrentHeight, getCircuitKey(10), hodlChan, - invalidKeySendPayload, + nil, invalidKeySendPayload, ) if err != nil { t.Fatal(err) @@ -852,8 +862,8 @@ func testKeySendImpl(t *testing.T, keySendEnabled bool, } resolution, err = ctx.registry.NotifyExitHopHtlc( - hash, amt, expiry, - testCurrentHeight, getCircuitKey(10), hodlChan, keySendPayload, + hash, amt, expiry, testCurrentHeight, getCircuitKey(10), + hodlChan, nil, keySendPayload, ) if err != nil { t.Fatal(err) @@ -881,8 +891,8 @@ func testKeySendImpl(t *testing.T, keySendEnabled bool, // Replay the same keysend payment. We expect an identical resolution, // but no event should be generated. resolution, err = ctx.registry.NotifyExitHopHtlc( - hash, amt, expiry, - testCurrentHeight, getCircuitKey(10), hodlChan, keySendPayload, + hash, amt, expiry, testCurrentHeight, getCircuitKey(10), + hodlChan, nil, keySendPayload, ) require.Nil(t, err) checkSettleResolution(t, resolution, preimage) @@ -905,8 +915,8 @@ func testKeySendImpl(t *testing.T, keySendEnabled bool, } resolution, err = ctx.registry.NotifyExitHopHtlc( - hash2, amt, expiry, - testCurrentHeight, getCircuitKey(20), hodlChan, keySendPayload2, + hash2, amt, expiry, testCurrentHeight, getCircuitKey(20), + hodlChan, nil, keySendPayload2, ) require.Nil(t, err) @@ -964,8 +974,8 @@ func testHoldKeysendImpl(t *testing.T, timeoutKeysend bool, } resolution, err := ctx.registry.NotifyExitHopHtlc( - hash, amt, expiry, - testCurrentHeight, getCircuitKey(10), hodlChan, keysendPayload, + hash, amt, expiry, testCurrentHeight, getCircuitKey(10), + hodlChan, nil, keysendPayload, ) if err != nil { t.Fatal(err) @@ -1047,8 +1057,8 @@ func testMppPayment(t *testing.T, hodlChan1 := make(chan interface{}, 1) resolution, err := ctx.registry.NotifyExitHopHtlc( testInvoicePaymentHash, testInvoice.Terms.Value/2, - testHtlcExpiry, - testCurrentHeight, getCircuitKey(10), hodlChan1, mppPayload, + testHtlcExpiry, testCurrentHeight, getCircuitKey(10), + hodlChan1, nil, mppPayload, ) if err != nil { t.Fatal(err) @@ -1075,8 +1085,8 @@ func testMppPayment(t *testing.T, hodlChan2 := make(chan interface{}, 1) resolution, err = ctx.registry.NotifyExitHopHtlc( testInvoicePaymentHash, testInvoice.Terms.Value/2, - testHtlcExpiry, - testCurrentHeight, getCircuitKey(11), hodlChan2, mppPayload, + testHtlcExpiry, testCurrentHeight, getCircuitKey(11), + hodlChan2, nil, mppPayload, ) if err != nil { t.Fatal(err) @@ -1089,8 +1099,8 @@ func testMppPayment(t *testing.T, hodlChan3 := make(chan interface{}, 1) resolution, err = ctx.registry.NotifyExitHopHtlc( testInvoicePaymentHash, testInvoice.Terms.Value/2, - testHtlcExpiry, - testCurrentHeight, getCircuitKey(12), hodlChan3, mppPayload, + testHtlcExpiry, testCurrentHeight, getCircuitKey(12), + hodlChan3, nil, mppPayload, ) if err != nil { t.Fatal(err) @@ -1149,7 +1159,7 @@ func testMppPaymentWithOverpayment(t *testing.T, resolution, err := ctx.registry.NotifyExitHopHtlc( testInvoicePaymentHash, testInvoice.Terms.Value/2, testHtlcExpiry, testCurrentHeight, getCircuitKey(11), - hodlChan1, mppPayload, + hodlChan1, nil, mppPayload, ) if err != nil { t.Fatal(err) @@ -1164,7 +1174,7 @@ func testMppPaymentWithOverpayment(t *testing.T, testInvoicePaymentHash, testInvoice.Terms.Value/2+overpayment, testHtlcExpiry, testCurrentHeight, getCircuitKey(12), hodlChan2, - mppPayload, + nil, mppPayload, ) if err != nil { t.Fatal(err) @@ -1449,7 +1459,7 @@ func testHeightExpiryWithRegistryImpl(t *testing.T, numParts int, settle bool, resolution, err := ctx.registry.NotifyExitHopHtlc( testInvoicePaymentHash, htlcAmt, expiry, testCurrentHeight, getCircuitKey(uint64(i)), hodlChan, - payLoad, + nil, payLoad, ) require.NoError(t, err) require.Nil(t, resolution, "did not expect direct resolution") @@ -1551,8 +1561,8 @@ func testMultipleSetHeightExpiry(t *testing.T, hodlChan1 := make(chan interface{}, 1) resolution, err := ctx.registry.NotifyExitHopHtlc( testInvoicePaymentHash, testInvoice.Terms.Value/2, - testHtlcExpiry, - testCurrentHeight, getCircuitKey(10), hodlChan1, mppPayload, + testHtlcExpiry, testCurrentHeight, getCircuitKey(10), + hodlChan1, nil, mppPayload, ) require.NoError(t, err) require.Nil(t, resolution, "did not expect direct resolution") @@ -1581,7 +1591,8 @@ func testMultipleSetHeightExpiry(t *testing.T, hodlChan2 := make(chan interface{}, 1) resolution, err = ctx.registry.NotifyExitHopHtlc( testInvoicePaymentHash, testInvoice.Terms.Value/2, expiry, - testCurrentHeight, getCircuitKey(11), hodlChan2, mppPayload, + testCurrentHeight, getCircuitKey(11), hodlChan2, + nil, mppPayload, ) require.NoError(t, err) require.Nil(t, resolution, "did not expect direct resolution") @@ -1590,7 +1601,8 @@ func testMultipleSetHeightExpiry(t *testing.T, hodlChan3 := make(chan interface{}, 1) resolution, err = ctx.registry.NotifyExitHopHtlc( testInvoicePaymentHash, testInvoice.Terms.Value/2, expiry, - testCurrentHeight, getCircuitKey(12), hodlChan3, mppPayload, + testCurrentHeight, getCircuitKey(12), hodlChan3, + nil, mppPayload, ) require.NoError(t, err) require.Nil(t, resolution, "did not expect direct resolution") @@ -1695,7 +1707,8 @@ func testSettleInvoicePaymentAddrRequired(t *testing.T, resolution, err := ctx.registry.NotifyExitHopHtlc( testInvoicePaymentHash, invoice.Terms.Value, uint32(testCurrentHeight)+testInvoiceCltvDelta-1, - testCurrentHeight, getCircuitKey(10), hodlChan, testPayload, + testCurrentHeight, getCircuitKey(10), hodlChan, + nil, testPayload, ) require.NoError(t, err) @@ -1784,9 +1797,9 @@ func testSettleInvoicePaymentAddrRequiredOptionalGrace(t *testing.T, // no problem as we should allow these existing invoices to be settled. hodlChan := make(chan interface{}, 1) resolution, err := ctx.registry.NotifyExitHopHtlc( - testInvoicePaymentHash, testInvoiceAmount, - testHtlcExpiry, testCurrentHeight, - getCircuitKey(10), hodlChan, testPayload, + testInvoicePaymentHash, testInvoiceAmount, testHtlcExpiry, + testCurrentHeight, getCircuitKey(10), hodlChan, + nil, testPayload, ) require.NoError(t, err) @@ -1848,8 +1861,8 @@ func testAMPWithoutMPPPayload(t *testing.T, hodlChan := make(chan interface{}, 1) resolution, err := ctx.registry.NotifyExitHopHtlc( - lntypes.Hash{}, shardAmt, expiry, - testCurrentHeight, getCircuitKey(uint64(10)), hodlChan, + lntypes.Hash{}, shardAmt, expiry, testCurrentHeight, + getCircuitKey(uint64(10)), hodlChan, nil, payload, ) require.NoError(t, err) @@ -2017,8 +2030,8 @@ func testSpontaneousAmpPaymentImpl( } resolution, err := ctx.registry.NotifyExitHopHtlc( - child.Hash, shardAmt, expiry, - testCurrentHeight, getCircuitKey(uint64(i)), hodlChan, + child.Hash, shardAmt, expiry, testCurrentHeight, + getCircuitKey(uint64(i)), hodlChan, nil, payload, ) require.NoError(t, err) diff --git a/invoices/invoices.go b/invoices/invoices.go index 24118f1e5d..c48629c583 100644 --- a/invoices/invoices.go +++ b/invoices/invoices.go @@ -547,6 +547,11 @@ type InvoiceHTLC struct { // the htlc. CustomRecords record.CustomSet + // WireCustomRecords contains the custom key/value pairs that were only + // included in p2p wire message of the HTLC and not in the onion + // payload. + WireCustomRecords lnwire.CustomRecords + // AMP encapsulates additional data relevant to AMP HTLCs. This includes // the AMP onion record, in addition to the HTLC's payment hash and // preimage since these are unique to each AMP HTLC, and not the invoice @@ -566,6 +571,7 @@ func (h *InvoiceHTLC) Copy() *InvoiceHTLC { result.CustomRecords[k] = v } + result.WireCustomRecords = h.WireCustomRecords.Copy() result.AMP = h.AMP.Copy() return &result diff --git a/invoices/update.go b/invoices/update.go index d14bafee08..3137bbbfa7 100644 --- a/invoices/update.go +++ b/invoices/update.go @@ -21,12 +21,20 @@ type invoiceUpdateCtx struct { expiry uint32 currentHeight int32 finalCltvRejectDelta int32 - customRecords record.CustomSet - mpp *record.MPP - amp *record.AMP - metadata []byte - pathID *chainhash.Hash - totalAmtMsat lnwire.MilliSatoshi + + // wireCustomRecords are the custom records that were included with the + // HTLC wire message. + wireCustomRecords lnwire.CustomRecords + + // customRecords is a map of custom records that were included with the + // HTLC onion payload. + customRecords record.CustomSet + + mpp *record.MPP + amp *record.AMP + metadata []byte + pathID *chainhash.Hash + totalAmtMsat lnwire.MilliSatoshi } // invoiceRef returns an identifier that can be used to lookup or update the @@ -95,12 +103,14 @@ func (i invoiceUpdateCtx) settleRes(preimage lntypes.Preimage, // acceptRes is a helper function which creates an accept resolution with // the information contained in the invoiceUpdateCtx and the accept resolution // result provided. -func (i invoiceUpdateCtx) acceptRes(outcome acceptResolutionResult) *htlcAcceptResolution { +func (i invoiceUpdateCtx) acceptRes( + outcome acceptResolutionResult) *htlcAcceptResolution { + return newAcceptResolution(i.circuitKey, outcome) } // updateInvoice is a callback for DB.UpdateInvoice that contains the invoice -// settlement logic. It returns a hltc resolution that indicates what the +// settlement logic. It returns a HTLC resolution that indicates what the // outcome of the update was. func updateInvoice(ctx *invoiceUpdateCtx, inv *Invoice) ( *InvoiceUpdateDesc, HtlcResolution, error) { @@ -119,7 +129,7 @@ func updateInvoice(ctx *invoiceUpdateCtx, inv *Invoice) ( pre := inv.Terms.PaymentPreimage // Terms.PaymentPreimage will be nil for AMP invoices. - // Set it to the HTLC's AMP Preimage instead. + // Set it to the HTLCs AMP Preimage instead. if pre == nil { pre = htlc.AMP.Preimage } @@ -180,13 +190,19 @@ func updateMpp(ctx *invoiceUpdateCtx, inv *Invoice) (*InvoiceUpdateDesc, paymentAddr = ctx.pathID[:] } + // For storage, we don't really care where the custom records came from. + // So we merge them together and store them in the same field. + customRecords := lnwire.CustomRecords( + ctx.customRecords, + ).MergedCopy(ctx.wireCustomRecords) + // Start building the accept descriptor. acceptDesc := &HtlcAcceptDesc{ Amt: ctx.amtPaid, Expiry: ctx.expiry, AcceptHeight: ctx.currentHeight, MppTotalAmt: totalAmt, - CustomRecords: ctx.customRecords, + CustomRecords: record.CustomSet(customRecords), } if ctx.amp != nil { @@ -223,7 +239,7 @@ func updateMpp(ctx *invoiceUpdateCtx, inv *Invoice) (*InvoiceUpdateDesc, htlcSet := inv.HTLCSet(setID, HtlcStateAccepted) - // Check whether total amt matches other htlcs in the set. + // Check whether total amt matches other HTLCs in the set. var newSetTotal lnwire.MilliSatoshi for _, htlc := range htlcSet { if totalAmt != htlc.MppTotalAmt { @@ -265,7 +281,7 @@ func updateMpp(ctx *invoiceUpdateCtx, inv *Invoice) (*InvoiceUpdateDesc, return &update, ctx.acceptRes(resultPartialAccepted), nil } - // Check to see if we can settle or this is an hold invoice and + // Check to see if we can settle or this is a hold invoice, and // we need to wait for the preimage. if inv.HodlInvoice { update.State = &InvoiceStateUpdateDesc{ @@ -434,13 +450,19 @@ func updateLegacy(ctx *invoiceUpdateCtx, return nil, ctx.failRes(ResultExpiryTooSoon), nil } + // For storage, we don't really care where the custom records came from. + // So we merge them together and store them in the same field. + customRecords := lnwire.CustomRecords( + ctx.customRecords, + ).MergedCopy(ctx.wireCustomRecords) + // Record HTLC in the invoice database. newHtlcs := map[CircuitKey]*HtlcAcceptDesc{ ctx.circuitKey: { Amt: ctx.amtPaid, Expiry: ctx.expiry, AcceptHeight: ctx.currentHeight, - CustomRecords: ctx.customRecords, + CustomRecords: record.CustomSet(customRecords), }, } diff --git a/invoices/update_invoice_test.go b/invoices/update_invoice_test.go index 42d370971d..6069fbecd7 100644 --- a/invoices/update_invoice_test.go +++ b/invoices/update_invoice_test.go @@ -5,6 +5,7 @@ import ( "time" "github.com/lightningnetwork/lnd/lntypes" + "github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/record" "github.com/stretchr/testify/require" ) @@ -37,98 +38,147 @@ func TestUpdateHTLC(t *testing.T) { { name: "MPP accept", input: InvoiceHTLC{ - Amt: 5000, - MppTotalAmt: 5000, - AcceptHeight: 100, - AcceptTime: testNow, - ResolveTime: time.Time{}, - Expiry: 40, - State: HtlcStateAccepted, - CustomRecords: make(record.CustomSet), - AMP: nil, + Amt: 5000, + MppTotalAmt: 5000, + AcceptHeight: 100, + AcceptTime: testNow, + ResolveTime: time.Time{}, + Expiry: 40, + State: HtlcStateAccepted, + CustomRecords: make(record.CustomSet), + WireCustomRecords: make(lnwire.CustomRecords), + AMP: nil, }, invState: ContractAccepted, setID: nil, output: InvoiceHTLC{ - Amt: 5000, - MppTotalAmt: 5000, - AcceptHeight: 100, - AcceptTime: testNow, - ResolveTime: time.Time{}, - Expiry: 40, - State: HtlcStateAccepted, - CustomRecords: make(record.CustomSet), - AMP: nil, + Amt: 5000, + MppTotalAmt: 5000, + AcceptHeight: 100, + AcceptTime: testNow, + ResolveTime: time.Time{}, + Expiry: 40, + State: HtlcStateAccepted, + CustomRecords: make(record.CustomSet), + WireCustomRecords: make(lnwire.CustomRecords), + AMP: nil, + }, + expErr: nil, + }, + { + name: "MPP accept, copy custom records", + input: InvoiceHTLC{ + Amt: 5000, + MppTotalAmt: 5000, + AcceptHeight: 100, + AcceptTime: testNow, + ResolveTime: time.Time{}, + Expiry: 40, + State: HtlcStateAccepted, + CustomRecords: record.CustomSet{ + 0x01: []byte{0x02}, + 0xffff: []byte{0x04, 0x05, 0x06}, + }, + WireCustomRecords: lnwire.CustomRecords{ + 0x010101: []byte{0x02, 0x03}, + 0xffffff: []byte{0x44, 0x55, 0x66}, + }, + AMP: nil, + }, + invState: ContractAccepted, + setID: nil, + output: InvoiceHTLC{ + Amt: 5000, + MppTotalAmt: 5000, + AcceptHeight: 100, + AcceptTime: testNow, + ResolveTime: time.Time{}, + Expiry: 40, + State: HtlcStateAccepted, + CustomRecords: record.CustomSet{ + 0x01: []byte{0x02}, + 0xffff: []byte{0x04, 0x05, 0x06}, + }, + WireCustomRecords: lnwire.CustomRecords{ + 0x010101: []byte{0x02, 0x03}, + 0xffffff: []byte{0x44, 0x55, 0x66}, + }, + AMP: nil, }, expErr: nil, }, { name: "MPP settle", input: InvoiceHTLC{ - Amt: 5000, - MppTotalAmt: 5000, - AcceptHeight: 100, - AcceptTime: testNow, - ResolveTime: time.Time{}, - Expiry: 40, - State: HtlcStateAccepted, - CustomRecords: make(record.CustomSet), - AMP: nil, + Amt: 5000, + MppTotalAmt: 5000, + AcceptHeight: 100, + AcceptTime: testNow, + ResolveTime: time.Time{}, + Expiry: 40, + State: HtlcStateAccepted, + CustomRecords: make(record.CustomSet), + WireCustomRecords: make(lnwire.CustomRecords), + AMP: nil, }, invState: ContractSettled, setID: nil, output: InvoiceHTLC{ - Amt: 5000, - MppTotalAmt: 5000, - AcceptHeight: 100, - AcceptTime: testNow, - ResolveTime: testNow, - Expiry: 40, - State: HtlcStateSettled, - CustomRecords: make(record.CustomSet), - AMP: nil, + Amt: 5000, + MppTotalAmt: 5000, + AcceptHeight: 100, + AcceptTime: testNow, + ResolveTime: testNow, + Expiry: 40, + State: HtlcStateSettled, + CustomRecords: make(record.CustomSet), + WireCustomRecords: make(lnwire.CustomRecords), + AMP: nil, }, expErr: nil, }, { name: "MPP cancel", input: InvoiceHTLC{ - Amt: 5000, - MppTotalAmt: 5000, - AcceptHeight: 100, - AcceptTime: testNow, - ResolveTime: time.Time{}, - Expiry: 40, - State: HtlcStateAccepted, - CustomRecords: make(record.CustomSet), - AMP: nil, + Amt: 5000, + MppTotalAmt: 5000, + AcceptHeight: 100, + AcceptTime: testNow, + ResolveTime: time.Time{}, + Expiry: 40, + State: HtlcStateAccepted, + CustomRecords: make(record.CustomSet), + WireCustomRecords: make(lnwire.CustomRecords), + AMP: nil, }, invState: ContractCanceled, setID: nil, output: InvoiceHTLC{ - Amt: 5000, - MppTotalAmt: 5000, - AcceptHeight: 100, - AcceptTime: testNow, - ResolveTime: testNow, - Expiry: 40, - State: HtlcStateCanceled, - CustomRecords: make(record.CustomSet), - AMP: nil, + Amt: 5000, + MppTotalAmt: 5000, + AcceptHeight: 100, + AcceptTime: testNow, + ResolveTime: testNow, + Expiry: 40, + State: HtlcStateCanceled, + CustomRecords: make(record.CustomSet), + WireCustomRecords: make(lnwire.CustomRecords), + AMP: nil, }, expErr: nil, }, { name: "AMP accept missing preimage", input: InvoiceHTLC{ - Amt: 5000, - MppTotalAmt: 5000, - AcceptHeight: 100, - AcceptTime: testNow, - ResolveTime: time.Time{}, - Expiry: 40, - State: HtlcStateAccepted, - CustomRecords: make(record.CustomSet), + Amt: 5000, + MppTotalAmt: 5000, + AcceptHeight: 100, + AcceptTime: testNow, + ResolveTime: time.Time{}, + Expiry: 40, + State: HtlcStateAccepted, + CustomRecords: make(record.CustomSet), + WireCustomRecords: make(lnwire.CustomRecords), AMP: &InvoiceHtlcAMPData{ Record: *ampRecord, Hash: hash, @@ -138,14 +188,15 @@ func TestUpdateHTLC(t *testing.T) { invState: ContractAccepted, setID: &setID, output: InvoiceHTLC{ - Amt: 5000, - MppTotalAmt: 5000, - AcceptHeight: 100, - AcceptTime: testNow, - ResolveTime: time.Time{}, - Expiry: 40, - State: HtlcStateAccepted, - CustomRecords: make(record.CustomSet), + Amt: 5000, + MppTotalAmt: 5000, + AcceptHeight: 100, + AcceptTime: testNow, + ResolveTime: time.Time{}, + Expiry: 40, + State: HtlcStateAccepted, + CustomRecords: make(record.CustomSet), + WireCustomRecords: make(lnwire.CustomRecords), AMP: &InvoiceHtlcAMPData{ Record: *ampRecord, Hash: hash, @@ -157,14 +208,15 @@ func TestUpdateHTLC(t *testing.T) { { name: "AMP accept invalid preimage", input: InvoiceHTLC{ - Amt: 5000, - MppTotalAmt: 5000, - AcceptHeight: 100, - AcceptTime: testNow, - ResolveTime: time.Time{}, - Expiry: 40, - State: HtlcStateAccepted, - CustomRecords: make(record.CustomSet), + Amt: 5000, + MppTotalAmt: 5000, + AcceptHeight: 100, + AcceptTime: testNow, + ResolveTime: time.Time{}, + Expiry: 40, + State: HtlcStateAccepted, + CustomRecords: make(record.CustomSet), + WireCustomRecords: make(lnwire.CustomRecords), AMP: &InvoiceHtlcAMPData{ Record: *ampRecord, Hash: hash, @@ -174,14 +226,15 @@ func TestUpdateHTLC(t *testing.T) { invState: ContractAccepted, setID: &setID, output: InvoiceHTLC{ - Amt: 5000, - MppTotalAmt: 5000, - AcceptHeight: 100, - AcceptTime: testNow, - ResolveTime: time.Time{}, - Expiry: 40, - State: HtlcStateAccepted, - CustomRecords: make(record.CustomSet), + Amt: 5000, + MppTotalAmt: 5000, + AcceptHeight: 100, + AcceptTime: testNow, + ResolveTime: time.Time{}, + Expiry: 40, + State: HtlcStateAccepted, + CustomRecords: make(record.CustomSet), + WireCustomRecords: make(lnwire.CustomRecords), AMP: &InvoiceHtlcAMPData{ Record: *ampRecord, Hash: hash, @@ -193,14 +246,15 @@ func TestUpdateHTLC(t *testing.T) { { name: "AMP accept valid preimage", input: InvoiceHTLC{ - Amt: 5000, - MppTotalAmt: 5000, - AcceptHeight: 100, - AcceptTime: testNow, - ResolveTime: time.Time{}, - Expiry: 40, - State: HtlcStateAccepted, - CustomRecords: make(record.CustomSet), + Amt: 5000, + MppTotalAmt: 5000, + AcceptHeight: 100, + AcceptTime: testNow, + ResolveTime: time.Time{}, + Expiry: 40, + State: HtlcStateAccepted, + CustomRecords: make(record.CustomSet), + WireCustomRecords: make(lnwire.CustomRecords), AMP: &InvoiceHtlcAMPData{ Record: *ampRecord, Hash: hash, @@ -210,14 +264,15 @@ func TestUpdateHTLC(t *testing.T) { invState: ContractAccepted, setID: &setID, output: InvoiceHTLC{ - Amt: 5000, - MppTotalAmt: 5000, - AcceptHeight: 100, - AcceptTime: testNow, - ResolveTime: time.Time{}, - Expiry: 40, - State: HtlcStateAccepted, - CustomRecords: make(record.CustomSet), + Amt: 5000, + MppTotalAmt: 5000, + AcceptHeight: 100, + AcceptTime: testNow, + ResolveTime: time.Time{}, + Expiry: 40, + State: HtlcStateAccepted, + CustomRecords: make(record.CustomSet), + WireCustomRecords: make(lnwire.CustomRecords), AMP: &InvoiceHtlcAMPData{ Record: *ampRecord, Hash: hash, @@ -229,14 +284,15 @@ func TestUpdateHTLC(t *testing.T) { { name: "AMP accept valid preimage different htlc set", input: InvoiceHTLC{ - Amt: 5000, - MppTotalAmt: 5000, - AcceptHeight: 100, - AcceptTime: testNow, - ResolveTime: time.Time{}, - Expiry: 40, - State: HtlcStateAccepted, - CustomRecords: make(record.CustomSet), + Amt: 5000, + MppTotalAmt: 5000, + AcceptHeight: 100, + AcceptTime: testNow, + ResolveTime: time.Time{}, + Expiry: 40, + State: HtlcStateAccepted, + CustomRecords: make(record.CustomSet), + WireCustomRecords: make(lnwire.CustomRecords), AMP: &InvoiceHtlcAMPData{ Record: *ampRecord, Hash: hash, @@ -246,14 +302,15 @@ func TestUpdateHTLC(t *testing.T) { invState: ContractAccepted, setID: &diffSetID, output: InvoiceHTLC{ - Amt: 5000, - MppTotalAmt: 5000, - AcceptHeight: 100, - AcceptTime: testNow, - ResolveTime: time.Time{}, - Expiry: 40, - State: HtlcStateAccepted, - CustomRecords: make(record.CustomSet), + Amt: 5000, + MppTotalAmt: 5000, + AcceptHeight: 100, + AcceptTime: testNow, + ResolveTime: time.Time{}, + Expiry: 40, + State: HtlcStateAccepted, + CustomRecords: make(record.CustomSet), + WireCustomRecords: make(lnwire.CustomRecords), AMP: &InvoiceHtlcAMPData{ Record: *ampRecord, Hash: hash, @@ -265,14 +322,15 @@ func TestUpdateHTLC(t *testing.T) { { name: "AMP settle missing preimage", input: InvoiceHTLC{ - Amt: 5000, - MppTotalAmt: 5000, - AcceptHeight: 100, - AcceptTime: testNow, - ResolveTime: time.Time{}, - Expiry: 40, - State: HtlcStateAccepted, - CustomRecords: make(record.CustomSet), + Amt: 5000, + MppTotalAmt: 5000, + AcceptHeight: 100, + AcceptTime: testNow, + ResolveTime: time.Time{}, + Expiry: 40, + State: HtlcStateAccepted, + CustomRecords: make(record.CustomSet), + WireCustomRecords: make(lnwire.CustomRecords), AMP: &InvoiceHtlcAMPData{ Record: *ampRecord, Hash: hash, @@ -282,14 +340,15 @@ func TestUpdateHTLC(t *testing.T) { invState: ContractSettled, setID: &setID, output: InvoiceHTLC{ - Amt: 5000, - MppTotalAmt: 5000, - AcceptHeight: 100, - AcceptTime: testNow, - ResolveTime: time.Time{}, - Expiry: 40, - State: HtlcStateAccepted, - CustomRecords: make(record.CustomSet), + Amt: 5000, + MppTotalAmt: 5000, + AcceptHeight: 100, + AcceptTime: testNow, + ResolveTime: time.Time{}, + Expiry: 40, + State: HtlcStateAccepted, + CustomRecords: make(record.CustomSet), + WireCustomRecords: make(lnwire.CustomRecords), AMP: &InvoiceHtlcAMPData{ Record: *ampRecord, Hash: hash, @@ -301,14 +360,15 @@ func TestUpdateHTLC(t *testing.T) { { name: "AMP settle invalid preimage", input: InvoiceHTLC{ - Amt: 5000, - MppTotalAmt: 5000, - AcceptHeight: 100, - AcceptTime: testNow, - ResolveTime: time.Time{}, - Expiry: 40, - State: HtlcStateAccepted, - CustomRecords: make(record.CustomSet), + Amt: 5000, + MppTotalAmt: 5000, + AcceptHeight: 100, + AcceptTime: testNow, + ResolveTime: time.Time{}, + Expiry: 40, + State: HtlcStateAccepted, + CustomRecords: make(record.CustomSet), + WireCustomRecords: make(lnwire.CustomRecords), AMP: &InvoiceHtlcAMPData{ Record: *ampRecord, Hash: hash, @@ -318,14 +378,15 @@ func TestUpdateHTLC(t *testing.T) { invState: ContractSettled, setID: &setID, output: InvoiceHTLC{ - Amt: 5000, - MppTotalAmt: 5000, - AcceptHeight: 100, - AcceptTime: testNow, - ResolveTime: time.Time{}, - Expiry: 40, - State: HtlcStateAccepted, - CustomRecords: make(record.CustomSet), + Amt: 5000, + MppTotalAmt: 5000, + AcceptHeight: 100, + AcceptTime: testNow, + ResolveTime: time.Time{}, + Expiry: 40, + State: HtlcStateAccepted, + CustomRecords: make(record.CustomSet), + WireCustomRecords: make(lnwire.CustomRecords), AMP: &InvoiceHtlcAMPData{ Record: *ampRecord, Hash: hash, @@ -337,14 +398,15 @@ func TestUpdateHTLC(t *testing.T) { { name: "AMP settle valid preimage", input: InvoiceHTLC{ - Amt: 5000, - MppTotalAmt: 5000, - AcceptHeight: 100, - AcceptTime: testNow, - ResolveTime: time.Time{}, - Expiry: 40, - State: HtlcStateAccepted, - CustomRecords: make(record.CustomSet), + Amt: 5000, + MppTotalAmt: 5000, + AcceptHeight: 100, + AcceptTime: testNow, + ResolveTime: time.Time{}, + Expiry: 40, + State: HtlcStateAccepted, + CustomRecords: make(record.CustomSet), + WireCustomRecords: make(lnwire.CustomRecords), AMP: &InvoiceHtlcAMPData{ Record: *ampRecord, Hash: hash, @@ -354,14 +416,15 @@ func TestUpdateHTLC(t *testing.T) { invState: ContractSettled, setID: &setID, output: InvoiceHTLC{ - Amt: 5000, - MppTotalAmt: 5000, - AcceptHeight: 100, - AcceptTime: testNow, - ResolveTime: testNow, - Expiry: 40, - State: HtlcStateSettled, - CustomRecords: make(record.CustomSet), + Amt: 5000, + MppTotalAmt: 5000, + AcceptHeight: 100, + AcceptTime: testNow, + ResolveTime: testNow, + Expiry: 40, + State: HtlcStateSettled, + CustomRecords: make(record.CustomSet), + WireCustomRecords: make(lnwire.CustomRecords), AMP: &InvoiceHtlcAMPData{ Record: *ampRecord, Hash: hash, @@ -377,14 +440,15 @@ func TestUpdateHTLC(t *testing.T) { // remain in the accepted state. name: "AMP settle valid preimage different htlc set", input: InvoiceHTLC{ - Amt: 5000, - MppTotalAmt: 5000, - AcceptHeight: 100, - AcceptTime: testNow, - ResolveTime: time.Time{}, - Expiry: 40, - State: HtlcStateAccepted, - CustomRecords: make(record.CustomSet), + Amt: 5000, + MppTotalAmt: 5000, + AcceptHeight: 100, + AcceptTime: testNow, + ResolveTime: time.Time{}, + Expiry: 40, + State: HtlcStateAccepted, + CustomRecords: make(record.CustomSet), + WireCustomRecords: make(lnwire.CustomRecords), AMP: &InvoiceHtlcAMPData{ Record: *ampRecord, Hash: hash, @@ -394,14 +458,15 @@ func TestUpdateHTLC(t *testing.T) { invState: ContractSettled, setID: &diffSetID, output: InvoiceHTLC{ - Amt: 5000, - MppTotalAmt: 5000, - AcceptHeight: 100, - AcceptTime: testNow, - ResolveTime: time.Time{}, - Expiry: 40, - State: HtlcStateAccepted, - CustomRecords: make(record.CustomSet), + Amt: 5000, + MppTotalAmt: 5000, + AcceptHeight: 100, + AcceptTime: testNow, + ResolveTime: time.Time{}, + Expiry: 40, + State: HtlcStateAccepted, + CustomRecords: make(record.CustomSet), + WireCustomRecords: make(lnwire.CustomRecords), AMP: &InvoiceHtlcAMPData{ Record: *ampRecord, Hash: hash, @@ -413,14 +478,15 @@ func TestUpdateHTLC(t *testing.T) { { name: "accept invoice htlc already settled", input: InvoiceHTLC{ - Amt: 5000, - MppTotalAmt: 5000, - AcceptHeight: 100, - AcceptTime: testNow, - ResolveTime: testAlreadyNow, - Expiry: 40, - State: HtlcStateSettled, - CustomRecords: make(record.CustomSet), + Amt: 5000, + MppTotalAmt: 5000, + AcceptHeight: 100, + AcceptTime: testNow, + ResolveTime: testAlreadyNow, + Expiry: 40, + State: HtlcStateSettled, + CustomRecords: make(record.CustomSet), + WireCustomRecords: make(lnwire.CustomRecords), AMP: &InvoiceHtlcAMPData{ Record: *ampRecord, Hash: hash, @@ -430,14 +496,15 @@ func TestUpdateHTLC(t *testing.T) { invState: ContractAccepted, setID: &setID, output: InvoiceHTLC{ - Amt: 5000, - MppTotalAmt: 5000, - AcceptHeight: 100, - AcceptTime: testNow, - ResolveTime: testAlreadyNow, - Expiry: 40, - State: HtlcStateSettled, - CustomRecords: make(record.CustomSet), + Amt: 5000, + MppTotalAmt: 5000, + AcceptHeight: 100, + AcceptTime: testNow, + ResolveTime: testAlreadyNow, + Expiry: 40, + State: HtlcStateSettled, + CustomRecords: make(record.CustomSet), + WireCustomRecords: make(lnwire.CustomRecords), AMP: &InvoiceHtlcAMPData{ Record: *ampRecord, Hash: hash, @@ -449,14 +516,15 @@ func TestUpdateHTLC(t *testing.T) { { name: "cancel invoice htlc already settled", input: InvoiceHTLC{ - Amt: 5000, - MppTotalAmt: 5000, - AcceptHeight: 100, - AcceptTime: testNow, - ResolveTime: testAlreadyNow, - Expiry: 40, - State: HtlcStateSettled, - CustomRecords: make(record.CustomSet), + Amt: 5000, + MppTotalAmt: 5000, + AcceptHeight: 100, + AcceptTime: testNow, + ResolveTime: testAlreadyNow, + Expiry: 40, + State: HtlcStateSettled, + CustomRecords: make(record.CustomSet), + WireCustomRecords: make(lnwire.CustomRecords), AMP: &InvoiceHtlcAMPData{ Record: *ampRecord, Hash: hash, @@ -466,14 +534,15 @@ func TestUpdateHTLC(t *testing.T) { invState: ContractCanceled, setID: &setID, output: InvoiceHTLC{ - Amt: 5000, - MppTotalAmt: 5000, - AcceptHeight: 100, - AcceptTime: testNow, - ResolveTime: testAlreadyNow, - Expiry: 40, - State: HtlcStateSettled, - CustomRecords: make(record.CustomSet), + Amt: 5000, + MppTotalAmt: 5000, + AcceptHeight: 100, + AcceptTime: testNow, + ResolveTime: testAlreadyNow, + Expiry: 40, + State: HtlcStateSettled, + CustomRecords: make(record.CustomSet), + WireCustomRecords: make(lnwire.CustomRecords), AMP: &InvoiceHtlcAMPData{ Record: *ampRecord, Hash: hash, @@ -485,14 +554,15 @@ func TestUpdateHTLC(t *testing.T) { { name: "settle invoice htlc already settled", input: InvoiceHTLC{ - Amt: 5000, - MppTotalAmt: 5000, - AcceptHeight: 100, - AcceptTime: testNow, - ResolveTime: testAlreadyNow, - Expiry: 40, - State: HtlcStateSettled, - CustomRecords: make(record.CustomSet), + Amt: 5000, + MppTotalAmt: 5000, + AcceptHeight: 100, + AcceptTime: testNow, + ResolveTime: testAlreadyNow, + Expiry: 40, + State: HtlcStateSettled, + CustomRecords: make(record.CustomSet), + WireCustomRecords: make(lnwire.CustomRecords), AMP: &InvoiceHtlcAMPData{ Record: *ampRecord, Hash: hash, @@ -502,14 +572,15 @@ func TestUpdateHTLC(t *testing.T) { invState: ContractSettled, setID: &setID, output: InvoiceHTLC{ - Amt: 5000, - MppTotalAmt: 5000, - AcceptHeight: 100, - AcceptTime: testNow, - ResolveTime: testAlreadyNow, - Expiry: 40, - State: HtlcStateSettled, - CustomRecords: make(record.CustomSet), + Amt: 5000, + MppTotalAmt: 5000, + AcceptHeight: 100, + AcceptTime: testNow, + ResolveTime: testAlreadyNow, + Expiry: 40, + State: HtlcStateSettled, + CustomRecords: make(record.CustomSet), + WireCustomRecords: make(lnwire.CustomRecords), AMP: &InvoiceHtlcAMPData{ Record: *ampRecord, Hash: hash, @@ -521,14 +592,15 @@ func TestUpdateHTLC(t *testing.T) { { name: "cancel invoice", input: InvoiceHTLC{ - Amt: 5000, - MppTotalAmt: 5000, - AcceptHeight: 100, - AcceptTime: testNow, - ResolveTime: time.Time{}, - Expiry: 40, - State: HtlcStateAccepted, - CustomRecords: make(record.CustomSet), + Amt: 5000, + MppTotalAmt: 5000, + AcceptHeight: 100, + AcceptTime: testNow, + ResolveTime: time.Time{}, + Expiry: 40, + State: HtlcStateAccepted, + CustomRecords: make(record.CustomSet), + WireCustomRecords: make(lnwire.CustomRecords), AMP: &InvoiceHtlcAMPData{ Record: *ampRecord, Hash: hash, @@ -538,14 +610,15 @@ func TestUpdateHTLC(t *testing.T) { invState: ContractCanceled, setID: &setID, output: InvoiceHTLC{ - Amt: 5000, - MppTotalAmt: 5000, - AcceptHeight: 100, - AcceptTime: testNow, - ResolveTime: testNow, - Expiry: 40, - State: HtlcStateCanceled, - CustomRecords: make(record.CustomSet), + Amt: 5000, + MppTotalAmt: 5000, + AcceptHeight: 100, + AcceptTime: testNow, + ResolveTime: testNow, + Expiry: 40, + State: HtlcStateCanceled, + CustomRecords: make(record.CustomSet), + WireCustomRecords: make(lnwire.CustomRecords), AMP: &InvoiceHtlcAMPData{ Record: *ampRecord, Hash: hash, @@ -557,14 +630,15 @@ func TestUpdateHTLC(t *testing.T) { { name: "accept invoice htlc already canceled", input: InvoiceHTLC{ - Amt: 5000, - MppTotalAmt: 5000, - AcceptHeight: 100, - AcceptTime: testNow, - ResolveTime: testAlreadyNow, - Expiry: 40, - State: HtlcStateCanceled, - CustomRecords: make(record.CustomSet), + Amt: 5000, + MppTotalAmt: 5000, + AcceptHeight: 100, + AcceptTime: testNow, + ResolveTime: testAlreadyNow, + Expiry: 40, + State: HtlcStateCanceled, + CustomRecords: make(record.CustomSet), + WireCustomRecords: make(lnwire.CustomRecords), AMP: &InvoiceHtlcAMPData{ Record: *ampRecord, Hash: hash, @@ -574,14 +648,15 @@ func TestUpdateHTLC(t *testing.T) { invState: ContractAccepted, setID: &setID, output: InvoiceHTLC{ - Amt: 5000, - MppTotalAmt: 5000, - AcceptHeight: 100, - AcceptTime: testNow, - ResolveTime: testAlreadyNow, - Expiry: 40, - State: HtlcStateCanceled, - CustomRecords: make(record.CustomSet), + Amt: 5000, + MppTotalAmt: 5000, + AcceptHeight: 100, + AcceptTime: testNow, + ResolveTime: testAlreadyNow, + Expiry: 40, + State: HtlcStateCanceled, + CustomRecords: make(record.CustomSet), + WireCustomRecords: make(lnwire.CustomRecords), AMP: &InvoiceHtlcAMPData{ Record: *ampRecord, Hash: hash, @@ -593,14 +668,15 @@ func TestUpdateHTLC(t *testing.T) { { name: "cancel invoice htlc already canceled", input: InvoiceHTLC{ - Amt: 5000, - MppTotalAmt: 5000, - AcceptHeight: 100, - AcceptTime: testNow, - ResolveTime: testAlreadyNow, - Expiry: 40, - State: HtlcStateCanceled, - CustomRecords: make(record.CustomSet), + Amt: 5000, + MppTotalAmt: 5000, + AcceptHeight: 100, + AcceptTime: testNow, + ResolveTime: testAlreadyNow, + Expiry: 40, + State: HtlcStateCanceled, + CustomRecords: make(record.CustomSet), + WireCustomRecords: make(lnwire.CustomRecords), AMP: &InvoiceHtlcAMPData{ Record: *ampRecord, Hash: hash, @@ -610,14 +686,15 @@ func TestUpdateHTLC(t *testing.T) { invState: ContractCanceled, setID: &setID, output: InvoiceHTLC{ - Amt: 5000, - MppTotalAmt: 5000, - AcceptHeight: 100, - AcceptTime: testNow, - ResolveTime: testAlreadyNow, - Expiry: 40, - State: HtlcStateCanceled, - CustomRecords: make(record.CustomSet), + Amt: 5000, + MppTotalAmt: 5000, + AcceptHeight: 100, + AcceptTime: testNow, + ResolveTime: testAlreadyNow, + Expiry: 40, + State: HtlcStateCanceled, + CustomRecords: make(record.CustomSet), + WireCustomRecords: make(lnwire.CustomRecords), AMP: &InvoiceHtlcAMPData{ Record: *ampRecord, Hash: hash, @@ -629,14 +706,15 @@ func TestUpdateHTLC(t *testing.T) { { name: "settle invoice htlc already canceled", input: InvoiceHTLC{ - Amt: 5000, - MppTotalAmt: 5000, - AcceptHeight: 100, - AcceptTime: testNow, - ResolveTime: testAlreadyNow, - Expiry: 40, - State: HtlcStateCanceled, - CustomRecords: make(record.CustomSet), + Amt: 5000, + MppTotalAmt: 5000, + AcceptHeight: 100, + AcceptTime: testNow, + ResolveTime: testAlreadyNow, + Expiry: 40, + State: HtlcStateCanceled, + CustomRecords: make(record.CustomSet), + WireCustomRecords: make(lnwire.CustomRecords), AMP: &InvoiceHtlcAMPData{ Record: *ampRecord, Hash: hash, @@ -646,14 +724,15 @@ func TestUpdateHTLC(t *testing.T) { invState: ContractSettled, setID: &setID, output: InvoiceHTLC{ - Amt: 5000, - MppTotalAmt: 5000, - AcceptHeight: 100, - AcceptTime: testNow, - ResolveTime: testAlreadyNow, - Expiry: 40, - State: HtlcStateCanceled, - CustomRecords: make(record.CustomSet), + Amt: 5000, + MppTotalAmt: 5000, + AcceptHeight: 100, + AcceptTime: testNow, + ResolveTime: testAlreadyNow, + Expiry: 40, + State: HtlcStateCanceled, + CustomRecords: make(record.CustomSet), + WireCustomRecords: make(lnwire.CustomRecords), AMP: &InvoiceHtlcAMPData{ Record: *ampRecord, Hash: hash, From 0cb599dbf6e9f3327c5865c3c769630be2433f7a Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Thu, 12 Sep 2024 17:59:38 +0200 Subject: [PATCH 126/218] lnrpc+rpcserver: encode custom records as custom channel data With this commit we encode the custom records as a TLV stream into the custom channel data field of the invoice HTLC. This allows the custom data parser to parse those records and replace it with human-readable JSON on the RPC interface. --- lnrpc/invoicesrpc/config_active.go | 5 ++ lnrpc/invoicesrpc/invoices.swagger.json | 5 ++ lnrpc/invoicesrpc/invoices_server.go | 27 +++++++++-- lnrpc/invoicesrpc/utils.go | 10 ++++ lnrpc/lightning.pb.go | 14 +++++- lnrpc/lightning.proto | 5 ++ lnrpc/lightning.swagger.json | 5 ++ rpcserver.go | 61 ++++++++++++++++++++++++- subrpcserver_config.go | 17 +++++++ 9 files changed, 144 insertions(+), 5 deletions(-) diff --git a/lnrpc/invoicesrpc/config_active.go b/lnrpc/invoicesrpc/config_active.go index b781be8756..9568ed8919 100644 --- a/lnrpc/invoicesrpc/config_active.go +++ b/lnrpc/invoicesrpc/config_active.go @@ -10,6 +10,7 @@ import ( "github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/macaroons" "github.com/lightningnetwork/lnd/netann" + "google.golang.org/protobuf/proto" ) // Config is the primary configuration struct for the invoices RPC server. It @@ -69,4 +70,8 @@ type Config struct { // GetAlias returns the peer's alias SCID if it exists given the // 32-byte ChannelID. GetAlias func(lnwire.ChannelID) (lnwire.ShortChannelID, error) + + // ParseAuxData is a function that can be used to parse the auxiliary + // data from the invoice. + ParseAuxData func(message proto.Message) error } diff --git a/lnrpc/invoicesrpc/invoices.swagger.json b/lnrpc/invoicesrpc/invoices.swagger.json index a51b102fbd..4e8cb6ac5f 100644 --- a/lnrpc/invoicesrpc/invoices.swagger.json +++ b/lnrpc/invoicesrpc/invoices.swagger.json @@ -784,6 +784,11 @@ "amp": { "$ref": "#/definitions/lnrpcAMP", "description": "Details relevant to AMP HTLCs, only populated if this is an AMP HTLC." + }, + "custom_channel_data": { + "type": "string", + "format": "byte", + "description": "Custom channel data that might be populated in custom channels." } }, "title": "Details of an HTLC that paid to an invoice" diff --git a/lnrpc/invoicesrpc/invoices_server.go b/lnrpc/invoicesrpc/invoices_server.go index d18ab09d29..8ca02b260d 100644 --- a/lnrpc/invoicesrpc/invoices_server.go +++ b/lnrpc/invoicesrpc/invoices_server.go @@ -222,8 +222,9 @@ func (r *ServerShell) RegisterWithRestServer(ctx context.Context, // methods routed towards it. // // NOTE: This is part of the lnrpc.GrpcHandler interface. -func (r *ServerShell) CreateSubServer(configRegistry lnrpc.SubServerConfigDispatcher) ( - lnrpc.SubServer, lnrpc.MacaroonPerms, error) { +func (r *ServerShell) CreateSubServer( + configRegistry lnrpc.SubServerConfigDispatcher) (lnrpc.SubServer, + lnrpc.MacaroonPerms, error) { subServer, macPermissions, err := createNewSubServer(configRegistry) if err != nil { @@ -264,6 +265,14 @@ func (s *Server) SubscribeSingleInvoice(req *SubscribeSingleInvoiceRequest, return err } + // Give the aux data parser a chance to format the + // custom data in the invoice HTLCs. + err = s.cfg.ParseAuxData(rpcInvoice) + if err != nil { + return fmt.Errorf("error parsing custom data: "+ + "%w", err) + } + if err := updateStream.Send(rpcInvoice); err != nil { return err } @@ -451,7 +460,19 @@ func (s *Server) LookupInvoiceV2(ctx context.Context, return nil, err } - return CreateRPCInvoice(&invoice, s.cfg.ChainParams) + rpcInvoice, err := CreateRPCInvoice(&invoice, s.cfg.ChainParams) + if err != nil { + return nil, err + } + + // Give the aux data parser a chance to format the custom data in the + // invoice HTLCs. + err = s.cfg.ParseAuxData(rpcInvoice) + if err != nil { + return nil, fmt.Errorf("error parsing custom data: %w", err) + } + + return rpcInvoice, nil } // HtlcModifier is a bidirectional streaming RPC that allows a client to diff --git a/lnrpc/invoicesrpc/utils.go b/lnrpc/invoicesrpc/utils.go index ce02769fb4..955ba6acf2 100644 --- a/lnrpc/invoicesrpc/utils.go +++ b/lnrpc/invoicesrpc/utils.go @@ -123,6 +123,16 @@ func CreateRPCInvoice(invoice *invoices.Invoice, MppTotalAmtMsat: uint64(htlc.MppTotalAmt), } + // The custom channel data is currently just the raw bytes of + // the encoded custom records. + customData, err := lnwire.CustomRecords( + htlc.CustomRecords, + ).Serialize() + if err != nil { + return nil, err + } + rpcHtlc.CustomChannelData = customData + // Populate any fields relevant to AMP payments. if htlc.AMP != nil { rootShare := htlc.AMP.Record.RootShare() diff --git a/lnrpc/lightning.pb.go b/lnrpc/lightning.pb.go index 26ab31dd93..435e56320d 100644 --- a/lnrpc/lightning.pb.go +++ b/lnrpc/lightning.pb.go @@ -12943,6 +12943,8 @@ type InvoiceHTLC struct { MppTotalAmtMsat uint64 `protobuf:"varint,10,opt,name=mpp_total_amt_msat,json=mppTotalAmtMsat,proto3" json:"mpp_total_amt_msat,omitempty"` // Details relevant to AMP HTLCs, only populated if this is an AMP HTLC. Amp *AMP `protobuf:"bytes,11,opt,name=amp,proto3" json:"amp,omitempty"` + // Custom channel data that might be populated in custom channels. + CustomChannelData []byte `protobuf:"bytes,12,opt,name=custom_channel_data,json=customChannelData,proto3" json:"custom_channel_data,omitempty"` } func (x *InvoiceHTLC) Reset() { @@ -13054,6 +13056,13 @@ func (x *InvoiceHTLC) GetAmp() *AMP { return nil } +func (x *InvoiceHTLC) GetCustomChannelData() []byte { + if x != nil { + return x.CustomChannelData + } + return nil +} + // Details specific to AMP HTLCs. type AMP struct { state protoimpl.MessageState @@ -20199,7 +20208,7 @@ var file_lightning_proto_rawDesc = []byte{ 0x0a, 0x12, 0x5f, 0x6d, 0x69, 0x6e, 0x5f, 0x6e, 0x75, 0x6d, 0x5f, 0x72, 0x65, 0x61, 0x6c, 0x5f, 0x68, 0x6f, 0x70, 0x73, 0x42, 0x0b, 0x0a, 0x09, 0x5f, 0x6e, 0x75, 0x6d, 0x5f, 0x68, 0x6f, 0x70, 0x73, 0x42, 0x10, 0x0a, 0x0e, 0x5f, 0x6d, 0x61, 0x78, 0x5f, 0x6e, 0x75, 0x6d, 0x5f, 0x70, 0x61, - 0x74, 0x68, 0x73, 0x22, 0xfc, 0x03, 0x0a, 0x0b, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x48, + 0x74, 0x68, 0x73, 0x22, 0xac, 0x04, 0x0a, 0x0b, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x48, 0x54, 0x4c, 0x43, 0x12, 0x1b, 0x0a, 0x07, 0x63, 0x68, 0x61, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x42, 0x02, 0x30, 0x01, 0x52, 0x06, 0x63, 0x68, 0x61, 0x6e, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x68, 0x74, 0x6c, 0x63, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x02, @@ -20227,6 +20236,9 @@ var file_lightning_proto_rawDesc = []byte{ 0x04, 0x52, 0x0f, 0x6d, 0x70, 0x70, 0x54, 0x6f, 0x74, 0x61, 0x6c, 0x41, 0x6d, 0x74, 0x4d, 0x73, 0x61, 0x74, 0x12, 0x1c, 0x0a, 0x03, 0x61, 0x6d, 0x70, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x4d, 0x50, 0x52, 0x03, 0x61, 0x6d, 0x70, + 0x12, 0x2e, 0x0a, 0x13, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x6e, + 0x65, 0x6c, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x11, 0x63, + 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x44, 0x61, 0x74, 0x61, 0x1a, 0x40, 0x0a, 0x12, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, diff --git a/lnrpc/lightning.proto b/lnrpc/lightning.proto index b44b04caa0..f8cfcaca62 100644 --- a/lnrpc/lightning.proto +++ b/lnrpc/lightning.proto @@ -3952,6 +3952,11 @@ message InvoiceHTLC { // Details relevant to AMP HTLCs, only populated if this is an AMP HTLC. AMP amp = 11; + + /* + Custom channel data that might be populated in custom channels. + */ + bytes custom_channel_data = 12; } // Details specific to AMP HTLCs. diff --git a/lnrpc/lightning.swagger.json b/lnrpc/lightning.swagger.json index 0e69fe1fdf..9e2d2d25be 100644 --- a/lnrpc/lightning.swagger.json +++ b/lnrpc/lightning.swagger.json @@ -5602,6 +5602,11 @@ "amp": { "$ref": "#/definitions/lnrpcAMP", "description": "Details relevant to AMP HTLCs, only populated if this is an AMP HTLC." + }, + "custom_channel_data": { + "type": "string", + "format": "byte", + "description": "Custom channel data that might be populated in custom channels." } }, "title": "Details of an HTLC that paid to an invoice" diff --git a/rpcserver.go b/rpcserver.go index ef9d893124..6404845f62 100644 --- a/rpcserver.go +++ b/rpcserver.go @@ -788,7 +788,8 @@ func (r *rpcServer) addDeps(s *server, macService *macaroons.Service, s.sweeper, tower, s.towerClientMgr, r.cfg.net.ResolveTCPAddr, genInvoiceFeatures, genAmpInvoiceFeatures, s.getNodeAnnouncement, s.updateAndBrodcastSelfNode, parseAddr, - rpcsLog, s.aliasMgr, invoiceHtlcModifier, + rpcsLog, s.aliasMgr, r.implCfg.AuxDataParser, + invoiceHtlcModifier, ) if err != nil { return err @@ -6124,6 +6125,19 @@ func (r *rpcServer) LookupInvoice(ctx context.Context, return nil, err } + // Give the aux data parser a chance to format the custom data in the + // invoice HTLCs. + err = fn.MapOptionZ( + r.server.implCfg.AuxDataParser, + func(parser AuxDataParser) error { + return parser.InlineParseCustomData(rpcInvoice) + }, + ) + if err != nil { + return nil, fmt.Errorf("error parsing custom data: %w", + err) + } + return rpcInvoice, nil } @@ -6179,6 +6193,21 @@ func (r *rpcServer) ListInvoices(ctx context.Context, if err != nil { return nil, err } + + // Give the aux data parser a chance to format the custom data + // in the invoice HTLCs. + err = fn.MapOptionZ( + r.server.implCfg.AuxDataParser, + func(parser AuxDataParser) error { + return parser.InlineParseCustomData( + resp.Invoices[i], + ) + }, + ) + if err != nil { + return nil, fmt.Errorf("error parsing custom data: %w", + err) + } } return resp, nil @@ -6207,6 +6236,21 @@ func (r *rpcServer) SubscribeInvoices(req *lnrpc.InvoiceSubscription, return err } + // Give the aux data parser a chance to format the + // custom data in the invoice HTLCs. + err = fn.MapOptionZ( + r.server.implCfg.AuxDataParser, + func(parser AuxDataParser) error { + return parser.InlineParseCustomData( + rpcInvoice, + ) + }, + ) + if err != nil { + return fmt.Errorf("error parsing custom data: "+ + "%w", err) + } + if err := updateStream.Send(rpcInvoice); err != nil { return err } @@ -6219,6 +6263,21 @@ func (r *rpcServer) SubscribeInvoices(req *lnrpc.InvoiceSubscription, return err } + // Give the aux data parser a chance to format the + // custom data in the invoice HTLCs. + err = fn.MapOptionZ( + r.server.implCfg.AuxDataParser, + func(parser AuxDataParser) error { + return parser.InlineParseCustomData( + rpcInvoice, + ) + }, + ) + if err != nil { + return fmt.Errorf("error parsing custom data: "+ + "%w", err) + } + if err := updateStream.Send(rpcInvoice); err != nil { return err } diff --git a/subrpcserver_config.go b/subrpcserver_config.go index 6f4c813367..5328c4919e 100644 --- a/subrpcserver_config.go +++ b/subrpcserver_config.go @@ -11,6 +11,7 @@ import ( "github.com/lightningnetwork/lnd/autopilot" "github.com/lightningnetwork/lnd/chainreg" "github.com/lightningnetwork/lnd/channeldb" + "github.com/lightningnetwork/lnd/fn" "github.com/lightningnetwork/lnd/htlcswitch" "github.com/lightningnetwork/lnd/invoices" "github.com/lightningnetwork/lnd/lncfg" @@ -32,6 +33,7 @@ import ( "github.com/lightningnetwork/lnd/sweep" "github.com/lightningnetwork/lnd/watchtower" "github.com/lightningnetwork/lnd/watchtower/wtclient" + "google.golang.org/protobuf/proto" ) // subRPCServerConfigs is special sub-config in the main configuration that @@ -123,6 +125,7 @@ func (s *subRPCServerConfigs) PopulateDependencies(cfg *Config, modifiers ...netann.NodeAnnModifier) error, parseAddr func(addr string) (net.Addr, error), rpcLogger btclog.Logger, aliasMgr *aliasmgr.Manager, + auxDataParser fn.Option[AuxDataParser], invoiceHtlcModifier *invoices.HtlcModificationInterceptor) error { // First, we'll use reflect to obtain a version of the config struct @@ -271,6 +274,20 @@ func (s *subRPCServerConfigs) PopulateDependencies(cfg *Config, reflect.ValueOf(aliasMgr.GetPeerAlias), ) + parseAuxData := func(m proto.Message) error { + return fn.MapOptionZ( + auxDataParser, + func(p AuxDataParser) error { + return p.InlineParseCustomData( + m, + ) + }, + ) + } + subCfgValue.FieldByName("ParseAuxData").Set( + reflect.ValueOf(parseAuxData), + ) + case *neutrinorpc.Config: subCfgValue := extractReflectValue(subCfg) From 35ce62c0215efbd42461cef737d155083f107457 Mon Sep 17 00:00:00 2001 From: ffranr Date: Tue, 30 Apr 2024 17:51:50 +0100 Subject: [PATCH 127/218] itest: add basic invoice HTLC modifier integration test This commit introduces a basic integration test for the invoice HTLC modifier. The test covers scenarios where an invoice is settled with a payment that is less than the invoice amount, facilitated by the invoice HTLC modifier. --- itest/list_on_test.go | 4 + itest/lnd_forward_interceptor_test.go | 80 ++++-- itest/lnd_invoice_acceptor_test.go | 358 ++++++++++++++++++++++++++ 3 files changed, 421 insertions(+), 21 deletions(-) create mode 100644 itest/lnd_invoice_acceptor_test.go diff --git a/itest/list_on_test.go b/itest/list_on_test.go index 76d4e9132d..910b6387bf 100644 --- a/itest/list_on_test.go +++ b/itest/list_on_test.go @@ -466,6 +466,10 @@ var allTestCases = []*lntest.TestCase{ Name: "forward interceptor restart", TestFunc: testForwardInterceptorRestart, }, + { + Name: "invoice HTLC modifier basic", + TestFunc: testInvoiceHtlcModifierBasic, + }, { Name: "zero conf channel open", TestFunc: testZeroConfChannelOpen, diff --git a/itest/lnd_forward_interceptor_test.go b/itest/lnd_forward_interceptor_test.go index 78cd4ff657..6148ba7f1a 100644 --- a/itest/lnd_forward_interceptor_test.go +++ b/itest/lnd_forward_interceptor_test.go @@ -10,6 +10,7 @@ import ( "github.com/btcsuite/btcd/btcutil" "github.com/lightningnetwork/lnd/chainreg" "github.com/lightningnetwork/lnd/lnrpc" + "github.com/lightningnetwork/lnd/lnrpc/invoicesrpc" "github.com/lightningnetwork/lnd/lnrpc/routerrpc" "github.com/lightningnetwork/lnd/lntest" "github.com/lightningnetwork/lnd/lntest/node" @@ -374,8 +375,13 @@ func testForwardInterceptorModifiedHtlc(ht *lntest.HarnessTest) { // Connect an interceptor to Bob's node. bobInterceptor, cancelBobInterceptor := bob.RPC.HtlcInterceptor() + // We're going to modify the payment amount and want Carol to accept the + // payment, so we set up an invoice acceptor on Dave. + carolAcceptor, carolCancel := carol.RPC.InvoiceHtlcModifier() + defer carolCancel() + // Prepare the test cases. - invoiceValueAmtMsat := int64(1000) + invoiceValueAmtMsat := int64(20_000_000) req := &lnrpc.Invoice{ValueMsat: invoiceValueAmtMsat} addResponse := carol.RPC.AddInvoice(req) invoice := carol.RPC.LookupInvoice(addResponse.RHash) @@ -408,10 +414,10 @@ func testForwardInterceptorModifiedHtlc(ht *lntest.HarnessTest) { crValue := []byte("custom-records-test-value") customRecords[crKey] = crValue - // TODO(guggero): Actually modify the amount once we have the invoice - // interceptor and can accept a lower amount. - newOutAmountMsat := packet.OutgoingAmountMsat - + // Modify the amount of the HTLC, so we send out less than the original + // amount. + const modifyAmount = 5_000_000 + newOutAmountMsat := packet.OutgoingAmountMsat - modifyAmount err := bobInterceptor.Send(&routerrpc.ForwardHtlcInterceptResponse{ IncomingCircuitKey: packet.IncomingCircuitKey, OutAmountMsat: newOutAmountMsat, @@ -420,6 +426,17 @@ func testForwardInterceptorModifiedHtlc(ht *lntest.HarnessTest) { }) require.NoError(ht, err, "failed to send request") + invoicePacket := ht.ReceiveInvoiceHtlcModification(carolAcceptor) + require.EqualValues( + ht, newOutAmountMsat, invoicePacket.ExitHtlcAmt, + ) + amtPaid := newOutAmountMsat + modifyAmount + err = carolAcceptor.Send(&invoicesrpc.HtlcModifyResponse{ + CircuitKey: invoicePacket.ExitHtlcCircuitKey, + AmtPaid: &amtPaid, + }) + require.NoError(ht, err, "carol acceptor response") + // Cancel the context, which will disconnect Bob's interceptor. cancelBobInterceptor() @@ -473,17 +490,23 @@ func testForwardInterceptorWireRecords(ht *lntest.HarnessTest) { carolInterceptor, cancelCarolInterceptor := carol.RPC.HtlcInterceptor() defer cancelCarolInterceptor() - req := &lnrpc.Invoice{ValueMsat: 1000} + // We're going to modify the payment amount and want Dave to accept the + // payment, so we set up an invoice acceptor on Dave. + daveAcceptor, daveCancel := dave.RPC.InvoiceHtlcModifier() + defer daveCancel() + + req := &lnrpc.Invoice{ValueMsat: 20_000_000} addResponse := dave.RPC.AddInvoice(req) invoice := dave.RPC.LookupInvoice(addResponse.RHash) + customRecords := map[uint64][]byte{ + 65537: []byte("test"), + } sendReq := &routerrpc.SendPaymentRequest{ - PaymentRequest: invoice.PaymentRequest, - TimeoutSeconds: int32(wait.PaymentTimeout.Seconds()), - FeeLimitMsat: noFeeLimitMsat, - FirstHopCustomRecords: map[uint64][]byte{ - 65537: []byte("test"), - }, + PaymentRequest: invoice.PaymentRequest, + TimeoutSeconds: int32(wait.PaymentTimeout.Seconds()), + FeeLimitMsat: noFeeLimitMsat, + FirstHopCustomRecords: customRecords, } _ = alice.RPC.SendPayment(sendReq) @@ -499,14 +522,10 @@ func testForwardInterceptorWireRecords(ht *lntest.HarnessTest) { require.True(ht, ok, "expected custom record") require.Equal(ht, []byte("test"), val) - // TODO(guggero): Actually modify the amount once we have the invoice - // interceptor and can accept a lower amount. - newOutAmountMsat := packet.OutgoingAmountMsat - + // Just resume the payment on Bob. err := bobInterceptor.Send(&routerrpc.ForwardHtlcInterceptResponse{ IncomingCircuitKey: packet.IncomingCircuitKey, - OutAmountMsat: newOutAmountMsat, - Action: actionResumeModify, + Action: actionResume, }) require.NoError(ht, err, "failed to send request") @@ -515,13 +534,32 @@ func testForwardInterceptorWireRecords(ht *lntest.HarnessTest) { packet = ht.ReceiveHtlcInterceptor(carolInterceptor) require.Len(ht, packet.InWireCustomRecords, 0) - // Just resume the payment on Carol. + // We're going to tell Carol to forward 5k sats less to Dave. We need to + // set custom records on the HTLC as well, to make sure the HTLC isn't + // rejected outright and actually gets to the invoice acceptor. + const modifyAmount = 5_000_000 + newOutAmountMsat := packet.OutgoingAmountMsat - modifyAmount err = carolInterceptor.Send(&routerrpc.ForwardHtlcInterceptResponse{ - IncomingCircuitKey: packet.IncomingCircuitKey, - Action: actionResume, + IncomingCircuitKey: packet.IncomingCircuitKey, + OutAmountMsat: newOutAmountMsat, + OutWireCustomRecords: customRecords, + Action: actionResumeModify, }) require.NoError(ht, err, "carol interceptor response") + // The payment should get to Dave, and we should be able to intercept + // and modify it, telling Dave to accept it. + invoicePacket := ht.ReceiveInvoiceHtlcModification(daveAcceptor) + require.EqualValues( + ht, newOutAmountMsat, invoicePacket.ExitHtlcAmt, + ) + amtPaid := newOutAmountMsat + modifyAmount + err = daveAcceptor.Send(&invoicesrpc.HtlcModifyResponse{ + CircuitKey: invoicePacket.ExitHtlcCircuitKey, + AmtPaid: &amtPaid, + }) + require.NoError(ht, err, "dave acceptor response") + // Assert that the payment was successful. var preimage lntypes.Preimage copy(preimage[:], invoice.RPreimage) diff --git a/itest/lnd_invoice_acceptor_test.go b/itest/lnd_invoice_acceptor_test.go new file mode 100644 index 0000000000..5a4a35ba05 --- /dev/null +++ b/itest/lnd_invoice_acceptor_test.go @@ -0,0 +1,358 @@ +package itest + +import ( + "context" + "time" + + "github.com/btcsuite/btcd/btcutil" + "github.com/lightningnetwork/lnd/chainreg" + "github.com/lightningnetwork/lnd/invoices" + "github.com/lightningnetwork/lnd/lnrpc" + "github.com/lightningnetwork/lnd/lnrpc/invoicesrpc" + "github.com/lightningnetwork/lnd/lnrpc/routerrpc" + "github.com/lightningnetwork/lnd/lntest" + "github.com/lightningnetwork/lnd/lntest/node" + "github.com/lightningnetwork/lnd/lnwire" + "github.com/lightningnetwork/lnd/routing/route" + "github.com/stretchr/testify/require" +) + +// testInvoiceHtlcModifierBasic tests the basic functionality of the invoice +// HTLC modifier RPC server. +func testInvoiceHtlcModifierBasic(ht *lntest.HarnessTest) { + ts := newAcceptorTestScenario(ht) + + alice, bob, carol := ts.alice, ts.bob, ts.carol + + // Open and wait for channels. + const chanAmt = btcutil.Amount(300000) + p := lntest.OpenChannelParams{Amt: chanAmt} + reqs := []*lntest.OpenChannelRequest{ + {Local: alice, Remote: bob, Param: p}, + {Local: bob, Remote: carol, Param: p}, + } + resp := ht.OpenMultiChannelsAsync(reqs) + cpAB, cpBC := resp[0], resp[1] + + // Make sure Alice is aware of channel Bob=>Carol. + ht.AssertTopologyChannelOpen(alice, cpBC) + + // Initiate Carol's invoice HTLC modifier. + invoiceModifier, cancelModifier := carol.RPC.InvoiceHtlcModifier() + + // We need to wait a bit to make sure the gRPC stream is established + // correctly. + time.Sleep(50 * time.Millisecond) + + // Make sure we get an error if we try to register a second modifier and + // then try to use it (the error won't be returned on connect, only on + // the first _read_ interaction on the stream). + mod2, err := carol.RPC.Invoice.HtlcModifier(context.Background()) + require.NoError(ht, err) + _, err = mod2.Recv() + require.ErrorContains( + ht, err, + invoices.ErrInterceptorClientAlreadyConnected.Error(), + ) + + // We also add a normal (forwarding) HTLC interceptor at Bob, so we can + // test that custom wire messages on the HTLC are forwarded correctly to + // the invoice HTLC interceptor. + bobInterceptor, bobInterceptorCancel := bob.RPC.HtlcInterceptor() + + // Prepare the test cases. + testCases := ts.prepareTestCases() + + for tcIdx, tc := range testCases { + ht.Logf("Running test case: %d", tcIdx) + + // Initiate a payment from Alice to Carol in a separate + // goroutine. We use a separate goroutine to avoid blocking the + // main goroutine where we will make use of the invoice + // acceptor. + sendPaymentDone := make(chan struct{}) + go func() { + // Signal that all the payments have been sent. + defer close(sendPaymentDone) + + _ = ts.sendPayment(tc) + }() + + // First, intercept the HTLC at Bob. + packet := ht.ReceiveHtlcInterceptor(bobInterceptor) + err := bobInterceptor.Send( + &routerrpc.ForwardHtlcInterceptResponse{ + IncomingCircuitKey: packet.IncomingCircuitKey, + OutAmountMsat: packet.OutgoingAmountMsat, + OutWireCustomRecords: tc.lastHopCustomRecords, + Action: actionResumeModify, + }, + ) + require.NoError(ht, err, "failed to send request") + + modifierRequest := ht.ReceiveInvoiceHtlcModification( + invoiceModifier, + ) + + // Sanity check the modifier request. + require.EqualValues( + ht, tc.invoiceAmountMsat, + modifierRequest.Invoice.ValueMsat, + ) + require.EqualValues( + ht, tc.sendAmountMsat, modifierRequest.ExitHtlcAmt, + ) + require.Equal( + ht, tc.lastHopCustomRecords, + modifierRequest.ExitHtlcWireCustomRecords, + ) + + // For all other packets we resolve according to the test case. + amtPaid := uint64(tc.invoiceAmountMsat) + err = invoiceModifier.Send( + &invoicesrpc.HtlcModifyResponse{ + CircuitKey: modifierRequest.ExitHtlcCircuitKey, + AmtPaid: &amtPaid, + }, + ) + require.NoError(ht, err, "failed to send request") + + ht.Log("Waiting for payment send to complete") + select { + case <-sendPaymentDone: + ht.Log("Payment send attempt complete") + case <-time.After(defaultTimeout): + require.Fail(ht, "timeout waiting for payment send") + } + + ht.Log("Ensure invoice status is settled") + require.Eventually(ht, func() bool { + updatedInvoice := carol.RPC.LookupInvoice( + tc.invoice.RHash, + ) + + return updatedInvoice.State == tc.finalInvoiceState + }, defaultTimeout, 1*time.Second) + + updatedInvoice := carol.RPC.LookupInvoice( + tc.invoice.RHash, + ) + + require.Len(ht, updatedInvoice.Htlcs, 1) + require.Equal( + ht, tc.lastHopCustomRecords, + updatedInvoice.Htlcs[0].CustomRecords, + ) + + // Make sure the custom channel data contains the encoded + // version of the custom records. + customRecords := lnwire.CustomRecords( + updatedInvoice.Htlcs[0].CustomRecords, + ) + encodedRecords, err := customRecords.Serialize() + require.NoError(ht, err) + + require.Equal( + ht, encodedRecords, + updatedInvoice.Htlcs[0].CustomChannelData, + ) + } + + // We don't need the HTLC interceptor at Bob anymore. + bobInterceptorCancel() + + // After the normal test cases, we test that we can shut down Carol + // while an HTLC interception is going on. We initiate a payment from + // Alice to Carol in a separate goroutine. We use a separate goroutine + // to avoid blocking the main goroutine where we will make use of the + // invoice acceptor. + sendPaymentDone := make(chan struct{}) + tc := &acceptorTestCase{ + invoiceAmountMsat: 9000, + sendAmountMsat: 9000, + } + ts.createInvoice(tc) + + go func() { + // Signal that all the payments have been sent. + defer close(sendPaymentDone) + + _ = ts.sendPayment(tc) + }() + + modifierRequest := ht.ReceiveInvoiceHtlcModification(invoiceModifier) + + // Sanity check the modifier request. + require.EqualValues( + ht, tc.invoiceAmountMsat, modifierRequest.Invoice.ValueMsat, + ) + require.EqualValues( + ht, tc.sendAmountMsat, modifierRequest.ExitHtlcAmt, + ) + + // We don't send a response to the modifier, but instead shut down and + // restart Carol. + restart := ht.SuspendNode(carol) + require.NoError(ht, restart()) + + ht.Log("Waiting for payment send to complete") + select { + case <-sendPaymentDone: + ht.Log("Payment send attempt complete") + case <-time.After(defaultTimeout): + require.Fail(ht, "timeout waiting for payment send") + } + + cancelModifier() + + // Finally, close channels. + ht.CloseChannel(alice, cpAB) + ht.CloseChannel(bob, cpBC) +} + +// acceptorTestCase is a helper struct to hold test case data. +type acceptorTestCase struct { + // invoiceAmountMsat is the amount of the invoice. + invoiceAmountMsat int64 + + // sendAmountMsat is the amount that will be sent in the payment. + sendAmountMsat int64 + + // lastHopCustomRecords is a map of custom records that will be added + // to the last hop of the payment, by an HTLC interceptor at Bob. + lastHopCustomRecords map[uint64][]byte + + // finalInvoiceState is the expected eventual final state of the + // invoice. + finalInvoiceState lnrpc.Invoice_InvoiceState + + // payAddr is the payment address of the invoice. + payAddr []byte + + // invoice is the invoice that will be paid. + invoice *lnrpc.Invoice +} + +// acceptorTestScenario is a helper struct to hold the test context and provides +// helpful functionality. +type acceptorTestScenario struct { + ht *lntest.HarnessTest + alice, bob, carol *node.HarnessNode +} + +// newAcceptorTestScenario initializes a new test scenario with three nodes and +// connects them to have the following topology, +// +// Alice --> Bob --> Carol +// +// Among them, Alice and Bob are standby nodes and Carol is a new node. +func newAcceptorTestScenario(ht *lntest.HarnessTest) *acceptorTestScenario { + alice, bob := ht.Alice, ht.Bob + carol := ht.NewNode("carol", nil) + + ht.EnsureConnected(alice, bob) + ht.EnsureConnected(bob, carol) + + return &acceptorTestScenario{ + ht: ht, + alice: alice, + bob: bob, + carol: carol, + } +} + +// prepareTestCases prepares test cases. +func (c *acceptorTestScenario) prepareTestCases() []*acceptorTestCase { + cases := []*acceptorTestCase{ + // Send a payment with amount less than the invoice amount. + // Amount checking is skipped during the invoice settlement + // process. The sent payment should eventually result in the + // invoice being settled. + { + invoiceAmountMsat: 9000, + sendAmountMsat: 1000, + finalInvoiceState: lnrpc.Invoice_SETTLED, + }, + { + invoiceAmountMsat: 9000, + sendAmountMsat: 1000, + finalInvoiceState: lnrpc.Invoice_SETTLED, + lastHopCustomRecords: map[uint64][]byte{ + lnwire.MinCustomRecordsTlvType: {1, 2, 3}, + }, + }, + } + + for _, t := range cases { + c.createInvoice(t) + } + + return cases +} + +// createInvoice creates an invoice for the given test case. +func (c *acceptorTestScenario) createInvoice(tc *acceptorTestCase) { + inv := &lnrpc.Invoice{ValueMsat: tc.invoiceAmountMsat} + addResponse := c.carol.RPC.AddInvoice(inv) + invoice := c.carol.RPC.LookupInvoice(addResponse.RHash) + + // We'll need to also decode the returned invoice so we can grab the + // payment address which is now required for ALL payments. + payReq := c.carol.RPC.DecodePayReq(invoice.PaymentRequest) + + tc.invoice = invoice + tc.payAddr = payReq.PaymentAddr +} + +// buildRoute is a helper function to build a route with given hops. +func (c *acceptorTestScenario) buildRoute(amtMsat int64, + hops []*node.HarnessNode, payAddr []byte) *lnrpc.Route { + + rpcHops := make([][]byte, 0, len(hops)) + for _, hop := range hops { + k := hop.PubKeyStr + pubkey, err := route.NewVertexFromStr(k) + require.NoErrorf(c.ht, err, "error parsing %v: %v", k, err) + rpcHops = append(rpcHops, pubkey[:]) + } + + req := &routerrpc.BuildRouteRequest{ + AmtMsat: amtMsat, + FinalCltvDelta: chainreg.DefaultBitcoinTimeLockDelta, + HopPubkeys: rpcHops, + PaymentAddr: payAddr, + } + + routeResp := c.alice.RPC.BuildRoute(req) + + return routeResp.Route +} + +// sendPaymentAndAssertAction sends a payment from alice to carol. +func (c *acceptorTestScenario) sendPayment( + tc *acceptorTestCase) *lnrpc.HTLCAttempt { + + // Build a route from alice to carol. + aliceBobCarolRoute := c.buildRoute( + tc.sendAmountMsat, []*node.HarnessNode{c.bob, c.carol}, + tc.payAddr, + ) + + // We need to cheat a bit. We are attempting to pay an invoice with + // amount X with an HTLC of amount Y that is less than X. And then we + // use the invoice HTLC interceptor to simulate the HTLC actually + // carrying amount X (even though the actual HTLC transaction output + // only has amount Y). But in order for the invoice to be settled, we + // need to make sure that the MPP total amount record in the last hop + // is set to the invoice amount. This would also be the case in a normal + // MPP payment, where each shard only pays a fraction of the invoice. + aliceBobCarolRoute.Hops[1].MppRecord.TotalAmtMsat = tc.invoiceAmountMsat + + // Send the payment. + sendReq := &routerrpc.SendToRouteRequest{ + PaymentHash: tc.invoice.RHash, + Route: aliceBobCarolRoute, + } + + return c.alice.RPC.SendToRouteV2(sendReq) +} From 6feb189a21feb3e160f92e2cbfb2ab63d5c62d14 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Wed, 29 May 2024 19:57:37 +0200 Subject: [PATCH 128/218] lnwire: add CustomRecords to shutdown message --- lnwire/lnwire_test.go | 9 +-- lnwire/shutdown.go | 46 ++++++++----- lnwire/shutdown_test.go | 145 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 178 insertions(+), 22 deletions(-) create mode 100644 lnwire/shutdown_test.go diff --git a/lnwire/lnwire_test.go b/lnwire/lnwire_test.go index e941962d57..828ddec67a 100644 --- a/lnwire/lnwire_test.go +++ b/lnwire/lnwire_test.go @@ -428,7 +428,7 @@ func randCustomRecords(t *testing.T, r *rand.Rand) CustomRecords { key := MinCustomRecordsTlvType + keyOffset // Values are byte slices of any length. - value := make([]byte, r.Intn(100)) + value := make([]byte, r.Intn(10)) _, err := r.Read(value) require.NoError(t, err) @@ -771,7 +771,6 @@ func TestLightningWireProtocol(t *testing.T) { req := Shutdown{ ChannelID: ChannelID(c), Address: shutdownAddr, - ExtraData: make([]byte, 0), } if r.Int31()%2 == 0 { @@ -933,12 +932,14 @@ func TestLightningWireProtocol(t *testing.T) { // Only create the slice if there will be any signatures // in it to prevent false positive test failures due to // an empty slice versus a nil slice. - numSigs := uint16(r.Int31n(1019)) + numSigs := uint16(r.Int31n(500)) if numSigs > 0 { req.HtlcSigs = make([]Sig, numSigs) } for i := 0; i < int(numSigs); i++ { - req.HtlcSigs[i], err = NewSigFromSignature(testSig) + req.HtlcSigs[i], err = NewSigFromSignature( + testSig, + ) if err != nil { t.Fatalf("unable to parse sig: %v", err) return diff --git a/lnwire/shutdown.go b/lnwire/shutdown.go index c5455651b8..b9899fcfb9 100644 --- a/lnwire/shutdown.go +++ b/lnwire/shutdown.go @@ -38,6 +38,11 @@ type Shutdown struct { // co-op sign offer. ShutdownNonce ShutdownNonceTLV + // CustomRecords maps TLV types to byte slices, storing arbitrary data + // intended for inclusion in the ExtraData field of the Shutdown + // message. + CustomRecords CustomRecords + // ExtraData is the set of data that was appended to this message to // fill out the full maximum transport message size. These fields can // be used to specify optional data such as custom TLV fields. @@ -56,7 +61,7 @@ func NewShutdown(cid ChannelID, addr DeliveryAddress) *Shutdown { // interface. var _ Message = (*Shutdown)(nil) -// Decode deserializes a serialized Shutdown stored in the passed io.Reader +// Decode deserializes a serialized Shutdown from the passed io.Reader, // observing the specified protocol version. // // This is part of the lnwire.Message interface. @@ -71,20 +76,23 @@ func (s *Shutdown) Decode(r io.Reader, pver uint32) error { return err } + // Extract TLV records from the extra data field. musigNonce := s.ShutdownNonce.Zero() - typeMap, err := tlvRecords.ExtractRecords(&musigNonce) + + customRecords, parsed, extraData, err := ParseAndExtractCustomRecords( + tlvRecords, &musigNonce, + ) if err != nil { return err } - // Set the corresponding TLV types if they were included in the stream. - if val, ok := typeMap[s.ShutdownNonce.TlvType()]; ok && val == nil { + // Assign the parsed records back to the message. + if _, ok := parsed[musigNonce.TlvType()]; ok { s.ShutdownNonce = tlv.SomeRecordT(musigNonce) } - if len(tlvRecords) != 0 { - s.ExtraData = tlvRecords - } + s.CustomRecords = customRecords + s.ExtraData = extraData return nil } @@ -94,26 +102,28 @@ func (s *Shutdown) Decode(r io.Reader, pver uint32) error { // // This is part of the lnwire.Message interface. func (s *Shutdown) Encode(w *bytes.Buffer, pver uint32) error { - recordProducers := make([]tlv.RecordProducer, 0, 1) - s.ShutdownNonce.WhenSome( - func(nonce tlv.RecordT[ShutdownNonceType, Musig2Nonce]) { - recordProducers = append(recordProducers, &nonce) - }, - ) - err := EncodeMessageExtraData(&s.ExtraData, recordProducers...) - if err != nil { + if err := WriteChannelID(w, s.ChannelID); err != nil { return err } - if err := WriteChannelID(w, s.ChannelID); err != nil { + if err := WriteDeliveryAddress(w, s.Address); err != nil { return err } - if err := WriteDeliveryAddress(w, s.Address); err != nil { + // Only include nonce in extra data if present. + var records []tlv.RecordProducer + s.ShutdownNonce.WhenSome( + func(nonce tlv.RecordT[ShutdownNonceType, Musig2Nonce]) { + records = append(records, &nonce) + }, + ) + + extraData, err := MergeAndEncode(records, s.ExtraData, s.CustomRecords) + if err != nil { return err } - return WriteBytes(w, s.ExtraData) + return WriteBytes(w, extraData) } // MsgType returns the integer uniquely identifying this message type on the diff --git a/lnwire/shutdown_test.go b/lnwire/shutdown_test.go new file mode 100644 index 0000000000..7275efc9fb --- /dev/null +++ b/lnwire/shutdown_test.go @@ -0,0 +1,145 @@ +package lnwire + +import ( + "bytes" + "fmt" + "testing" + + "github.com/btcsuite/btcd/btcec/v2/schnorr/musig2" + "github.com/lightningnetwork/lnd/tlv" + "github.com/stretchr/testify/require" +) + +// testCaseShutdown is a test case for the Shutdown message. +type testCaseShutdown struct { + // Msg is the message to be encoded and decoded. + Msg Shutdown + + // ExpectEncodeError is a flag that indicates whether we expect the + // encoding of the message to fail. + ExpectEncodeError bool +} + +// generateShutdownTestCases generates a set of Shutdown message test cases. +func generateShutdownTestCases(t *testing.T) []testCaseShutdown { + // Firstly, we'll set basic values for the message fields. + // + // Generate random channel ID. + chanIDBytes, err := generateRandomBytes(32) + require.NoError(t, err) + + var chanID ChannelID + copy(chanID[:], chanIDBytes) + + // Generate random payment preimage. + paymentPreimageBytes, err := generateRandomBytes(32) + require.NoError(t, err) + + var paymentPreimage [32]byte + copy(paymentPreimage[:], paymentPreimageBytes) + + deliveryAddr, err := generateRandomBytes(16) + require.NoError(t, err) + + // Define custom records. + recordKey1 := uint64(MinCustomRecordsTlvType + 1) + recordValue1, err := generateRandomBytes(10) + require.NoError(t, err) + + recordKey2 := uint64(MinCustomRecordsTlvType + 2) + recordValue2, err := generateRandomBytes(10) + require.NoError(t, err) + + customRecords := CustomRecords{ + recordKey1: recordValue1, + recordKey2: recordValue2, + } + + dummyPubKey, err := pubkeyFromHex( + "0228f2af0abe322403480fb3ee172f7f1601e67d1da6cad40b54c4468d4" + + "8236c39", + ) + require.NoError(t, err) + + muSig2Nonce, err := musig2.GenNonces(musig2.WithPublicKey(dummyPubKey)) + require.NoError(t, err) + + // Construct an instance of extra data that contains records with TLV + // types below the minimum custom records threshold and that lack + // corresponding fields in the message struct. Content should persist in + // the extra data field after encoding and decoding. + var ( + recordBytes45 = []byte("recordBytes45") + tlvRecord45 = tlv.NewPrimitiveRecord[tlv.TlvType45]( + recordBytes45, + ) + + recordBytes55 = []byte("recordBytes55") + tlvRecord55 = tlv.NewPrimitiveRecord[tlv.TlvType55]( + recordBytes55, + ) + ) + + var extraData ExtraOpaqueData + err = extraData.PackRecords( + []tlv.RecordProducer{&tlvRecord45, &tlvRecord55}..., + ) + require.NoError(t, err) + + return []testCaseShutdown{ + { + Msg: Shutdown{ + ChannelID: chanID, + CustomRecords: customRecords, + ExtraData: extraData, + Address: deliveryAddr, + }, + }, + { + Msg: Shutdown{ + ChannelID: chanID, + CustomRecords: customRecords, + ExtraData: extraData, + Address: deliveryAddr, + ShutdownNonce: SomeShutdownNonce( + muSig2Nonce.PubNonce, + ), + }, + }, + } +} + +// TestShutdownEncodeDecode tests Shutdown message encoding and decoding for all +// supported field values. +func TestShutdownEncodeDecode(t *testing.T) { + t.Parallel() + + // Generate test cases. + testCases := generateShutdownTestCases(t) + + // Execute test cases. + for tcIdx, tc := range testCases { + t.Run(fmt.Sprintf("testcase-%d", tcIdx), func(t *testing.T) { + // Encode test case message. + var buf bytes.Buffer + err := tc.Msg.Encode(&buf, 0) + + // Check if we expect an encoding error. + if tc.ExpectEncodeError { + require.Error(t, err) + return + } + + require.NoError(t, err) + + // Decode the encoded message bytes message. + var actualMsg Shutdown + decodeReader := bytes.NewReader(buf.Bytes()) + err = actualMsg.Decode(decodeReader, 0) + require.NoError(t, err) + + // Compare the two messages to ensure equality. + require.Equal(t, tc.Msg, actualMsg) + }) + } +} From 7e4f6f5b4a489c63d1461bb1497dd426ea0c7c49 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Wed, 29 May 2024 19:57:38 +0200 Subject: [PATCH 129/218] lnwallet: add FundingBlob method to LightningChannel --- lnwallet/channel.go | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/lnwallet/channel.go b/lnwallet/channel.go index c78742d139..b509a45356 100644 --- a/lnwallet/channel.go +++ b/lnwallet/channel.go @@ -9095,3 +9095,16 @@ func (lc *LightningChannel) LocalCommitmentBlob() fn.Option[tlv.Blob] { return newBlob })(localBalance) } + +// FundingBlob returns the funding custom blob. +func (lc *LightningChannel) FundingBlob() fn.Option[tlv.Blob] { + lc.RLock() + defer lc.RUnlock() + + return fn.MapOption(func(b tlv.Blob) tlv.Blob { + newBlob := make([]byte, len(b)) + copy(newBlob, b) + + return newBlob + })(lc.channelState.CustomBlob) +} From 0cb211e14c2d5dc667775e45a82ff6edbd1359d0 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Wed, 29 May 2024 19:57:39 +0200 Subject: [PATCH 130/218] lnwallet: add ability to add extra co-op close outputs --- lnwallet/channel.go | 153 ++++++++++++++++++- lnwallet/channel_test.go | 313 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 460 insertions(+), 6 deletions(-) diff --git a/lnwallet/channel.go b/lnwallet/channel.go index b509a45356..6f41408359 100644 --- a/lnwallet/channel.go +++ b/lnwallet/channel.go @@ -7691,10 +7691,21 @@ func NewLocalForceCloseSummary(chanState *channeldb.OpenChannel, }, nil } +// CloseOutput wraps a normal tx out with additional metadata that indicates if +// the output belongs to the initiator of the channel or not. +type CloseOutput struct { + wire.TxOut + + // IsLocal indicates if the output belong to the local party. + IsLocal bool +} + // chanCloseOpt is a functional option that can be used to modify the co-op // close process. type chanCloseOpt struct { musigSession *MusigSession + + extraCloseOutputs []CloseOutput } // ChanCloseOpt is a closure type that cen be used to modify the set of default @@ -7715,6 +7726,14 @@ func WithCoopCloseMusigSession(session *MusigSession) ChanCloseOpt { } } +// WithExtraCloseOutputs can be used to add extra outputs to the cooperative +// transaction. +func WithExtraCloseOutputs(extraOutputs []CloseOutput) ChanCloseOpt { + return func(opts *chanCloseOpt) { + opts.extraCloseOutputs = extraOutputs + } +} + // CreateCloseProposal is used by both parties in a cooperative channel close // workflow to generate proposed close transactions and signatures. This method // should only be executed once all pending HTLCs (if any) on the channel have @@ -7722,9 +7741,6 @@ func WithCoopCloseMusigSession(session *MusigSession) ChanCloseOpt { // the "closing" state, which indicates that all incoming/outgoing HTLC // requests should be rejected. A signature for the closing transaction is // returned. -// -// TODO(roasbeef): caller should initiate signal to reject all incoming HTLCs, -// settle any in flight. func (lc *LightningChannel) CreateCloseProposal(proposedFee btcutil.Amount, localDeliveryScript []byte, remoteDeliveryScript []byte, closeOpts ...ChanCloseOpt) (input.Signature, *chainhash.Hash, @@ -7735,7 +7751,6 @@ func (lc *LightningChannel) CreateCloseProposal(proposedFee btcutil.Amount, // If we're already closing the channel, then ignore this request. if lc.isClosed { - // TODO(roasbeef): check to ensure no pending payments return nil, nil, 0, ErrChanClosing } @@ -7762,6 +7777,14 @@ func (lc *LightningChannel) CreateCloseProposal(proposedFee btcutil.Amount, closeTxOpts = append(closeTxOpts, WithRBFCloseTx()) } + // If we have any extra outputs to pass along, then we'll map that to + // the co-op close option txn type. + if opts.extraCloseOutputs != nil { + closeTxOpts = append(closeTxOpts, WithExtraTxCloseOutputs( + opts.extraCloseOutputs, + )) + } + closeTx := CreateCooperativeCloseTx( fundingTxIn(lc.channelState), lc.channelState.LocalChanCfg.DustLimit, lc.channelState.RemoteChanCfg.DustLimit, ourBalance, theirBalance, @@ -7844,6 +7867,14 @@ func (lc *LightningChannel) CompleteCooperativeClose( closeTxOpts = append(closeTxOpts, WithRBFCloseTx()) } + // If we have any extra outputs to pass along, then we'll map that to + // the co-op close option txn type. + if opts.extraCloseOutputs != nil { + closeTxOpts = append(closeTxOpts, WithExtraTxCloseOutputs( + opts.extraCloseOutputs, + )) + } + // Create the transaction used to return the current settled balance // on this active channel back to both parties. In this current model, // the initiator pays full fees for the cooperative close transaction. @@ -8549,6 +8580,10 @@ type closeTxOpts struct { // enableRBF indicates whether the cooperative close tx should signal // RBF or not. enableRBF bool + + // extraCloseOutputs is a set of additional outputs that should be + // added the co-op close transaction. + extraCloseOutputs []CloseOutput } // defaultCloseTxOpts returns a closeTxOpts struct with default values. @@ -8569,6 +8604,14 @@ func WithRBFCloseTx() CloseTxOpt { } } +// WithExtraTxCloseOutputs can be used to add extra outputs to the cooperative +// transaction. +func WithExtraTxCloseOutputs(extraOutputs []CloseOutput) CloseTxOpt { + return func(o *closeTxOpts) { + o.extraCloseOutputs = extraOutputs + } +} + // CreateCooperativeCloseTx creates a transaction which if signed by both // parties, then broadcast cooperatively closes an active channel. The creation // of the closure transaction is modified by a boolean indicating if the party @@ -8600,17 +8643,115 @@ func CreateCooperativeCloseTx(fundingTxIn wire.TxIn, // Create both cooperative closure outputs, properly respecting the // dust limits of both parties. - if ourBalance >= localDust { + var localOutputIdx fn.Option[int] + haveLocalOutput := ourBalance >= localDust + if haveLocalOutput { closeTx.AddTxOut(&wire.TxOut{ PkScript: ourDeliveryScript, Value: int64(ourBalance), }) + + localOutputIdx = fn.Some(len(closeTx.TxOut) - 1) } - if theirBalance >= remoteDust { + + var remoteOutputIdx fn.Option[int] + haveRemoteOutput := theirBalance >= remoteDust + if haveRemoteOutput { closeTx.AddTxOut(&wire.TxOut{ PkScript: theirDeliveryScript, Value: int64(theirBalance), }) + + remoteOutputIdx = fn.Some(len(closeTx.TxOut) - 1) + } + + // If we have extra outputs to add to the co-op close transaction, then + // we'll examine them now. We'll deduct the output's value from the + // owning party. In the case that a party can't pay for the output, then + // their normal output will be omitted. + for _, extraTxOut := range opts.extraCloseOutputs { + switch { + // For additional local outputs, add the output, then deduct + // the balance from our local balance. + case extraTxOut.IsLocal: + // The extraCloseOutputs in the options just indicate if + // an extra output should be added in general. But we + // only add one if we actually _need_ one, based on the + // balance. If we don't have enough local balance to + // cover the extra output, then localOutputIdx is None. + localOutputIdx.WhenSome(func(idx int) { + // The output that currently represents the + // local balance, which means: + // txOut.Value == ourBalance. + txOut := closeTx.TxOut[idx] + + // The extra output (if one exists) is the more + // important one, as in custom channels it might + // carry some additional values. The normal + // output is just an address that sends the + // local balance back to our wallet. The extra + // one also goes to our wallet, but might also + // carry other values, so it has higher + // priority. Do we have enough balance to have + // both the extra output with the given value + // (which is subtracted from our balance) and + // still an above-dust normal output? If not, we + // skip the extra output and just overwrite the + // existing output script with the one from the + // extra output. + amtAfterOutput := btcutil.Amount( + txOut.Value - extraTxOut.Value, + ) + if amtAfterOutput <= localDust { + txOut.PkScript = extraTxOut.PkScript + + return + } + + txOut.Value -= extraTxOut.Value + closeTx.AddTxOut(&extraTxOut.TxOut) + }) + + // For extra remote outputs, we'll do the opposite. + case !extraTxOut.IsLocal: + // The extraCloseOutputs in the options just indicate if + // an extra output should be added in general. But we + // only add one if we actually _need_ one, based on the + // balance. If we don't have enough remote balance to + // cover the extra output, then remoteOutputIdx is None. + remoteOutputIdx.WhenSome(func(idx int) { + // The output that currently represents the + // remote balance, which means: + // txOut.Value == theirBalance. + txOut := closeTx.TxOut[idx] + + // The extra output (if one exists) is the more + // important one, as in custom channels it might + // carry some additional values. The normal + // output is just an address that sends the + // remote balance back to their wallet. The + // extra one also goes to their wallet, but + // might also carry other values, so it has + // higher priority. Do they have enough balance + // to have both the extra output with the given + // value (which is subtracted from their + // balance) and still an above-dust normal + // output? If not, we skip the extra output and + // just overwrite the existing output script + // with the one from the extra output. + amtAfterOutput := btcutil.Amount( + txOut.Value - extraTxOut.Value, + ) + if amtAfterOutput <= remoteDust { + txOut.PkScript = extraTxOut.PkScript + + return + } + + txOut.Value -= extraTxOut.Value + closeTx.AddTxOut(&extraTxOut.TxOut) + }) + } } txsort.InPlaceSort(closeTx) diff --git a/lnwallet/channel_test.go b/lnwallet/channel_test.go index 24bda0b019..a5cf044b45 100644 --- a/lnwallet/channel_test.go +++ b/lnwallet/channel_test.go @@ -15,6 +15,7 @@ import ( "github.com/btcsuite/btcd/btcec/v2" "github.com/btcsuite/btcd/btcec/v2/schnorr/musig2" "github.com/btcsuite/btcd/btcutil" + "github.com/btcsuite/btcd/btcutil/txsort" "github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/btcsuite/btcd/txscript" "github.com/btcsuite/btcd/wire" @@ -11435,3 +11436,315 @@ func TestBlindingPointPersistence(t *testing.T) { require.Equal(t, blinding, bobCommit.incomingHTLCs[0].BlindingPoint.UnwrapOrFailV(t)) } + +// TestCreateCooperativeCloseTx tests that the cooperative close transaction is +// properly created based on the standard and also optional parameters. +func TestCreateCooperativeCloseTx(t *testing.T) { + t.Parallel() + + fundingTxIn := &wire.TxIn{} + + localDust := btcutil.Amount(400) + remoteDust := btcutil.Amount(400) + + localScript := []byte{0} + localExtraScript := []byte{2} + + remoteScript := []byte{1} + remoteExtraScript := []byte{3} + + tests := []struct { + name string + + enableRBF bool + + localBalance btcutil.Amount + remoteBalance btcutil.Amount + + extraCloseOutputs []CloseOutput + + expectedTx *wire.MsgTx + }{ + { + name: "no dust, no extra outputs", + localBalance: 1_000, + remoteBalance: 1_000, + expectedTx: &wire.MsgTx{ + TxIn: []*wire.TxIn{ + fundingTxIn, + }, + Version: 2, + TxOut: []*wire.TxOut{ + { + Value: 1_000, + PkScript: localScript, + }, + { + Value: 1_000, + PkScript: remoteScript, + }, + }, + }, + }, + { + name: "local dust, no extra outputs", + localBalance: 100, + remoteBalance: 1_000, + expectedTx: &wire.MsgTx{ + TxIn: []*wire.TxIn{ + fundingTxIn, + }, + Version: 2, + TxOut: []*wire.TxOut{ + { + Value: 1_000, + PkScript: remoteScript, + }, + }, + }, + }, + { + name: "remote dust, no extra outputs", + localBalance: 1_000, + remoteBalance: 100, + expectedTx: &wire.MsgTx{ + TxIn: []*wire.TxIn{ + fundingTxIn, + }, + Version: 2, + TxOut: []*wire.TxOut{ + { + Value: 1_000, + PkScript: localScript, + }, + }, + }, + }, + { + name: "no dust, local extra output", + localBalance: 10_000, + remoteBalance: 10_000, + extraCloseOutputs: []CloseOutput{ + { + TxOut: wire.TxOut{ + Value: 1_000, + PkScript: localExtraScript, + }, + IsLocal: true, + }, + }, + expectedTx: &wire.MsgTx{ + TxIn: []*wire.TxIn{ + fundingTxIn, + }, + Version: 2, + TxOut: []*wire.TxOut{ + { + Value: 10_000, + PkScript: remoteScript, + }, + { + Value: 9_000, + PkScript: localScript, + }, + { + Value: 1_000, + PkScript: localExtraScript, + }, + }, + }, + }, + { + name: "no dust, remote extra output", + localBalance: 10_000, + remoteBalance: 10_000, + extraCloseOutputs: []CloseOutput{ + { + TxOut: wire.TxOut{ + Value: 1_000, + PkScript: remoteExtraScript, + }, + IsLocal: false, + }, + }, + expectedTx: &wire.MsgTx{ + TxIn: []*wire.TxIn{ + fundingTxIn, + }, + Version: 2, + TxOut: []*wire.TxOut{ + { + Value: 10_000, + PkScript: localScript, + }, + { + Value: 9_000, + PkScript: remoteScript, + }, + { + Value: 1_000, + PkScript: remoteExtraScript, + }, + }, + }, + }, + { + name: "no dust, local+remote extra output", + localBalance: 10_000, + remoteBalance: 10_000, + extraCloseOutputs: []CloseOutput{ + { + TxOut: wire.TxOut{ + Value: 1_000, + PkScript: remoteExtraScript, + }, + IsLocal: false, + }, + { + TxOut: wire.TxOut{ + Value: 1_000, + PkScript: localExtraScript, + }, + IsLocal: true, + }, + }, + expectedTx: &wire.MsgTx{ + TxIn: []*wire.TxIn{ + fundingTxIn, + }, + Version: 2, + TxOut: []*wire.TxOut{ + { + Value: 9_000, + PkScript: localScript, + }, + { + Value: 9_000, + PkScript: remoteScript, + }, + { + Value: 1_000, + PkScript: remoteExtraScript, + }, + { + Value: 1_000, + PkScript: localExtraScript, + }, + }, + }, + }, + { + name: "no dust, local+remote extra output, " + + "remote can't afford", + localBalance: 10_000, + remoteBalance: 1_000, + extraCloseOutputs: []CloseOutput{ + { + TxOut: wire.TxOut{ + Value: 1_000, + PkScript: remoteExtraScript, + }, + IsLocal: false, + }, + { + TxOut: wire.TxOut{ + Value: 1_000, + PkScript: localExtraScript, + }, + IsLocal: true, + }, + }, + expectedTx: &wire.MsgTx{ + TxIn: []*wire.TxIn{ + fundingTxIn, + }, + Version: 2, + TxOut: []*wire.TxOut{ + { + Value: 9_000, + PkScript: localScript, + }, + { + Value: 1_000, + PkScript: remoteExtraScript, + }, + { + Value: 1_000, + PkScript: localExtraScript, + }, + }, + }, + }, + { + name: "no dust, local+remote extra output, " + + "local can't afford", + localBalance: 1_000, + remoteBalance: 10_000, + extraCloseOutputs: []CloseOutput{ + { + TxOut: wire.TxOut{ + Value: 1_000, + PkScript: remoteExtraScript, + }, + IsLocal: false, + }, + { + TxOut: wire.TxOut{ + Value: 1_000, + PkScript: localExtraScript, + }, + IsLocal: true, + }, + }, + expectedTx: &wire.MsgTx{ + TxIn: []*wire.TxIn{ + fundingTxIn, + }, + Version: 2, + TxOut: []*wire.TxOut{ + { + Value: 9_000, + PkScript: remoteScript, + }, + { + Value: 1_000, + PkScript: remoteExtraScript, + }, + { + Value: 1_000, + PkScript: localExtraScript, + }, + }, + }, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + var opts []CloseTxOpt + if test.extraCloseOutputs != nil { + opts = append( + opts, + WithExtraTxCloseOutputs( + test.extraCloseOutputs, + ), + ) + } + + closeTx := CreateCooperativeCloseTx( + *fundingTxIn, localDust, remoteDust, + test.localBalance, test.remoteBalance, + localScript, remoteScript, opts..., + ) + + txsort.InPlaceSort(test.expectedTx) + + require.Equal( + t, test.expectedTx, closeTx, + "expected %v, got %v", + spew.Sdump(test.expectedTx), + spew.Sdump(closeTx), + ) + }) + } +} From 2d8c4fda2f6c63f242817a26fe26302a3348554b Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Wed, 29 May 2024 19:57:44 +0200 Subject: [PATCH 131/218] lnwallet: add ability to do custom sort for coop close txn --- lnwallet/channel.go | 62 ++++++++++++++++++++++++++++++++++++---- lnwallet/channel_test.go | 3 +- 2 files changed, 59 insertions(+), 6 deletions(-) diff --git a/lnwallet/channel.go b/lnwallet/channel.go index 6f41408359..c09b20000a 100644 --- a/lnwallet/channel.go +++ b/lnwallet/channel.go @@ -7700,12 +7700,21 @@ type CloseOutput struct { IsLocal bool } +// CloseSortFunc is a function type alias for a function that sorts the closing +// transaction. +type CloseSortFunc func(*wire.MsgTx) error + // chanCloseOpt is a functional option that can be used to modify the co-op // close process. type chanCloseOpt struct { musigSession *MusigSession extraCloseOutputs []CloseOutput + + // customSort is a custom function that can be used to sort the + // transaction outputs. If this isn't set, then the default BIP-69 + // sorting is used. + customSort CloseSortFunc } // ChanCloseOpt is a closure type that cen be used to modify the set of default @@ -7734,6 +7743,14 @@ func WithExtraCloseOutputs(extraOutputs []CloseOutput) ChanCloseOpt { } } +// WithCustomCoopSort can be used to modify the way the co-op close transaction +// is sorted. +func WithCustomCoopSort(sorter CloseSortFunc) ChanCloseOpt { + return func(opts *chanCloseOpt) { + opts.customSort = sorter + } +} + // CreateCloseProposal is used by both parties in a cooperative channel close // workflow to generate proposed close transactions and signatures. This method // should only be executed once all pending HTLCs (if any) on the channel have @@ -7784,12 +7801,20 @@ func (lc *LightningChannel) CreateCloseProposal(proposedFee btcutil.Amount, opts.extraCloseOutputs, )) } + if opts.customSort != nil { + closeTxOpts = append( + closeTxOpts, WithCustomTxSort(opts.customSort), + ) + } - closeTx := CreateCooperativeCloseTx( + closeTx, err := CreateCooperativeCloseTx( fundingTxIn(lc.channelState), lc.channelState.LocalChanCfg.DustLimit, lc.channelState.RemoteChanCfg.DustLimit, ourBalance, theirBalance, localDeliveryScript, remoteDeliveryScript, closeTxOpts..., ) + if err != nil { + return nil, nil, 0, err + } // Ensure that the transaction doesn't explicitly violate any // consensus rules such as being too big, or having any value with a @@ -7874,15 +7899,23 @@ func (lc *LightningChannel) CompleteCooperativeClose( opts.extraCloseOutputs, )) } + if opts.customSort != nil { + closeTxOpts = append( + closeTxOpts, WithCustomTxSort(opts.customSort), + ) + } // Create the transaction used to return the current settled balance // on this active channel back to both parties. In this current model, // the initiator pays full fees for the cooperative close transaction. - closeTx := CreateCooperativeCloseTx( + closeTx, err := CreateCooperativeCloseTx( fundingTxIn(lc.channelState), lc.channelState.LocalChanCfg.DustLimit, lc.channelState.RemoteChanCfg.DustLimit, ourBalance, theirBalance, localDeliveryScript, remoteDeliveryScript, closeTxOpts..., ) + if err != nil { + return nil, 0, err + } // Ensure that the transaction doesn't explicitly validate any // consensus rules such as being too big, or having any value with a @@ -8584,6 +8617,11 @@ type closeTxOpts struct { // extraCloseOutputs is a set of additional outputs that should be // added the co-op close transaction. extraCloseOutputs []CloseOutput + + // customSort is a custom function that can be used to sort the + // transaction outputs. If this isn't set, then the default BIP-69 + // sorting is used. + customSort CloseSortFunc } // defaultCloseTxOpts returns a closeTxOpts struct with default values. @@ -8612,6 +8650,14 @@ func WithExtraTxCloseOutputs(extraOutputs []CloseOutput) CloseTxOpt { } } +// WithCustomTxSort can be used to modify the way the close transaction is +// sorted. +func WithCustomTxSort(sorter CloseSortFunc) CloseTxOpt { + return func(opts *closeTxOpts) { + opts.customSort = sorter + } +} + // CreateCooperativeCloseTx creates a transaction which if signed by both // parties, then broadcast cooperatively closes an active channel. The creation // of the closure transaction is modified by a boolean indicating if the party @@ -8621,7 +8667,7 @@ func WithExtraTxCloseOutputs(extraOutputs []CloseOutput) CloseTxOpt { func CreateCooperativeCloseTx(fundingTxIn wire.TxIn, localDust, remoteDust, ourBalance, theirBalance btcutil.Amount, ourDeliveryScript, theirDeliveryScript []byte, - closeOpts ...CloseTxOpt) *wire.MsgTx { + closeOpts ...CloseTxOpt) (*wire.MsgTx, error) { opts := defaultCloseTxOpts() for _, optFunc := range closeOpts { @@ -8754,9 +8800,15 @@ func CreateCooperativeCloseTx(fundingTxIn wire.TxIn, } } - txsort.InPlaceSort(closeTx) + if opts.customSort != nil { + if err := opts.customSort(closeTx); err != nil { + return nil, err + } + } else { + txsort.InPlaceSort(closeTx) + } - return closeTx + return closeTx, nil } // LocalBalanceDust returns true if when creating a co-op close transaction, diff --git a/lnwallet/channel_test.go b/lnwallet/channel_test.go index a5cf044b45..54ca723e27 100644 --- a/lnwallet/channel_test.go +++ b/lnwallet/channel_test.go @@ -11731,11 +11731,12 @@ func TestCreateCooperativeCloseTx(t *testing.T) { ) } - closeTx := CreateCooperativeCloseTx( + closeTx, err := CreateCooperativeCloseTx( *fundingTxIn, localDust, remoteDust, test.localBalance, test.remoteBalance, localScript, remoteScript, opts..., ) + require.NoError(t, err) txsort.InPlaceSort(test.expectedTx) From e2dae56698d7a1599e878aa35a47ceaa66ee4af3 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Wed, 29 May 2024 19:57:40 +0200 Subject: [PATCH 132/218] lnwallet/chancloser: add new AuxChanCloser interface --- lnwallet/chancloser/aux_closer.go | 104 ++++++++++++++++++++++++++++++ 1 file changed, 104 insertions(+) create mode 100644 lnwallet/chancloser/aux_closer.go diff --git a/lnwallet/chancloser/aux_closer.go b/lnwallet/chancloser/aux_closer.go new file mode 100644 index 0000000000..8b1c445ca3 --- /dev/null +++ b/lnwallet/chancloser/aux_closer.go @@ -0,0 +1,104 @@ +package chancloser + +import ( + "github.com/btcsuite/btcd/btcec/v2" + "github.com/btcsuite/btcd/btcutil" + "github.com/btcsuite/btcd/wire" + "github.com/lightningnetwork/lnd/fn" + "github.com/lightningnetwork/lnd/lnwallet" + "github.com/lightningnetwork/lnd/lnwire" + "github.com/lightningnetwork/lnd/tlv" +) + +// CloseOutput represents an output that should be included in the close +// transaction. +type CloseOutput struct { + // Amt is the amount of the output. + Amt btcutil.Amount + + // DustLimit is the dust limit for the local node. + DustLimit btcutil.Amount + + // PkScript is the script that should be used to pay to the output. + PkScript []byte + + // ShutdownRecords is the set of custom records that may result in + // extra close outputs being added. + ShutdownRecords lnwire.CustomRecords +} + +// AuxShutdownReq is used to request a set of extra custom records to include +// in the shutdown message. +type AuxShutdownReq struct { + // ChanPoint is the channel point of the channel that is being shut + // down. + ChanPoint wire.OutPoint + + // ShortChanID is the short channel ID of the channel that is being + // closed. + ShortChanID lnwire.ShortChannelID + + // Initiator is true if the local node is the initiator of the channel. + Initiator bool + + // InternalKey is the internal key for the shutdown addr. This will + // only be set for taproot shutdown addrs. + InternalKey fn.Option[btcec.PublicKey] + + // CommitBlob is the blob that was included in the last commitment. + CommitBlob fn.Option[tlv.Blob] + + // FundingBlob is the blob that was included in the funding state. + FundingBlob fn.Option[tlv.Blob] +} + +// AuxCloseDesc is used to describe the channel close that is being performed. +type AuxCloseDesc struct { + AuxShutdownReq + + // CloseFee is the closing fee to be paid for this state. + CloseFee btcutil.Amount + + // CommitFee is the fee that was paid for the last commitment. + CommitFee btcutil.Amount + + // LocalCloseOutput is the output that the local node should be paid + // to. This is None if the local party will not have an output on the + // co-op close transaction. + LocalCloseOutput fn.Option[CloseOutput] + + // RemoteCloseOutput is the output that the remote node should be paid + // to. This will be None if the remote party will not have an output on + // the co-op close transaction. + RemoteCloseOutput fn.Option[CloseOutput] +} + +// AuxCloseOutputs is used to specify extra outputs that should be used when +// constructing the co-op close transaction. +type AuxCloseOutputs struct { + // ExtraCloseOutputs is a set of extra outputs that should be included + // in the close transaction. + ExtraCloseOutputs []lnwallet.CloseOutput + + // CustomSort is a custom function that can be used to sort the + // transaction outputs. If this isn't set, then the default BIP-69 + // sorting is used. + CustomSort lnwallet.CloseSortFunc +} + +// AuxChanCloser is used to allow an external caller to modify the co-op close +// transaction. +type AuxChanCloser interface { + // ShutdownBlob returns the set of custom records that should be + // included in the shutdown message. + ShutdownBlob(req AuxShutdownReq) (fn.Option[lnwire.CustomRecords], + error) + + // AuxCloseOutputs returns the set of custom outputs that should be used + // to construct the co-op close transaction. + AuxCloseOutputs(desc AuxCloseDesc) (fn.Option[AuxCloseOutputs], error) + + // FinalizeClose is called after the close transaction has been agreed + // upon. + FinalizeClose(desc AuxCloseDesc, closeTx *wire.MsgTx) error +} From e536cfad0fe1b97df0bd78e038820c643da9f960 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Wed, 29 May 2024 19:57:42 +0200 Subject: [PATCH 133/218] lnwallet/chancloser: add aux chan closer, use in coop flow --- lnwallet/chancloser/chancloser.go | 190 ++++++++++++++++++++++++- lnwallet/chancloser/chancloser_test.go | 25 +++- lnwallet/chancloser/interface.go | 28 +++- lnwallet/channel.go | 43 +++++- 4 files changed, 269 insertions(+), 17 deletions(-) diff --git a/lnwallet/chancloser/chancloser.go b/lnwallet/chancloser/chancloser.go index 57033d4b36..5c73e56fdf 100644 --- a/lnwallet/chancloser/chancloser.go +++ b/lnwallet/chancloser/chancloser.go @@ -140,6 +140,10 @@ type ChanCloseCfg struct { // FeeEstimator is used to estimate the absolute starting co-op close // fee. FeeEstimator CoopFeeEstimator + + // AuxCloser is an optional interface that can be used to modify the + // way the co-op close process proceeds. + AuxCloser fn.Option[AuxChanCloser] } // ChanCloser is a state machine that handles the cooperative channel closure @@ -215,6 +219,20 @@ type ChanCloser struct { // we use to handle a specific race condition caused by the independent // message processing queues. cachedClosingSigned fn.Option[lnwire.ClosingSigned] + + // localCloseOutput is the local output on the closing transaction that + // the local party should be paid to. This will only be populated if the + // local balance isn't dust. + localCloseOutput fn.Option[CloseOutput] + + // remoteCloseOutput is the remote output on the closing transaction + // that the remote party should be paid to. This will only be populated + // if the remote balance isn't dust. + remoteCloseOutput fn.Option[CloseOutput] + + // auxOutputs are the optional additional outputs that might be added to + // the closing transaction. + auxOutputs fn.Option[AuxCloseOutputs] } // calcCoopCloseFee computes an "ideal" absolute co-op close fee given the @@ -295,13 +313,13 @@ func (c *ChanCloser) initFeeBaseline() { // Depending on if a balance ends up being dust or not, we'll pass a // nil TxOut into the EstimateFee call which can handle it. var localTxOut, remoteTxOut *wire.TxOut - if !c.cfg.Channel.LocalBalanceDust() { + if isDust, _ := c.cfg.Channel.LocalBalanceDust(); !isDust { localTxOut = &wire.TxOut{ PkScript: c.localDeliveryScript, Value: 0, } } - if !c.cfg.Channel.RemoteBalanceDust() { + if isDust, _ := c.cfg.Channel.RemoteBalanceDust(); !isDust { remoteTxOut = &wire.TxOut{ PkScript: c.remoteDeliveryScript, Value: 0, @@ -337,6 +355,30 @@ func (c *ChanCloser) initChanShutdown() (*lnwire.Shutdown, error) { // desired closing script. shutdown := lnwire.NewShutdown(c.cid, c.localDeliveryScript) + // At this point, we'll check to see if we have any custom records to + // add to the shutdown message. + err := fn.MapOptionZ(c.cfg.AuxCloser, func(a AuxChanCloser) error { + shutdownCustomRecords, err := a.ShutdownBlob(AuxShutdownReq{ + ChanPoint: c.chanPoint, + ShortChanID: c.cfg.Channel.ShortChanID(), + Initiator: c.cfg.Channel.IsInitiator(), + CommitBlob: c.cfg.Channel.LocalCommitmentBlob(), + FundingBlob: c.cfg.Channel.FundingBlob(), + }) + if err != nil { + return err + } + + shutdownCustomRecords.WhenSome(func(cr lnwire.CustomRecords) { + shutdown.CustomRecords = cr + }) + + return nil + }) + if err != nil { + return nil, err + } + // If this is a taproot channel, then we'll need to also generate a // nonce that'll be used sign the co-op close transaction offer. if c.cfg.Channel.ChanType().IsTaproot() { @@ -370,11 +412,22 @@ func (c *ChanCloser) initChanShutdown() (*lnwire.Shutdown, error) { shutdownInfo := channeldb.NewShutdownInfo( c.localDeliveryScript, c.closer.IsLocal(), ) - err := c.cfg.Channel.MarkShutdownSent(shutdownInfo) + err = c.cfg.Channel.MarkShutdownSent(shutdownInfo) if err != nil { return nil, err } + // We'll track our local close output, even if it's dust in BTC terms, + // it might still carry value in custom channel terms. + _, dustAmt := c.cfg.Channel.LocalBalanceDust() + localBalance, _ := c.cfg.Channel.CommitBalances() + c.localCloseOutput = fn.Some(CloseOutput{ + Amt: localBalance, + DustLimit: dustAmt, + PkScript: c.localDeliveryScript, + ShutdownRecords: shutdown.CustomRecords, + }) + return shutdown, nil } @@ -444,6 +497,21 @@ func (c *ChanCloser) NegotiationHeight() uint32 { return c.negotiationHeight } +// LocalCloseOutput returns the local close output. +func (c *ChanCloser) LocalCloseOutput() fn.Option[CloseOutput] { + return c.localCloseOutput +} + +// RemoteCloseOutput returns the remote close output. +func (c *ChanCloser) RemoteCloseOutput() fn.Option[CloseOutput] { + return c.remoteCloseOutput +} + +// AuxOutputs returns optional extra outputs. +func (c *ChanCloser) AuxOutputs() fn.Option[AuxCloseOutputs] { + return c.auxOutputs +} + // validateShutdownScript attempts to match and validate the script provided in // our peer's shutdown message with the upfront shutdown script we have on // record. For any script specified, we also make sure it matches our @@ -503,6 +571,17 @@ func (c *ChanCloser) ReceiveShutdown(msg lnwire.Shutdown) ( noShutdown := fn.None[lnwire.Shutdown]() + // We'll track their remote close output, even if it's dust in BTC + // terms, it might still carry value in custom channel terms. + _, dustAmt := c.cfg.Channel.RemoteBalanceDust() + _, remoteBalance := c.cfg.Channel.CommitBalances() + c.remoteCloseOutput = fn.Some(CloseOutput{ + Amt: remoteBalance, + DustLimit: dustAmt, + PkScript: msg.Address, + ShutdownRecords: msg.CustomRecords, + }) + switch c.state { // If we're in the close idle state, and we're receiving a channel // closure related message, then this indicates that we're on the @@ -850,6 +929,25 @@ func (c *ChanCloser) ReceiveClosingSigned( //nolint:funlen } } + // Before we complete the cooperative close, we'll see if we + // have any extra aux options. + c.auxOutputs, err = c.auxCloseOutputs(remoteProposedFee) + if err != nil { + return noClosing, err + } + c.auxOutputs.WhenSome(func(outs AuxCloseOutputs) { + closeOpts = append( + closeOpts, lnwallet.WithExtraCloseOutputs( + outs.ExtraCloseOutputs, + ), + ) + closeOpts = append( + closeOpts, lnwallet.WithCustomCoopSort( + outs.CustomSort, + ), + ) + }) + closeTx, _, err := c.cfg.Channel.CompleteCooperativeClose( localSig, remoteSig, c.localDeliveryScript, c.remoteDeliveryScript, remoteProposedFee, closeOpts..., @@ -859,6 +957,32 @@ func (c *ChanCloser) ReceiveClosingSigned( //nolint:funlen } c.closingTx = closeTx + // If there's an aux chan closer, then we'll finalize with it + // before we write to disk. + err = fn.MapOptionZ( + c.cfg.AuxCloser, func(aux AuxChanCloser) error { + channel := c.cfg.Channel + //nolint:lll + req := AuxShutdownReq{ + ChanPoint: c.chanPoint, + ShortChanID: c.cfg.Channel.ShortChanID(), + Initiator: channel.IsInitiator(), + CommitBlob: channel.LocalCommitmentBlob(), + FundingBlob: channel.FundingBlob(), + } + desc := AuxCloseDesc{ + AuxShutdownReq: req, + LocalCloseOutput: c.localCloseOutput, + RemoteCloseOutput: c.remoteCloseOutput, + } + + return aux.FinalizeClose(desc, closeTx) + }, + ) + if err != nil { + return noClosing, err + } + // Before publishing the closing tx, we persist it to the // database, such that it can be republished if something goes // wrong. @@ -908,9 +1032,45 @@ func (c *ChanCloser) ReceiveClosingSigned( //nolint:funlen } } +// auxCloseOutputs returns any additional outputs that should be used when +// closing the channel. +func (c *ChanCloser) auxCloseOutputs( + closeFee btcutil.Amount) (fn.Option[AuxCloseOutputs], error) { + + var closeOuts fn.Option[AuxCloseOutputs] + err := fn.MapOptionZ(c.cfg.AuxCloser, func(aux AuxChanCloser) error { + req := AuxShutdownReq{ + ChanPoint: c.chanPoint, + ShortChanID: c.cfg.Channel.ShortChanID(), + Initiator: c.cfg.Channel.IsInitiator(), + CommitBlob: c.cfg.Channel.LocalCommitmentBlob(), + FundingBlob: c.cfg.Channel.FundingBlob(), + } + outs, err := aux.AuxCloseOutputs(AuxCloseDesc{ + AuxShutdownReq: req, + CloseFee: closeFee, + CommitFee: c.cfg.Channel.CommitFee(), + LocalCloseOutput: c.localCloseOutput, + RemoteCloseOutput: c.remoteCloseOutput, + }) + if err != nil { + return err + } + + closeOuts = outs + + return nil + }) + if err != nil { + return closeOuts, err + } + + return closeOuts, nil +} + // proposeCloseSigned attempts to propose a new signature for the closing -// transaction for a channel based on the prior fee negotiations and our current -// compromise fee. +// transaction for a channel based on the prior fee negotiations and our +// current compromise fee. func (c *ChanCloser) proposeCloseSigned(fee btcutil.Amount) ( *lnwire.ClosingSigned, error) { @@ -928,6 +1088,26 @@ func (c *ChanCloser) proposeCloseSigned(fee btcutil.Amount) ( } } + // We'll also now see if the aux chan closer has any additional options + // for the closing purpose. + c.auxOutputs, err = c.auxCloseOutputs(fee) + if err != nil { + return nil, err + } + c.auxOutputs.WhenSome(func(outs AuxCloseOutputs) { + closeOpts = append( + closeOpts, lnwallet.WithExtraCloseOutputs( + outs.ExtraCloseOutputs, + ), + ) + closeOpts = append( + closeOpts, lnwallet.WithCustomCoopSort( + outs.CustomSort, + ), + ) + }) + + // With all our options added, we'll attempt to co-op close now. rawSig, _, _, err := c.cfg.Channel.CreateCloseProposal( fee, c.localDeliveryScript, c.remoteDeliveryScript, closeOpts..., diff --git a/lnwallet/chancloser/chancloser_test.go b/lnwallet/chancloser/chancloser_test.go index a6688ed391..b32ce5ead9 100644 --- a/lnwallet/chancloser/chancloser_test.go +++ b/lnwallet/chancloser/chancloser_test.go @@ -22,6 +22,7 @@ import ( "github.com/lightningnetwork/lnd/lnwallet" "github.com/lightningnetwork/lnd/lnwallet/chainfee" "github.com/lightningnetwork/lnd/lnwire" + "github.com/lightningnetwork/lnd/tlv" "github.com/stretchr/testify/require" ) @@ -152,6 +153,14 @@ func (m *mockChannel) ChannelPoint() wire.OutPoint { return m.chanPoint } +func (m *mockChannel) LocalCommitmentBlob() fn.Option[tlv.Blob] { + return fn.None[tlv.Blob]() +} + +func (m *mockChannel) FundingBlob() fn.Option[tlv.Blob] { + return fn.None[tlv.Blob]() +} + func (m *mockChannel) MarkCoopBroadcasted(*wire.MsgTx, lntypes.ChannelParty) error { @@ -205,12 +214,20 @@ func (m *mockChannel) CompleteCooperativeClose(localSig, return &wire.MsgTx{}, 0, nil } -func (m *mockChannel) LocalBalanceDust() bool { - return false +func (m *mockChannel) LocalBalanceDust() (bool, btcutil.Amount) { + return false, 0 +} + +func (m *mockChannel) RemoteBalanceDust() (bool, btcutil.Amount) { + return false, 0 +} + +func (m *mockChannel) CommitBalances() (btcutil.Amount, btcutil.Amount) { + return 0, 0 } -func (m *mockChannel) RemoteBalanceDust() bool { - return false +func (m *mockChannel) CommitFee() btcutil.Amount { + return 0 } func (m *mockChannel) ChanType() channeldb.ChannelType { diff --git a/lnwallet/chancloser/interface.go b/lnwallet/chancloser/interface.go index 2e9fa98ae8..729cdc545b 100644 --- a/lnwallet/chancloser/interface.go +++ b/lnwallet/chancloser/interface.go @@ -6,11 +6,13 @@ import ( "github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/btcsuite/btcd/wire" "github.com/lightningnetwork/lnd/channeldb" + "github.com/lightningnetwork/lnd/fn" "github.com/lightningnetwork/lnd/input" "github.com/lightningnetwork/lnd/lntypes" "github.com/lightningnetwork/lnd/lnwallet" "github.com/lightningnetwork/lnd/lnwallet/chainfee" "github.com/lightningnetwork/lnd/lnwire" + "github.com/lightningnetwork/lnd/tlv" ) // CoopFeeEstimator is used to estimate the fee of a co-op close transaction. @@ -32,6 +34,14 @@ type Channel interface { //nolint:interfacebloat // ChannelPoint returns the channel point of the target channel. ChannelPoint() wire.OutPoint + // LocalCommitmentBlob may return the auxiliary data storage blob for + // the local commitment transaction. + LocalCommitmentBlob() fn.Option[tlv.Blob] + + // FundingBlob may return the auxiliary data storage blob related to + // funding details for the channel. + FundingBlob() fn.Option[tlv.Blob] + // MarkCoopBroadcasted persistently marks that the channel close // transaction has been broadcast. MarkCoopBroadcasted(*wire.MsgTx, lntypes.ChannelParty) error @@ -60,13 +70,23 @@ type Channel interface { //nolint:interfacebloat // LocalBalanceDust returns true if when creating a co-op close // transaction, the balance of the local party will be dust after - // accounting for any anchor outputs. - LocalBalanceDust() bool + // accounting for any anchor outputs. The dust value for the local + // party is also returned. + LocalBalanceDust() (bool, btcutil.Amount) // RemoteBalanceDust returns true if when creating a co-op close // transaction, the balance of the remote party will be dust after - // accounting for any anchor outputs. - RemoteBalanceDust() bool + // accounting for any anchor outputs. The dust value the remote party + // is also returned. + RemoteBalanceDust() (bool, btcutil.Amount) + + // CommitBalances returns the local and remote balances in the current + // commitment state. + CommitBalances() (btcutil.Amount, btcutil.Amount) + + // CommitFee returns the commitment fee for the current commitment + // state. + CommitFee() btcutil.Amount // RemoteUpfrontShutdownScript returns the upfront shutdown script of // the remote party. If the remote party didn't specify such a script, diff --git a/lnwallet/channel.go b/lnwallet/channel.go index c09b20000a..0e6300a0e6 100644 --- a/lnwallet/channel.go +++ b/lnwallet/channel.go @@ -8814,7 +8814,7 @@ func CreateCooperativeCloseTx(fundingTxIn wire.TxIn, // LocalBalanceDust returns true if when creating a co-op close transaction, // the balance of the local party will be dust after accounting for any anchor // outputs. -func (lc *LightningChannel) LocalBalanceDust() bool { +func (lc *LightningChannel) LocalBalanceDust() (bool, btcutil.Amount) { lc.RLock() defer lc.RUnlock() @@ -8828,13 +8828,15 @@ func (lc *LightningChannel) LocalBalanceDust() bool { localBalance += 2 * AnchorSize } - return localBalance <= chanState.LocalChanCfg.DustLimit + localDust := chanState.LocalChanCfg.DustLimit + + return localBalance <= localDust, localDust } // RemoteBalanceDust returns true if when creating a co-op close transaction, // the balance of the remote party will be dust after accounting for any anchor // outputs. -func (lc *LightningChannel) RemoteBalanceDust() bool { +func (lc *LightningChannel) RemoteBalanceDust() (bool, btcutil.Amount) { lc.RLock() defer lc.RUnlock() @@ -8848,7 +8850,40 @@ func (lc *LightningChannel) RemoteBalanceDust() bool { remoteBalance += 2 * AnchorSize } - return remoteBalance <= chanState.RemoteChanCfg.DustLimit + remoteDust := chanState.RemoteChanCfg.DustLimit + + return remoteBalance <= remoteDust, remoteDust +} + +// CommitBalances returns the local and remote balances in the current +// commitment state. +func (lc *LightningChannel) CommitBalances() (btcutil.Amount, btcutil.Amount) { + lc.RLock() + defer lc.RUnlock() + + chanState := lc.channelState + localCommit := lc.channelState.LocalCommitment + + localBalance := localCommit.LocalBalance.ToSatoshis() + remoteBalance := localCommit.RemoteBalance.ToSatoshis() + + if chanState.ChanType.HasAnchors() { + if chanState.IsInitiator { + localBalance += 2 * AnchorSize + } else { + remoteBalance += 2 * AnchorSize + } + } + + return localBalance, remoteBalance +} + +// CommitFee returns the commitment fee for the current commitment state. +func (lc *LightningChannel) CommitFee() btcutil.Amount { + lc.RLock() + defer lc.RUnlock() + + return lc.channelState.LocalCommitment.CommitFee } // CalcFee returns the commitment fee to use for the given fee rate From 8b97d4f833f0462893a6a21144ff0cb5500ee38c Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Wed, 29 May 2024 19:57:43 +0200 Subject: [PATCH 134/218] lnwallet: modify CoopCloseBalance to not depend on chan commit --- lnwallet/channel.go | 10 ++++++++-- lnwallet/commitment.go | 10 +++------- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/lnwallet/channel.go b/lnwallet/channel.go index 0e6300a0e6..8f7513f105 100644 --- a/lnwallet/channel.go +++ b/lnwallet/channel.go @@ -7781,7 +7781,10 @@ func (lc *LightningChannel) CreateCloseProposal(proposedFee btcutil.Amount, // during the channel closing process. ourBalance, theirBalance, err := CoopCloseBalance( lc.channelState.ChanType, lc.channelState.IsInitiator, - proposedFee, lc.channelState.LocalCommitment, + proposedFee, + lc.channelState.LocalCommitment.LocalBalance.ToSatoshis(), + lc.channelState.LocalCommitment.RemoteBalance.ToSatoshis(), + lc.channelState.LocalCommitment.CommitFee, ) if err != nil { return nil, nil, 0, err @@ -7879,7 +7882,10 @@ func (lc *LightningChannel) CompleteCooperativeClose( // Get the final balances after subtracting the proposed fee. ourBalance, theirBalance, err := CoopCloseBalance( lc.channelState.ChanType, lc.channelState.IsInitiator, - proposedFee, lc.channelState.LocalCommitment, + proposedFee, + lc.channelState.LocalCommitment.LocalBalance.ToSatoshis(), + lc.channelState.LocalCommitment.RemoteBalance.ToSatoshis(), + lc.channelState.LocalCommitment.CommitFee, ) if err != nil { return nil, 0, err diff --git a/lnwallet/commitment.go b/lnwallet/commitment.go index 73565ea184..170efece1b 100644 --- a/lnwallet/commitment.go +++ b/lnwallet/commitment.go @@ -1033,16 +1033,12 @@ func CreateCommitTx(chanType channeldb.ChannelType, // CoopCloseBalance returns the final balances that should be used to create // the cooperative close tx, given the channel type and transaction fee. func CoopCloseBalance(chanType channeldb.ChannelType, isInitiator bool, - coopCloseFee btcutil.Amount, localCommit channeldb.ChannelCommitment) ( - btcutil.Amount, btcutil.Amount, error) { - - // Get both parties' balances from the latest commitment. - ourBalance := localCommit.LocalBalance.ToSatoshis() - theirBalance := localCommit.RemoteBalance.ToSatoshis() + coopCloseFee, ourBalance, theirBalance, + commitFee btcutil.Amount) (btcutil.Amount, btcutil.Amount, error) { // We'll make sure we account for the complete balance by adding the // current dangling commitment fee to the balance of the initiator. - initiatorDelta := localCommit.CommitFee + initiatorDelta := commitFee // Since the initiator's balance also is stored after subtracting the // anchor values, add that back in case this was an anchor commitment. From fbbcecc635a1e86dc121869906ed19238b788a4f Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Wed, 29 May 2024 19:57:46 +0200 Subject: [PATCH 135/218] server+peer: init peer struct w/ AuxChanCloser if present --- config_builder.go | 5 +++++ peer/brontide.go | 4 ++++ server.go | 1 + 3 files changed, 10 insertions(+) diff --git a/config_builder.go b/config_builder.go index ee4064035f..4b585cb585 100644 --- a/config_builder.go +++ b/config_builder.go @@ -42,6 +42,7 @@ import ( "github.com/lightningnetwork/lnd/lnrpc" "github.com/lightningnetwork/lnd/lnwallet" "github.com/lightningnetwork/lnd/lnwallet/btcwallet" + "github.com/lightningnetwork/lnd/lnwallet/chancloser" "github.com/lightningnetwork/lnd/lnwallet/rpcwallet" "github.com/lightningnetwork/lnd/macaroons" "github.com/lightningnetwork/lnd/msgmux" @@ -182,6 +183,10 @@ type AuxComponents struct { // AuxDataParser is an optional data parser that can be used to parse // auxiliary data for certain custom channel types. AuxDataParser fn.Option[AuxDataParser] + + // AuxChanCloser is an optional channel closer that can be used to + // modify the way a coop-close transaction is constructed. + AuxChanCloser fn.Option[chancloser.AuxChanCloser] } // DefaultWalletImpl is the default implementation of our normal, btcwallet diff --git a/peer/brontide.go b/peer/brontide.go index 2e4646a78f..beb7bd955d 100644 --- a/peer/brontide.go +++ b/peer/brontide.go @@ -400,6 +400,10 @@ type Config struct { // in place. MsgRouter fn.Option[msgmux.Router] + // AuxChanCloser is an optional instance of an abstraction that can be + // used to modify the way the co-op close transaction is constructed. + AuxChanCloser fn.Option[chancloser.AuxChanCloser] + // Quit is the server's quit channel. If this is closed, we halt operation. Quit chan struct{} } diff --git a/server.go b/server.go index b336036f0b..20506c059a 100644 --- a/server.go +++ b/server.go @@ -4075,6 +4075,7 @@ func (s *server) peerConnected(conn net.Conn, connReq *connmgr.ConnReq, AuxLeafStore: s.implCfg.AuxLeafStore, AuxSigner: s.implCfg.AuxSigner, MsgRouter: s.implCfg.MsgRouter, + AuxChanCloser: s.implCfg.AuxChanCloser, } copy(pCfg.PubKeyBytes[:], peerAddr.IdentityKey.SerializeCompressed()) From 39e4e8d8a4fbb144092c98534d498c2d9070825d Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Wed, 29 May 2024 19:57:47 +0200 Subject: [PATCH 136/218] peer: decorate delivery addr w/ internal key In this commit, we move to add the internal key to the delivery addr. This way, we give the aux chan closer the extra information it may need to properly augment the normal co-op close process. --- lntest/mock/walletcontroller.go | 5 +- lnwallet/chancloser/chancloser.go | 25 +++++- lnwallet/chancloser/chancloser_test.go | 10 ++- peer/brontide.go | 108 +++++++++++++++++++++++-- 4 files changed, 131 insertions(+), 17 deletions(-) diff --git a/lntest/mock/walletcontroller.go b/lntest/mock/walletcontroller.go index 7af22e0383..87fce9cba5 100644 --- a/lntest/mock/walletcontroller.go +++ b/lntest/mock/walletcontroller.go @@ -78,9 +78,8 @@ func (w *WalletController) ConfirmedBalance(int32, string) (btcutil.Amount, func (w *WalletController) NewAddress(lnwallet.AddressType, bool, string) (btcutil.Address, error) { - addr, _ := btcutil.NewAddressPubKey( - w.RootKey.PubKey().SerializeCompressed(), &chaincfg.MainNetParams, - ) + pkh := btcutil.Hash160(w.RootKey.PubKey().SerializeCompressed()) + addr, _ := btcutil.NewAddressPubKeyHash(pkh, &chaincfg.MainNetParams) return addr, nil } diff --git a/lnwallet/chancloser/chancloser.go b/lnwallet/chancloser/chancloser.go index 5c73e56fdf..662a38aa76 100644 --- a/lnwallet/chancloser/chancloser.go +++ b/lnwallet/chancloser/chancloser.go @@ -4,6 +4,7 @@ import ( "bytes" "fmt" + "github.com/btcsuite/btcd/btcec/v2" "github.com/btcsuite/btcd/btcec/v2/schnorr/musig2" "github.com/btcsuite/btcd/btcutil" "github.com/btcsuite/btcd/chaincfg" @@ -106,6 +107,18 @@ const ( defaultMaxFeeMultiplier = 3 ) +// DeliveryAddrWithKey wraps a normal delivery addr, but also includes the +// internal key for the delivery addr if known. +type DeliveryAddrWithKey struct { + // DeliveryAddress is the raw, serialized pkScript of the delivery + // address. + lnwire.DeliveryAddress + + // InternalKey is the Taproot internal key of the delivery address, if + // the address is a P2TR output. + InternalKey fn.Option[btcec.PublicKey] +} + // ChanCloseCfg holds all the items that a ChanCloser requires to carry out its // duties. type ChanCloseCfg struct { @@ -208,6 +221,10 @@ type ChanCloser struct { // funds to. localDeliveryScript []byte + // localInternalKey is the local delivery address Taproot internal key, + // if the local delivery script is a P2TR output. + localInternalKey fn.Option[btcec.PublicKey] + // remoteDeliveryScript is the script that we'll send the remote party's // settled channel funds to. remoteDeliveryScript []byte @@ -284,7 +301,7 @@ func (d *SimpleCoopFeeEstimator) EstimateFee(chanType channeldb.ChannelType, // NewChanCloser creates a new instance of the channel closure given the passed // configuration, and delivery+fee preference. The final argument should only // be populated iff, we're the initiator of this closing request. -func NewChanCloser(cfg ChanCloseCfg, deliveryScript []byte, +func NewChanCloser(cfg ChanCloseCfg, deliveryScript DeliveryAddrWithKey, idealFeePerKw chainfee.SatPerKWeight, negotiationHeight uint32, closeReq *htlcswitch.ChanClose, closer lntypes.ChannelParty) *ChanCloser { @@ -299,7 +316,8 @@ func NewChanCloser(cfg ChanCloseCfg, deliveryScript []byte, cfg: cfg, negotiationHeight: negotiationHeight, idealFeeRate: idealFeePerKw, - localDeliveryScript: deliveryScript, + localInternalKey: deliveryScript.InternalKey, + localDeliveryScript: deliveryScript.DeliveryAddress, priorFeeOffers: make( map[btcutil.Amount]*lnwire.ClosingSigned, ), @@ -362,6 +380,7 @@ func (c *ChanCloser) initChanShutdown() (*lnwire.Shutdown, error) { ChanPoint: c.chanPoint, ShortChanID: c.cfg.Channel.ShortChanID(), Initiator: c.cfg.Channel.IsInitiator(), + InternalKey: c.localInternalKey, CommitBlob: c.cfg.Channel.LocalCommitmentBlob(), FundingBlob: c.cfg.Channel.FundingBlob(), }) @@ -966,6 +985,7 @@ func (c *ChanCloser) ReceiveClosingSigned( //nolint:funlen req := AuxShutdownReq{ ChanPoint: c.chanPoint, ShortChanID: c.cfg.Channel.ShortChanID(), + InternalKey: c.localInternalKey, Initiator: channel.IsInitiator(), CommitBlob: channel.LocalCommitmentBlob(), FundingBlob: channel.FundingBlob(), @@ -1042,6 +1062,7 @@ func (c *ChanCloser) auxCloseOutputs( req := AuxShutdownReq{ ChanPoint: c.chanPoint, ShortChanID: c.cfg.Channel.ShortChanID(), + InternalKey: c.localInternalKey, Initiator: c.cfg.Channel.IsInitiator(), CommitBlob: c.cfg.Channel.LocalCommitmentBlob(), FundingBlob: c.cfg.Channel.FundingBlob(), diff --git a/lnwallet/chancloser/chancloser_test.go b/lnwallet/chancloser/chancloser_test.go index b32ce5ead9..28709fd5f8 100644 --- a/lnwallet/chancloser/chancloser_test.go +++ b/lnwallet/chancloser/chancloser_test.go @@ -361,7 +361,8 @@ func TestMaxFeeClamp(t *testing.T) { Channel: &channel, MaxFee: test.inputMaxFee, FeeEstimator: &SimpleCoopFeeEstimator{}, - }, nil, test.idealFee, 0, nil, lntypes.Remote, + }, DeliveryAddrWithKey{}, test.idealFee, 0, nil, + lntypes.Remote, ) // We'll call initFeeBaseline early here since we need @@ -402,7 +403,8 @@ func TestMaxFeeBailOut(t *testing.T) { MaxFee: idealFee * 2, } chanCloser := NewChanCloser( - closeCfg, nil, idealFee, 0, nil, lntypes.Remote, + closeCfg, DeliveryAddrWithKey{}, idealFee, 0, + nil, lntypes.Remote, ) // We'll now force the channel state into the @@ -526,7 +528,7 @@ func TestTaprootFastClose(t *testing.T) { DisableChannel: func(wire.OutPoint) error { return nil }, - }, nil, idealFee, 0, nil, lntypes.Local, + }, DeliveryAddrWithKey{}, idealFee, 0, nil, lntypes.Local, ) aliceCloser.initFeeBaseline() @@ -543,7 +545,7 @@ func TestTaprootFastClose(t *testing.T) { DisableChannel: func(wire.OutPoint) error { return nil }, - }, nil, idealFee, 0, nil, lntypes.Remote, + }, DeliveryAddrWithKey{}, idealFee, 0, nil, lntypes.Remote, ) bobCloser.initFeeBaseline() diff --git a/peer/brontide.go b/peer/brontide.go index beb7bd955d..b7d3a4087b 100644 --- a/peer/brontide.go +++ b/peer/brontide.go @@ -13,11 +13,13 @@ import ( "time" "github.com/btcsuite/btcd/btcec/v2" + "github.com/btcsuite/btcd/btcutil" "github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/btcsuite/btcd/connmgr" "github.com/btcsuite/btcd/txscript" "github.com/btcsuite/btcd/wire" "github.com/btcsuite/btclog" + "github.com/btcsuite/btcwallet/waddrmgr" "github.com/davecgh/go-spew/spew" "github.com/lightningnetwork/lnd/buffer" "github.com/lightningnetwork/lnd/build" @@ -885,6 +887,73 @@ func (p *Brontide) QuitSignal() <-chan struct{} { return p.quit } +// internalKeyForAddr returns the internal key associated with a taproot +// address. +func internalKeyForAddr(wallet *lnwallet.LightningWallet, + deliveryScript []byte) (fn.Option[btcec.PublicKey], error) { + + none := fn.None[btcec.PublicKey]() + + pkScript, err := txscript.ParsePkScript(deliveryScript) + if err != nil { + return none, err + } + addr, err := pkScript.Address(&wallet.Cfg.NetParams) + if err != nil { + return none, err + } + + // If it's not a taproot address, we don't require to know the internal + // key in the first place. So we don't return an error here, but also no + // internal key. + _, isTaproot := addr.(*btcutil.AddressTaproot) + if !isTaproot { + return none, nil + } + + walletAddr, err := wallet.AddressInfo(addr) + if err != nil { + return none, err + } + + // If the address isn't known to the wallet, we can't determine the + // internal key. + if walletAddr == nil { + return none, nil + } + + pubKeyAddr, ok := walletAddr.(waddrmgr.ManagedPubKeyAddress) + if !ok { + return none, fmt.Errorf("expected pubkey addr, got %T", + pubKeyAddr) + } + + return fn.Some(*pubKeyAddr.PubKey()), nil +} + +// addrWithInternalKey takes a delivery script, then attempts to supplement it +// with information related to the internal key for the addr, but only if it's +// a taproot addr. +func (p *Brontide) addrWithInternalKey( + deliveryScript []byte) fn.Result[chancloser.DeliveryAddrWithKey] { + + // TODO(roasbeef): not compatible with external shutdown addr? + // Currently, custom channels cannot be created with external upfront + // shutdown addresses, so this shouldn't be an issue. We only require + // the internal key for taproot addresses to be able to provide a non + // inclusion proof of any scripts. + + internalKey, err := internalKeyForAddr(p.cfg.Wallet, deliveryScript) + if err != nil { + return fn.Err[chancloser.DeliveryAddrWithKey](err) + } + + return fn.Ok(chancloser.DeliveryAddrWithKey{ + DeliveryAddress: deliveryScript, + InternalKey: internalKey, + }) +} + // loadActiveChannels creates indexes within the peer for tracking all active // channels returned by the database. It returns a slice of channel reestablish // messages that should be sent to the peer immediately, in case we have borked @@ -1125,9 +1194,16 @@ func (p *Brontide) loadActiveChannels(chans []*channeldb.OpenChannel) ( return } + addr, err := p.addrWithInternalKey( + info.DeliveryScript.Val, + ).Unpack() + if err != nil { + shutdownInfoErr = fmt.Errorf("unable to make "+ + "delivery addr: %w", err) + return + } chanCloser, err := p.createChanCloser( - lnChan, info.DeliveryScript.Val, feePerKw, nil, - info.Closer(), + lnChan, addr, feePerKw, nil, info.Closer(), ) if err != nil { shutdownInfoErr = fmt.Errorf("unable to "+ @@ -2886,8 +2962,12 @@ func (p *Brontide) fetchActiveChanCloser(chanID lnwire.ChannelID) ( return nil, fmt.Errorf("unable to estimate fee") } + addr, err := p.addrWithInternalKey(deliveryScript).Unpack() + if err != nil { + return nil, fmt.Errorf("unable to parse addr: %w", err) + } chanCloser, err = p.createChanCloser( - channel, deliveryScript, feePerKw, nil, lntypes.Remote, + channel, addr, feePerKw, nil, lntypes.Remote, ) if err != nil { p.log.Errorf("unable to create chan closer: %v", err) @@ -3129,8 +3209,12 @@ func (p *Brontide) restartCoopClose(lnChan *lnwallet.LightningChannel) ( closingParty = lntypes.Local } + addr, err := p.addrWithInternalKey(deliveryScript).Unpack() + if err != nil { + return nil, fmt.Errorf("unable to parse addr: %w", err) + } chanCloser, err := p.createChanCloser( - lnChan, deliveryScript, feePerKw, nil, closingParty, + lnChan, addr, feePerKw, nil, closingParty, ) if err != nil { p.log.Errorf("unable to create chan closer: %v", err) @@ -3157,8 +3241,8 @@ func (p *Brontide) restartCoopClose(lnChan *lnwallet.LightningChannel) ( // createChanCloser constructs a ChanCloser from the passed parameters and is // used to de-duplicate code. func (p *Brontide) createChanCloser(channel *lnwallet.LightningChannel, - deliveryScript lnwire.DeliveryAddress, fee chainfee.SatPerKWeight, - req *htlcswitch.ChanClose, + deliveryScript chancloser.DeliveryAddrWithKey, + fee chainfee.SatPerKWeight, req *htlcswitch.ChanClose, closer lntypes.ChannelParty) (*chancloser.ChanCloser, error) { _, startingHeight, err := p.cfg.ChainIO.GetBestBlock() @@ -3179,6 +3263,7 @@ func (p *Brontide) createChanCloser(channel *lnwallet.LightningChannel, MusigSession: NewMusigChanCloser(channel), FeeEstimator: &chancloser.SimpleCoopFeeEstimator{}, BroadcastTx: p.cfg.Wallet.PublishTransaction, + AuxCloser: p.cfg.AuxChanCloser, DisableChannel: func(op wire.OutPoint) error { return p.cfg.ChanStatusMgr.RequestDisable( op, false, @@ -3250,10 +3335,17 @@ func (p *Brontide) handleLocalCloseReq(req *htlcswitch.ChanClose) { return } } + addr, err := p.addrWithInternalKey(deliveryScript).Unpack() + if err != nil { + err = fmt.Errorf("unable to parse addr for channel "+ + "%v: %w", req.ChanPoint, err) + p.log.Errorf(err.Error()) + req.Err <- err + return + } chanCloser, err := p.createChanCloser( - channel, deliveryScript, req.TargetFeePerKw, req, - lntypes.Local, + channel, addr, req.TargetFeePerKw, req, lntypes.Local, ) if err != nil { p.log.Errorf(err.Error()) From 9ce799578cce896e2729b1cd4831617a0e0906d8 Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Wed, 29 May 2024 19:57:49 +0200 Subject: [PATCH 137/218] multi: add co-op close custom data to close update With this commit we populate additional information about the close outputs (including potential custom channel data) in the close update RPC message. This will allow custom channels to find out how the additional close outputs look like on chain and what data they might commit to. We also hook up the aux custom data formatter, so it can format the custom channel data to JSON. --- lnrpc/lightning.pb.go | 6735 +++++++++++++++++----------------- lnrpc/lightning.proto | 28 + lnrpc/lightning.swagger.json | 39 + peer/brontide.go | 31 +- rpcserver.go | 99 +- 5 files changed, 3624 insertions(+), 3308 deletions(-) diff --git a/lnrpc/lightning.pb.go b/lnrpc/lightning.pb.go index 435e56320d..8c5b0513dc 100644 --- a/lnrpc/lightning.pb.go +++ b/lnrpc/lightning.pb.go @@ -1017,7 +1017,7 @@ func (x PendingChannelsResponse_ForceClosedChannel_AnchorState) Number() protore // Deprecated: Use PendingChannelsResponse_ForceClosedChannel_AnchorState.Descriptor instead. func (PendingChannelsResponse_ForceClosedChannel_AnchorState) EnumDescriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{89, 5, 0} + return file_lightning_proto_rawDescGZIP(), []int{90, 5, 0} } type ChannelEventUpdate_UpdateType int32 @@ -1075,7 +1075,7 @@ func (x ChannelEventUpdate_UpdateType) Number() protoreflect.EnumNumber { // Deprecated: Use ChannelEventUpdate_UpdateType.Descriptor instead. func (ChannelEventUpdate_UpdateType) EnumDescriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{91, 0} + return file_lightning_proto_rawDescGZIP(), []int{92, 0} } type Invoice_InvoiceState int32 @@ -1127,7 +1127,7 @@ func (x Invoice_InvoiceState) Number() protoreflect.EnumNumber { // Deprecated: Use Invoice_InvoiceState.Descriptor instead. func (Invoice_InvoiceState) EnumDescriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{134, 0} + return file_lightning_proto_rawDescGZIP(), []int{135, 0} } type Payment_PaymentStatus int32 @@ -1189,7 +1189,7 @@ func (x Payment_PaymentStatus) Number() protoreflect.EnumNumber { // Deprecated: Use Payment_PaymentStatus.Descriptor instead. func (Payment_PaymentStatus) EnumDescriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{143, 0} + return file_lightning_proto_rawDescGZIP(), []int{144, 0} } type HTLCAttempt_HTLCStatus int32 @@ -1238,7 +1238,7 @@ func (x HTLCAttempt_HTLCStatus) Number() protoreflect.EnumNumber { // Deprecated: Use HTLCAttempt_HTLCStatus.Descriptor instead. func (HTLCAttempt_HTLCStatus) EnumDescriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{144, 0} + return file_lightning_proto_rawDescGZIP(), []int{145, 0} } type Failure_FailureCode int32 @@ -1372,7 +1372,7 @@ func (x Failure_FailureCode) Number() protoreflect.EnumNumber { // Deprecated: Use Failure_FailureCode.Descriptor instead. func (Failure_FailureCode) EnumDescriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{188, 0} + return file_lightning_proto_rawDescGZIP(), []int{189, 0} } type LookupHtlcResolutionRequest struct { @@ -6738,6 +6738,84 @@ func (x *ChannelOpenUpdate) GetChannelPoint() *ChannelPoint { return nil } +type CloseOutput struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The amount in satoshi of this close output. This amount is the final + // commitment balance of the channel and the actual amount paid out on chain + // might be smaller due to subtracted fees. + AmountSat int64 `protobuf:"varint,1,opt,name=amount_sat,json=amountSat,proto3" json:"amount_sat,omitempty"` + // The pkScript of the close output. + PkScript []byte `protobuf:"bytes,2,opt,name=pk_script,json=pkScript,proto3" json:"pk_script,omitempty"` + // Whether this output is for the local or remote node. + IsLocal bool `protobuf:"varint,3,opt,name=is_local,json=isLocal,proto3" json:"is_local,omitempty"` + // The TLV encoded custom channel data records for this output, which might + // be set for custom channels. + CustomChannelData []byte `protobuf:"bytes,4,opt,name=custom_channel_data,json=customChannelData,proto3" json:"custom_channel_data,omitempty"` +} + +func (x *CloseOutput) Reset() { + *x = CloseOutput{} + if protoimpl.UnsafeEnabled { + mi := &file_lightning_proto_msgTypes[66] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *CloseOutput) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CloseOutput) ProtoMessage() {} + +func (x *CloseOutput) ProtoReflect() protoreflect.Message { + mi := &file_lightning_proto_msgTypes[66] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use CloseOutput.ProtoReflect.Descriptor instead. +func (*CloseOutput) Descriptor() ([]byte, []int) { + return file_lightning_proto_rawDescGZIP(), []int{66} +} + +func (x *CloseOutput) GetAmountSat() int64 { + if x != nil { + return x.AmountSat + } + return 0 +} + +func (x *CloseOutput) GetPkScript() []byte { + if x != nil { + return x.PkScript + } + return nil +} + +func (x *CloseOutput) GetIsLocal() bool { + if x != nil { + return x.IsLocal + } + return false +} + +func (x *CloseOutput) GetCustomChannelData() []byte { + if x != nil { + return x.CustomChannelData + } + return nil +} + type ChannelCloseUpdate struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -6745,12 +6823,20 @@ type ChannelCloseUpdate struct { ClosingTxid []byte `protobuf:"bytes,1,opt,name=closing_txid,json=closingTxid,proto3" json:"closing_txid,omitempty"` Success bool `protobuf:"varint,2,opt,name=success,proto3" json:"success,omitempty"` + // The local channel close output. If the local channel balance was dust to + // begin with, this output will not be set. + LocalCloseOutput *CloseOutput `protobuf:"bytes,3,opt,name=local_close_output,json=localCloseOutput,proto3" json:"local_close_output,omitempty"` + // The remote channel close output. If the remote channel balance was dust + // to begin with, this output will not be set. + RemoteCloseOutput *CloseOutput `protobuf:"bytes,4,opt,name=remote_close_output,json=remoteCloseOutput,proto3" json:"remote_close_output,omitempty"` + // Any additional outputs that might be added for custom channel types. + AdditionalOutputs []*CloseOutput `protobuf:"bytes,5,rep,name=additional_outputs,json=additionalOutputs,proto3" json:"additional_outputs,omitempty"` } func (x *ChannelCloseUpdate) Reset() { *x = ChannelCloseUpdate{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[66] + mi := &file_lightning_proto_msgTypes[67] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6763,7 +6849,7 @@ func (x *ChannelCloseUpdate) String() string { func (*ChannelCloseUpdate) ProtoMessage() {} func (x *ChannelCloseUpdate) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[66] + mi := &file_lightning_proto_msgTypes[67] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6776,7 +6862,7 @@ func (x *ChannelCloseUpdate) ProtoReflect() protoreflect.Message { // Deprecated: Use ChannelCloseUpdate.ProtoReflect.Descriptor instead. func (*ChannelCloseUpdate) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{66} + return file_lightning_proto_rawDescGZIP(), []int{67} } func (x *ChannelCloseUpdate) GetClosingTxid() []byte { @@ -6793,6 +6879,27 @@ func (x *ChannelCloseUpdate) GetSuccess() bool { return false } +func (x *ChannelCloseUpdate) GetLocalCloseOutput() *CloseOutput { + if x != nil { + return x.LocalCloseOutput + } + return nil +} + +func (x *ChannelCloseUpdate) GetRemoteCloseOutput() *CloseOutput { + if x != nil { + return x.RemoteCloseOutput + } + return nil +} + +func (x *ChannelCloseUpdate) GetAdditionalOutputs() []*CloseOutput { + if x != nil { + return x.AdditionalOutputs + } + return nil +} + type CloseChannelRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -6835,7 +6942,7 @@ type CloseChannelRequest struct { func (x *CloseChannelRequest) Reset() { *x = CloseChannelRequest{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[67] + mi := &file_lightning_proto_msgTypes[68] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6848,7 +6955,7 @@ func (x *CloseChannelRequest) String() string { func (*CloseChannelRequest) ProtoMessage() {} func (x *CloseChannelRequest) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[67] + mi := &file_lightning_proto_msgTypes[68] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6861,7 +6968,7 @@ func (x *CloseChannelRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use CloseChannelRequest.ProtoReflect.Descriptor instead. func (*CloseChannelRequest) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{67} + return file_lightning_proto_rawDescGZIP(), []int{68} } func (x *CloseChannelRequest) GetChannelPoint() *ChannelPoint { @@ -6937,7 +7044,7 @@ type CloseStatusUpdate struct { func (x *CloseStatusUpdate) Reset() { *x = CloseStatusUpdate{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[68] + mi := &file_lightning_proto_msgTypes[69] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6950,7 +7057,7 @@ func (x *CloseStatusUpdate) String() string { func (*CloseStatusUpdate) ProtoMessage() {} func (x *CloseStatusUpdate) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[68] + mi := &file_lightning_proto_msgTypes[69] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6963,7 +7070,7 @@ func (x *CloseStatusUpdate) ProtoReflect() protoreflect.Message { // Deprecated: Use CloseStatusUpdate.ProtoReflect.Descriptor instead. func (*CloseStatusUpdate) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{68} + return file_lightning_proto_rawDescGZIP(), []int{69} } func (m *CloseStatusUpdate) GetUpdate() isCloseStatusUpdate_Update { @@ -7028,7 +7135,7 @@ type PendingUpdate struct { func (x *PendingUpdate) Reset() { *x = PendingUpdate{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[69] + mi := &file_lightning_proto_msgTypes[70] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7041,7 +7148,7 @@ func (x *PendingUpdate) String() string { func (*PendingUpdate) ProtoMessage() {} func (x *PendingUpdate) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[69] + mi := &file_lightning_proto_msgTypes[70] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7054,7 +7161,7 @@ func (x *PendingUpdate) ProtoReflect() protoreflect.Message { // Deprecated: Use PendingUpdate.ProtoReflect.Descriptor instead. func (*PendingUpdate) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{69} + return file_lightning_proto_rawDescGZIP(), []int{70} } func (x *PendingUpdate) GetTxid() []byte { @@ -7080,7 +7187,7 @@ type InstantUpdate struct { func (x *InstantUpdate) Reset() { *x = InstantUpdate{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[70] + mi := &file_lightning_proto_msgTypes[71] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7093,7 +7200,7 @@ func (x *InstantUpdate) String() string { func (*InstantUpdate) ProtoMessage() {} func (x *InstantUpdate) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[70] + mi := &file_lightning_proto_msgTypes[71] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7106,7 +7213,7 @@ func (x *InstantUpdate) ProtoReflect() protoreflect.Message { // Deprecated: Use InstantUpdate.ProtoReflect.Descriptor instead. func (*InstantUpdate) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{70} + return file_lightning_proto_rawDescGZIP(), []int{71} } type ReadyForPsbtFunding struct { @@ -7130,7 +7237,7 @@ type ReadyForPsbtFunding struct { func (x *ReadyForPsbtFunding) Reset() { *x = ReadyForPsbtFunding{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[71] + mi := &file_lightning_proto_msgTypes[72] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7143,7 +7250,7 @@ func (x *ReadyForPsbtFunding) String() string { func (*ReadyForPsbtFunding) ProtoMessage() {} func (x *ReadyForPsbtFunding) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[71] + mi := &file_lightning_proto_msgTypes[72] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7156,7 +7263,7 @@ func (x *ReadyForPsbtFunding) ProtoReflect() protoreflect.Message { // Deprecated: Use ReadyForPsbtFunding.ProtoReflect.Descriptor instead. func (*ReadyForPsbtFunding) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{71} + return file_lightning_proto_rawDescGZIP(), []int{72} } func (x *ReadyForPsbtFunding) GetFundingAddress() string { @@ -7208,7 +7315,7 @@ type BatchOpenChannelRequest struct { func (x *BatchOpenChannelRequest) Reset() { *x = BatchOpenChannelRequest{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[72] + mi := &file_lightning_proto_msgTypes[73] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7221,7 +7328,7 @@ func (x *BatchOpenChannelRequest) String() string { func (*BatchOpenChannelRequest) ProtoMessage() {} func (x *BatchOpenChannelRequest) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[72] + mi := &file_lightning_proto_msgTypes[73] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7234,7 +7341,7 @@ func (x *BatchOpenChannelRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use BatchOpenChannelRequest.ProtoReflect.Descriptor instead. func (*BatchOpenChannelRequest) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{72} + return file_lightning_proto_rawDescGZIP(), []int{73} } func (x *BatchOpenChannelRequest) GetChannels() []*BatchOpenChannel { @@ -7366,7 +7473,7 @@ type BatchOpenChannel struct { func (x *BatchOpenChannel) Reset() { *x = BatchOpenChannel{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[73] + mi := &file_lightning_proto_msgTypes[74] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7379,7 +7486,7 @@ func (x *BatchOpenChannel) String() string { func (*BatchOpenChannel) ProtoMessage() {} func (x *BatchOpenChannel) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[73] + mi := &file_lightning_proto_msgTypes[74] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7392,7 +7499,7 @@ func (x *BatchOpenChannel) ProtoReflect() protoreflect.Message { // Deprecated: Use BatchOpenChannel.ProtoReflect.Descriptor instead. func (*BatchOpenChannel) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{73} + return file_lightning_proto_rawDescGZIP(), []int{74} } func (x *BatchOpenChannel) GetNodePubkey() []byte { @@ -7546,7 +7653,7 @@ type BatchOpenChannelResponse struct { func (x *BatchOpenChannelResponse) Reset() { *x = BatchOpenChannelResponse{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[74] + mi := &file_lightning_proto_msgTypes[75] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7559,7 +7666,7 @@ func (x *BatchOpenChannelResponse) String() string { func (*BatchOpenChannelResponse) ProtoMessage() {} func (x *BatchOpenChannelResponse) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[74] + mi := &file_lightning_proto_msgTypes[75] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7572,7 +7679,7 @@ func (x *BatchOpenChannelResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use BatchOpenChannelResponse.ProtoReflect.Descriptor instead. func (*BatchOpenChannelResponse) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{74} + return file_lightning_proto_rawDescGZIP(), []int{75} } func (x *BatchOpenChannelResponse) GetPendingChannels() []*PendingUpdate { @@ -7693,7 +7800,7 @@ type OpenChannelRequest struct { func (x *OpenChannelRequest) Reset() { *x = OpenChannelRequest{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[75] + mi := &file_lightning_proto_msgTypes[76] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7706,7 +7813,7 @@ func (x *OpenChannelRequest) String() string { func (*OpenChannelRequest) ProtoMessage() {} func (x *OpenChannelRequest) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[75] + mi := &file_lightning_proto_msgTypes[76] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7719,7 +7826,7 @@ func (x *OpenChannelRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use OpenChannelRequest.ProtoReflect.Descriptor instead. func (*OpenChannelRequest) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{75} + return file_lightning_proto_rawDescGZIP(), []int{76} } func (x *OpenChannelRequest) GetSatPerVbyte() uint64 { @@ -7939,7 +8046,7 @@ type OpenStatusUpdate struct { func (x *OpenStatusUpdate) Reset() { *x = OpenStatusUpdate{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[76] + mi := &file_lightning_proto_msgTypes[77] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7952,7 +8059,7 @@ func (x *OpenStatusUpdate) String() string { func (*OpenStatusUpdate) ProtoMessage() {} func (x *OpenStatusUpdate) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[76] + mi := &file_lightning_proto_msgTypes[77] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7965,7 +8072,7 @@ func (x *OpenStatusUpdate) ProtoReflect() protoreflect.Message { // Deprecated: Use OpenStatusUpdate.ProtoReflect.Descriptor instead. func (*OpenStatusUpdate) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{76} + return file_lightning_proto_rawDescGZIP(), []int{77} } func (m *OpenStatusUpdate) GetUpdate() isOpenStatusUpdate_Update { @@ -8045,7 +8152,7 @@ type KeyLocator struct { func (x *KeyLocator) Reset() { *x = KeyLocator{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[77] + mi := &file_lightning_proto_msgTypes[78] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -8058,7 +8165,7 @@ func (x *KeyLocator) String() string { func (*KeyLocator) ProtoMessage() {} func (x *KeyLocator) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[77] + mi := &file_lightning_proto_msgTypes[78] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -8071,7 +8178,7 @@ func (x *KeyLocator) ProtoReflect() protoreflect.Message { // Deprecated: Use KeyLocator.ProtoReflect.Descriptor instead. func (*KeyLocator) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{77} + return file_lightning_proto_rawDescGZIP(), []int{78} } func (x *KeyLocator) GetKeyFamily() int32 { @@ -8102,7 +8209,7 @@ type KeyDescriptor struct { func (x *KeyDescriptor) Reset() { *x = KeyDescriptor{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[78] + mi := &file_lightning_proto_msgTypes[79] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -8115,7 +8222,7 @@ func (x *KeyDescriptor) String() string { func (*KeyDescriptor) ProtoMessage() {} func (x *KeyDescriptor) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[78] + mi := &file_lightning_proto_msgTypes[79] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -8128,7 +8235,7 @@ func (x *KeyDescriptor) ProtoReflect() protoreflect.Message { // Deprecated: Use KeyDescriptor.ProtoReflect.Descriptor instead. func (*KeyDescriptor) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{78} + return file_lightning_proto_rawDescGZIP(), []int{79} } func (x *KeyDescriptor) GetRawKeyBytes() []byte { @@ -8177,7 +8284,7 @@ type ChanPointShim struct { func (x *ChanPointShim) Reset() { *x = ChanPointShim{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[79] + mi := &file_lightning_proto_msgTypes[80] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -8190,7 +8297,7 @@ func (x *ChanPointShim) String() string { func (*ChanPointShim) ProtoMessage() {} func (x *ChanPointShim) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[79] + mi := &file_lightning_proto_msgTypes[80] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -8203,7 +8310,7 @@ func (x *ChanPointShim) ProtoReflect() protoreflect.Message { // Deprecated: Use ChanPointShim.ProtoReflect.Descriptor instead. func (*ChanPointShim) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{79} + return file_lightning_proto_rawDescGZIP(), []int{80} } func (x *ChanPointShim) GetAmt() int64 { @@ -8279,7 +8386,7 @@ type PsbtShim struct { func (x *PsbtShim) Reset() { *x = PsbtShim{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[80] + mi := &file_lightning_proto_msgTypes[81] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -8292,7 +8399,7 @@ func (x *PsbtShim) String() string { func (*PsbtShim) ProtoMessage() {} func (x *PsbtShim) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[80] + mi := &file_lightning_proto_msgTypes[81] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -8305,7 +8412,7 @@ func (x *PsbtShim) ProtoReflect() protoreflect.Message { // Deprecated: Use PsbtShim.ProtoReflect.Descriptor instead. func (*PsbtShim) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{80} + return file_lightning_proto_rawDescGZIP(), []int{81} } func (x *PsbtShim) GetPendingChanId() []byte { @@ -8344,7 +8451,7 @@ type FundingShim struct { func (x *FundingShim) Reset() { *x = FundingShim{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[81] + mi := &file_lightning_proto_msgTypes[82] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -8357,7 +8464,7 @@ func (x *FundingShim) String() string { func (*FundingShim) ProtoMessage() {} func (x *FundingShim) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[81] + mi := &file_lightning_proto_msgTypes[82] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -8370,7 +8477,7 @@ func (x *FundingShim) ProtoReflect() protoreflect.Message { // Deprecated: Use FundingShim.ProtoReflect.Descriptor instead. func (*FundingShim) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{81} + return file_lightning_proto_rawDescGZIP(), []int{82} } func (m *FundingShim) GetShim() isFundingShim_Shim { @@ -8426,7 +8533,7 @@ type FundingShimCancel struct { func (x *FundingShimCancel) Reset() { *x = FundingShimCancel{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[82] + mi := &file_lightning_proto_msgTypes[83] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -8439,7 +8546,7 @@ func (x *FundingShimCancel) String() string { func (*FundingShimCancel) ProtoMessage() {} func (x *FundingShimCancel) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[82] + mi := &file_lightning_proto_msgTypes[83] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -8452,7 +8559,7 @@ func (x *FundingShimCancel) ProtoReflect() protoreflect.Message { // Deprecated: Use FundingShimCancel.ProtoReflect.Descriptor instead. func (*FundingShimCancel) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{82} + return file_lightning_proto_rawDescGZIP(), []int{83} } func (x *FundingShimCancel) GetPendingChanId() []byte { @@ -8489,7 +8596,7 @@ type FundingPsbtVerify struct { func (x *FundingPsbtVerify) Reset() { *x = FundingPsbtVerify{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[83] + mi := &file_lightning_proto_msgTypes[84] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -8502,7 +8609,7 @@ func (x *FundingPsbtVerify) String() string { func (*FundingPsbtVerify) ProtoMessage() {} func (x *FundingPsbtVerify) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[83] + mi := &file_lightning_proto_msgTypes[84] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -8515,7 +8622,7 @@ func (x *FundingPsbtVerify) ProtoReflect() protoreflect.Message { // Deprecated: Use FundingPsbtVerify.ProtoReflect.Descriptor instead. func (*FundingPsbtVerify) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{83} + return file_lightning_proto_rawDescGZIP(), []int{84} } func (x *FundingPsbtVerify) GetFundedPsbt() []byte { @@ -8559,7 +8666,7 @@ type FundingPsbtFinalize struct { func (x *FundingPsbtFinalize) Reset() { *x = FundingPsbtFinalize{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[84] + mi := &file_lightning_proto_msgTypes[85] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -8572,7 +8679,7 @@ func (x *FundingPsbtFinalize) String() string { func (*FundingPsbtFinalize) ProtoMessage() {} func (x *FundingPsbtFinalize) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[84] + mi := &file_lightning_proto_msgTypes[85] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -8585,7 +8692,7 @@ func (x *FundingPsbtFinalize) ProtoReflect() protoreflect.Message { // Deprecated: Use FundingPsbtFinalize.ProtoReflect.Descriptor instead. func (*FundingPsbtFinalize) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{84} + return file_lightning_proto_rawDescGZIP(), []int{85} } func (x *FundingPsbtFinalize) GetSignedPsbt() []byte { @@ -8626,7 +8733,7 @@ type FundingTransitionMsg struct { func (x *FundingTransitionMsg) Reset() { *x = FundingTransitionMsg{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[85] + mi := &file_lightning_proto_msgTypes[86] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -8639,7 +8746,7 @@ func (x *FundingTransitionMsg) String() string { func (*FundingTransitionMsg) ProtoMessage() {} func (x *FundingTransitionMsg) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[85] + mi := &file_lightning_proto_msgTypes[86] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -8652,7 +8759,7 @@ func (x *FundingTransitionMsg) ProtoReflect() protoreflect.Message { // Deprecated: Use FundingTransitionMsg.ProtoReflect.Descriptor instead. func (*FundingTransitionMsg) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{85} + return file_lightning_proto_rawDescGZIP(), []int{86} } func (m *FundingTransitionMsg) GetTrigger() isFundingTransitionMsg_Trigger { @@ -8738,7 +8845,7 @@ type FundingStateStepResp struct { func (x *FundingStateStepResp) Reset() { *x = FundingStateStepResp{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[86] + mi := &file_lightning_proto_msgTypes[87] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -8751,7 +8858,7 @@ func (x *FundingStateStepResp) String() string { func (*FundingStateStepResp) ProtoMessage() {} func (x *FundingStateStepResp) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[86] + mi := &file_lightning_proto_msgTypes[87] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -8764,7 +8871,7 @@ func (x *FundingStateStepResp) ProtoReflect() protoreflect.Message { // Deprecated: Use FundingStateStepResp.ProtoReflect.Descriptor instead. func (*FundingStateStepResp) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{86} + return file_lightning_proto_rawDescGZIP(), []int{87} } type PendingHTLC struct { @@ -8791,7 +8898,7 @@ type PendingHTLC struct { func (x *PendingHTLC) Reset() { *x = PendingHTLC{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[87] + mi := &file_lightning_proto_msgTypes[88] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -8804,7 +8911,7 @@ func (x *PendingHTLC) String() string { func (*PendingHTLC) ProtoMessage() {} func (x *PendingHTLC) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[87] + mi := &file_lightning_proto_msgTypes[88] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -8817,7 +8924,7 @@ func (x *PendingHTLC) ProtoReflect() protoreflect.Message { // Deprecated: Use PendingHTLC.ProtoReflect.Descriptor instead. func (*PendingHTLC) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{87} + return file_lightning_proto_rawDescGZIP(), []int{88} } func (x *PendingHTLC) GetIncoming() bool { @@ -8875,7 +8982,7 @@ type PendingChannelsRequest struct { func (x *PendingChannelsRequest) Reset() { *x = PendingChannelsRequest{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[88] + mi := &file_lightning_proto_msgTypes[89] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -8888,7 +8995,7 @@ func (x *PendingChannelsRequest) String() string { func (*PendingChannelsRequest) ProtoMessage() {} func (x *PendingChannelsRequest) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[88] + mi := &file_lightning_proto_msgTypes[89] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -8901,7 +9008,7 @@ func (x *PendingChannelsRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use PendingChannelsRequest.ProtoReflect.Descriptor instead. func (*PendingChannelsRequest) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{88} + return file_lightning_proto_rawDescGZIP(), []int{89} } func (x *PendingChannelsRequest) GetIncludeRawTx() bool { @@ -8935,7 +9042,7 @@ type PendingChannelsResponse struct { func (x *PendingChannelsResponse) Reset() { *x = PendingChannelsResponse{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[89] + mi := &file_lightning_proto_msgTypes[90] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -8948,7 +9055,7 @@ func (x *PendingChannelsResponse) String() string { func (*PendingChannelsResponse) ProtoMessage() {} func (x *PendingChannelsResponse) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[89] + mi := &file_lightning_proto_msgTypes[90] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -8961,7 +9068,7 @@ func (x *PendingChannelsResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use PendingChannelsResponse.ProtoReflect.Descriptor instead. func (*PendingChannelsResponse) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{89} + return file_lightning_proto_rawDescGZIP(), []int{90} } func (x *PendingChannelsResponse) GetTotalLimboBalance() int64 { @@ -9009,7 +9116,7 @@ type ChannelEventSubscription struct { func (x *ChannelEventSubscription) Reset() { *x = ChannelEventSubscription{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[90] + mi := &file_lightning_proto_msgTypes[91] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -9022,7 +9129,7 @@ func (x *ChannelEventSubscription) String() string { func (*ChannelEventSubscription) ProtoMessage() {} func (x *ChannelEventSubscription) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[90] + mi := &file_lightning_proto_msgTypes[91] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -9035,7 +9142,7 @@ func (x *ChannelEventSubscription) ProtoReflect() protoreflect.Message { // Deprecated: Use ChannelEventSubscription.ProtoReflect.Descriptor instead. func (*ChannelEventSubscription) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{90} + return file_lightning_proto_rawDescGZIP(), []int{91} } type ChannelEventUpdate struct { @@ -9058,7 +9165,7 @@ type ChannelEventUpdate struct { func (x *ChannelEventUpdate) Reset() { *x = ChannelEventUpdate{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[91] + mi := &file_lightning_proto_msgTypes[92] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -9071,7 +9178,7 @@ func (x *ChannelEventUpdate) String() string { func (*ChannelEventUpdate) ProtoMessage() {} func (x *ChannelEventUpdate) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[91] + mi := &file_lightning_proto_msgTypes[92] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -9084,7 +9191,7 @@ func (x *ChannelEventUpdate) ProtoReflect() protoreflect.Message { // Deprecated: Use ChannelEventUpdate.ProtoReflect.Descriptor instead. func (*ChannelEventUpdate) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{91} + return file_lightning_proto_rawDescGZIP(), []int{92} } func (m *ChannelEventUpdate) GetChannel() isChannelEventUpdate_Channel { @@ -9197,7 +9304,7 @@ type WalletAccountBalance struct { func (x *WalletAccountBalance) Reset() { *x = WalletAccountBalance{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[92] + mi := &file_lightning_proto_msgTypes[93] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -9210,7 +9317,7 @@ func (x *WalletAccountBalance) String() string { func (*WalletAccountBalance) ProtoMessage() {} func (x *WalletAccountBalance) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[92] + mi := &file_lightning_proto_msgTypes[93] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -9223,7 +9330,7 @@ func (x *WalletAccountBalance) ProtoReflect() protoreflect.Message { // Deprecated: Use WalletAccountBalance.ProtoReflect.Descriptor instead. func (*WalletAccountBalance) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{92} + return file_lightning_proto_rawDescGZIP(), []int{93} } func (x *WalletAccountBalance) GetConfirmedBalance() int64 { @@ -9257,7 +9364,7 @@ type WalletBalanceRequest struct { func (x *WalletBalanceRequest) Reset() { *x = WalletBalanceRequest{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[93] + mi := &file_lightning_proto_msgTypes[94] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -9270,7 +9377,7 @@ func (x *WalletBalanceRequest) String() string { func (*WalletBalanceRequest) ProtoMessage() {} func (x *WalletBalanceRequest) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[93] + mi := &file_lightning_proto_msgTypes[94] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -9283,7 +9390,7 @@ func (x *WalletBalanceRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use WalletBalanceRequest.ProtoReflect.Descriptor instead. func (*WalletBalanceRequest) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{93} + return file_lightning_proto_rawDescGZIP(), []int{94} } func (x *WalletBalanceRequest) GetAccount() string { @@ -9323,7 +9430,7 @@ type WalletBalanceResponse struct { func (x *WalletBalanceResponse) Reset() { *x = WalletBalanceResponse{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[94] + mi := &file_lightning_proto_msgTypes[95] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -9336,7 +9443,7 @@ func (x *WalletBalanceResponse) String() string { func (*WalletBalanceResponse) ProtoMessage() {} func (x *WalletBalanceResponse) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[94] + mi := &file_lightning_proto_msgTypes[95] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -9349,7 +9456,7 @@ func (x *WalletBalanceResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use WalletBalanceResponse.ProtoReflect.Descriptor instead. func (*WalletBalanceResponse) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{94} + return file_lightning_proto_rawDescGZIP(), []int{95} } func (x *WalletBalanceResponse) GetTotalBalance() int64 { @@ -9408,7 +9515,7 @@ type Amount struct { func (x *Amount) Reset() { *x = Amount{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[95] + mi := &file_lightning_proto_msgTypes[96] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -9421,7 +9528,7 @@ func (x *Amount) String() string { func (*Amount) ProtoMessage() {} func (x *Amount) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[95] + mi := &file_lightning_proto_msgTypes[96] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -9434,7 +9541,7 @@ func (x *Amount) ProtoReflect() protoreflect.Message { // Deprecated: Use Amount.ProtoReflect.Descriptor instead. func (*Amount) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{95} + return file_lightning_proto_rawDescGZIP(), []int{96} } func (x *Amount) GetSat() uint64 { @@ -9460,7 +9567,7 @@ type ChannelBalanceRequest struct { func (x *ChannelBalanceRequest) Reset() { *x = ChannelBalanceRequest{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[96] + mi := &file_lightning_proto_msgTypes[97] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -9473,7 +9580,7 @@ func (x *ChannelBalanceRequest) String() string { func (*ChannelBalanceRequest) ProtoMessage() {} func (x *ChannelBalanceRequest) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[96] + mi := &file_lightning_proto_msgTypes[97] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -9486,7 +9593,7 @@ func (x *ChannelBalanceRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ChannelBalanceRequest.ProtoReflect.Descriptor instead. func (*ChannelBalanceRequest) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{96} + return file_lightning_proto_rawDescGZIP(), []int{97} } type ChannelBalanceResponse struct { @@ -9522,7 +9629,7 @@ type ChannelBalanceResponse struct { func (x *ChannelBalanceResponse) Reset() { *x = ChannelBalanceResponse{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[97] + mi := &file_lightning_proto_msgTypes[98] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -9535,7 +9642,7 @@ func (x *ChannelBalanceResponse) String() string { func (*ChannelBalanceResponse) ProtoMessage() {} func (x *ChannelBalanceResponse) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[97] + mi := &file_lightning_proto_msgTypes[98] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -9548,7 +9655,7 @@ func (x *ChannelBalanceResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ChannelBalanceResponse.ProtoReflect.Descriptor instead. func (*ChannelBalanceResponse) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{97} + return file_lightning_proto_rawDescGZIP(), []int{98} } // Deprecated: Marked as deprecated in lightning.proto. @@ -9699,7 +9806,7 @@ type QueryRoutesRequest struct { func (x *QueryRoutesRequest) Reset() { *x = QueryRoutesRequest{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[98] + mi := &file_lightning_proto_msgTypes[99] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -9712,7 +9819,7 @@ func (x *QueryRoutesRequest) String() string { func (*QueryRoutesRequest) ProtoMessage() {} func (x *QueryRoutesRequest) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[98] + mi := &file_lightning_proto_msgTypes[99] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -9725,7 +9832,7 @@ func (x *QueryRoutesRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use QueryRoutesRequest.ProtoReflect.Descriptor instead. func (*QueryRoutesRequest) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{98} + return file_lightning_proto_rawDescGZIP(), []int{99} } func (x *QueryRoutesRequest) GetPubKey() string { @@ -9871,7 +9978,7 @@ type NodePair struct { func (x *NodePair) Reset() { *x = NodePair{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[99] + mi := &file_lightning_proto_msgTypes[100] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -9884,7 +9991,7 @@ func (x *NodePair) String() string { func (*NodePair) ProtoMessage() {} func (x *NodePair) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[99] + mi := &file_lightning_proto_msgTypes[100] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -9897,7 +10004,7 @@ func (x *NodePair) ProtoReflect() protoreflect.Message { // Deprecated: Use NodePair.ProtoReflect.Descriptor instead. func (*NodePair) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{99} + return file_lightning_proto_rawDescGZIP(), []int{100} } func (x *NodePair) GetFrom() []byte { @@ -9931,7 +10038,7 @@ type EdgeLocator struct { func (x *EdgeLocator) Reset() { *x = EdgeLocator{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[100] + mi := &file_lightning_proto_msgTypes[101] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -9944,7 +10051,7 @@ func (x *EdgeLocator) String() string { func (*EdgeLocator) ProtoMessage() {} func (x *EdgeLocator) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[100] + mi := &file_lightning_proto_msgTypes[101] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -9957,7 +10064,7 @@ func (x *EdgeLocator) ProtoReflect() protoreflect.Message { // Deprecated: Use EdgeLocator.ProtoReflect.Descriptor instead. func (*EdgeLocator) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{100} + return file_lightning_proto_rawDescGZIP(), []int{101} } func (x *EdgeLocator) GetChannelId() uint64 { @@ -9990,7 +10097,7 @@ type QueryRoutesResponse struct { func (x *QueryRoutesResponse) Reset() { *x = QueryRoutesResponse{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[101] + mi := &file_lightning_proto_msgTypes[102] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -10003,7 +10110,7 @@ func (x *QueryRoutesResponse) String() string { func (*QueryRoutesResponse) ProtoMessage() {} func (x *QueryRoutesResponse) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[101] + mi := &file_lightning_proto_msgTypes[102] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -10016,7 +10123,7 @@ func (x *QueryRoutesResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use QueryRoutesResponse.ProtoReflect.Descriptor instead. func (*QueryRoutesResponse) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{101} + return file_lightning_proto_rawDescGZIP(), []int{102} } func (x *QueryRoutesResponse) GetRoutes() []*Route { @@ -10100,7 +10207,7 @@ type Hop struct { func (x *Hop) Reset() { *x = Hop{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[102] + mi := &file_lightning_proto_msgTypes[103] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -10113,7 +10220,7 @@ func (x *Hop) String() string { func (*Hop) ProtoMessage() {} func (x *Hop) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[102] + mi := &file_lightning_proto_msgTypes[103] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -10126,7 +10233,7 @@ func (x *Hop) ProtoReflect() protoreflect.Message { // Deprecated: Use Hop.ProtoReflect.Descriptor instead. func (*Hop) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{102} + return file_lightning_proto_rawDescGZIP(), []int{103} } func (x *Hop) GetChanId() uint64 { @@ -10266,7 +10373,7 @@ type MPPRecord struct { func (x *MPPRecord) Reset() { *x = MPPRecord{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[103] + mi := &file_lightning_proto_msgTypes[104] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -10279,7 +10386,7 @@ func (x *MPPRecord) String() string { func (*MPPRecord) ProtoMessage() {} func (x *MPPRecord) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[103] + mi := &file_lightning_proto_msgTypes[104] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -10292,7 +10399,7 @@ func (x *MPPRecord) ProtoReflect() protoreflect.Message { // Deprecated: Use MPPRecord.ProtoReflect.Descriptor instead. func (*MPPRecord) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{103} + return file_lightning_proto_rawDescGZIP(), []int{104} } func (x *MPPRecord) GetPaymentAddr() []byte { @@ -10322,7 +10429,7 @@ type AMPRecord struct { func (x *AMPRecord) Reset() { *x = AMPRecord{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[104] + mi := &file_lightning_proto_msgTypes[105] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -10335,7 +10442,7 @@ func (x *AMPRecord) String() string { func (*AMPRecord) ProtoMessage() {} func (x *AMPRecord) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[104] + mi := &file_lightning_proto_msgTypes[105] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -10348,7 +10455,7 @@ func (x *AMPRecord) ProtoReflect() protoreflect.Message { // Deprecated: Use AMPRecord.ProtoReflect.Descriptor instead. func (*AMPRecord) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{104} + return file_lightning_proto_rawDescGZIP(), []int{105} } func (x *AMPRecord) GetRootShare() []byte { @@ -10420,7 +10527,7 @@ type Route struct { func (x *Route) Reset() { *x = Route{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[105] + mi := &file_lightning_proto_msgTypes[106] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -10433,7 +10540,7 @@ func (x *Route) String() string { func (*Route) ProtoMessage() {} func (x *Route) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[105] + mi := &file_lightning_proto_msgTypes[106] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -10446,7 +10553,7 @@ func (x *Route) ProtoReflect() protoreflect.Message { // Deprecated: Use Route.ProtoReflect.Descriptor instead. func (*Route) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{105} + return file_lightning_proto_rawDescGZIP(), []int{106} } func (x *Route) GetTotalTimeLock() uint32 { @@ -10521,7 +10628,7 @@ type NodeInfoRequest struct { func (x *NodeInfoRequest) Reset() { *x = NodeInfoRequest{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[106] + mi := &file_lightning_proto_msgTypes[107] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -10534,7 +10641,7 @@ func (x *NodeInfoRequest) String() string { func (*NodeInfoRequest) ProtoMessage() {} func (x *NodeInfoRequest) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[106] + mi := &file_lightning_proto_msgTypes[107] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -10547,7 +10654,7 @@ func (x *NodeInfoRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use NodeInfoRequest.ProtoReflect.Descriptor instead. func (*NodeInfoRequest) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{106} + return file_lightning_proto_rawDescGZIP(), []int{107} } func (x *NodeInfoRequest) GetPubKey() string { @@ -10585,7 +10692,7 @@ type NodeInfo struct { func (x *NodeInfo) Reset() { *x = NodeInfo{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[107] + mi := &file_lightning_proto_msgTypes[108] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -10598,7 +10705,7 @@ func (x *NodeInfo) String() string { func (*NodeInfo) ProtoMessage() {} func (x *NodeInfo) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[107] + mi := &file_lightning_proto_msgTypes[108] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -10611,7 +10718,7 @@ func (x *NodeInfo) ProtoReflect() protoreflect.Message { // Deprecated: Use NodeInfo.ProtoReflect.Descriptor instead. func (*NodeInfo) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{107} + return file_lightning_proto_rawDescGZIP(), []int{108} } func (x *NodeInfo) GetNode() *LightningNode { @@ -10664,7 +10771,7 @@ type LightningNode struct { func (x *LightningNode) Reset() { *x = LightningNode{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[108] + mi := &file_lightning_proto_msgTypes[109] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -10677,7 +10784,7 @@ func (x *LightningNode) String() string { func (*LightningNode) ProtoMessage() {} func (x *LightningNode) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[108] + mi := &file_lightning_proto_msgTypes[109] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -10690,7 +10797,7 @@ func (x *LightningNode) ProtoReflect() protoreflect.Message { // Deprecated: Use LightningNode.ProtoReflect.Descriptor instead. func (*LightningNode) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{108} + return file_lightning_proto_rawDescGZIP(), []int{109} } func (x *LightningNode) GetLastUpdate() uint32 { @@ -10754,7 +10861,7 @@ type NodeAddress struct { func (x *NodeAddress) Reset() { *x = NodeAddress{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[109] + mi := &file_lightning_proto_msgTypes[110] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -10767,7 +10874,7 @@ func (x *NodeAddress) String() string { func (*NodeAddress) ProtoMessage() {} func (x *NodeAddress) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[109] + mi := &file_lightning_proto_msgTypes[110] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -10780,7 +10887,7 @@ func (x *NodeAddress) ProtoReflect() protoreflect.Message { // Deprecated: Use NodeAddress.ProtoReflect.Descriptor instead. func (*NodeAddress) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{109} + return file_lightning_proto_rawDescGZIP(), []int{110} } func (x *NodeAddress) GetNetwork() string { @@ -10818,7 +10925,7 @@ type RoutingPolicy struct { func (x *RoutingPolicy) Reset() { *x = RoutingPolicy{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[110] + mi := &file_lightning_proto_msgTypes[111] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -10831,7 +10938,7 @@ func (x *RoutingPolicy) String() string { func (*RoutingPolicy) ProtoMessage() {} func (x *RoutingPolicy) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[110] + mi := &file_lightning_proto_msgTypes[111] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -10844,7 +10951,7 @@ func (x *RoutingPolicy) ProtoReflect() protoreflect.Message { // Deprecated: Use RoutingPolicy.ProtoReflect.Descriptor instead. func (*RoutingPolicy) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{110} + return file_lightning_proto_rawDescGZIP(), []int{111} } func (x *RoutingPolicy) GetTimeLockDelta() uint32 { @@ -10946,7 +11053,7 @@ type ChannelEdge struct { func (x *ChannelEdge) Reset() { *x = ChannelEdge{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[111] + mi := &file_lightning_proto_msgTypes[112] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -10959,7 +11066,7 @@ func (x *ChannelEdge) String() string { func (*ChannelEdge) ProtoMessage() {} func (x *ChannelEdge) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[111] + mi := &file_lightning_proto_msgTypes[112] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -10972,7 +11079,7 @@ func (x *ChannelEdge) ProtoReflect() protoreflect.Message { // Deprecated: Use ChannelEdge.ProtoReflect.Descriptor instead. func (*ChannelEdge) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{111} + return file_lightning_proto_rawDescGZIP(), []int{112} } func (x *ChannelEdge) GetChannelId() uint64 { @@ -11053,7 +11160,7 @@ type ChannelGraphRequest struct { func (x *ChannelGraphRequest) Reset() { *x = ChannelGraphRequest{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[112] + mi := &file_lightning_proto_msgTypes[113] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -11066,7 +11173,7 @@ func (x *ChannelGraphRequest) String() string { func (*ChannelGraphRequest) ProtoMessage() {} func (x *ChannelGraphRequest) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[112] + mi := &file_lightning_proto_msgTypes[113] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -11079,7 +11186,7 @@ func (x *ChannelGraphRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ChannelGraphRequest.ProtoReflect.Descriptor instead. func (*ChannelGraphRequest) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{112} + return file_lightning_proto_rawDescGZIP(), []int{113} } func (x *ChannelGraphRequest) GetIncludeUnannounced() bool { @@ -11104,7 +11211,7 @@ type ChannelGraph struct { func (x *ChannelGraph) Reset() { *x = ChannelGraph{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[113] + mi := &file_lightning_proto_msgTypes[114] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -11117,7 +11224,7 @@ func (x *ChannelGraph) String() string { func (*ChannelGraph) ProtoMessage() {} func (x *ChannelGraph) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[113] + mi := &file_lightning_proto_msgTypes[114] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -11130,7 +11237,7 @@ func (x *ChannelGraph) ProtoReflect() protoreflect.Message { // Deprecated: Use ChannelGraph.ProtoReflect.Descriptor instead. func (*ChannelGraph) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{113} + return file_lightning_proto_rawDescGZIP(), []int{114} } func (x *ChannelGraph) GetNodes() []*LightningNode { @@ -11159,7 +11266,7 @@ type NodeMetricsRequest struct { func (x *NodeMetricsRequest) Reset() { *x = NodeMetricsRequest{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[114] + mi := &file_lightning_proto_msgTypes[115] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -11172,7 +11279,7 @@ func (x *NodeMetricsRequest) String() string { func (*NodeMetricsRequest) ProtoMessage() {} func (x *NodeMetricsRequest) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[114] + mi := &file_lightning_proto_msgTypes[115] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -11185,7 +11292,7 @@ func (x *NodeMetricsRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use NodeMetricsRequest.ProtoReflect.Descriptor instead. func (*NodeMetricsRequest) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{114} + return file_lightning_proto_rawDescGZIP(), []int{115} } func (x *NodeMetricsRequest) GetTypes() []NodeMetricType { @@ -11211,7 +11318,7 @@ type NodeMetricsResponse struct { func (x *NodeMetricsResponse) Reset() { *x = NodeMetricsResponse{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[115] + mi := &file_lightning_proto_msgTypes[116] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -11224,7 +11331,7 @@ func (x *NodeMetricsResponse) String() string { func (*NodeMetricsResponse) ProtoMessage() {} func (x *NodeMetricsResponse) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[115] + mi := &file_lightning_proto_msgTypes[116] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -11237,7 +11344,7 @@ func (x *NodeMetricsResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use NodeMetricsResponse.ProtoReflect.Descriptor instead. func (*NodeMetricsResponse) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{115} + return file_lightning_proto_rawDescGZIP(), []int{116} } func (x *NodeMetricsResponse) GetBetweennessCentrality() map[string]*FloatMetric { @@ -11261,7 +11368,7 @@ type FloatMetric struct { func (x *FloatMetric) Reset() { *x = FloatMetric{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[116] + mi := &file_lightning_proto_msgTypes[117] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -11274,7 +11381,7 @@ func (x *FloatMetric) String() string { func (*FloatMetric) ProtoMessage() {} func (x *FloatMetric) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[116] + mi := &file_lightning_proto_msgTypes[117] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -11287,7 +11394,7 @@ func (x *FloatMetric) ProtoReflect() protoreflect.Message { // Deprecated: Use FloatMetric.ProtoReflect.Descriptor instead. func (*FloatMetric) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{116} + return file_lightning_proto_rawDescGZIP(), []int{117} } func (x *FloatMetric) GetValue() float64 { @@ -11321,7 +11428,7 @@ type ChanInfoRequest struct { func (x *ChanInfoRequest) Reset() { *x = ChanInfoRequest{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[117] + mi := &file_lightning_proto_msgTypes[118] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -11334,7 +11441,7 @@ func (x *ChanInfoRequest) String() string { func (*ChanInfoRequest) ProtoMessage() {} func (x *ChanInfoRequest) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[117] + mi := &file_lightning_proto_msgTypes[118] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -11347,7 +11454,7 @@ func (x *ChanInfoRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ChanInfoRequest.ProtoReflect.Descriptor instead. func (*ChanInfoRequest) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{117} + return file_lightning_proto_rawDescGZIP(), []int{118} } func (x *ChanInfoRequest) GetChanId() uint64 { @@ -11373,7 +11480,7 @@ type NetworkInfoRequest struct { func (x *NetworkInfoRequest) Reset() { *x = NetworkInfoRequest{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[118] + mi := &file_lightning_proto_msgTypes[119] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -11386,7 +11493,7 @@ func (x *NetworkInfoRequest) String() string { func (*NetworkInfoRequest) ProtoMessage() {} func (x *NetworkInfoRequest) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[118] + mi := &file_lightning_proto_msgTypes[119] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -11399,7 +11506,7 @@ func (x *NetworkInfoRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use NetworkInfoRequest.ProtoReflect.Descriptor instead. func (*NetworkInfoRequest) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{118} + return file_lightning_proto_rawDescGZIP(), []int{119} } type NetworkInfo struct { @@ -11424,7 +11531,7 @@ type NetworkInfo struct { func (x *NetworkInfo) Reset() { *x = NetworkInfo{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[119] + mi := &file_lightning_proto_msgTypes[120] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -11437,7 +11544,7 @@ func (x *NetworkInfo) String() string { func (*NetworkInfo) ProtoMessage() {} func (x *NetworkInfo) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[119] + mi := &file_lightning_proto_msgTypes[120] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -11450,7 +11557,7 @@ func (x *NetworkInfo) ProtoReflect() protoreflect.Message { // Deprecated: Use NetworkInfo.ProtoReflect.Descriptor instead. func (*NetworkInfo) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{119} + return file_lightning_proto_rawDescGZIP(), []int{120} } func (x *NetworkInfo) GetGraphDiameter() uint32 { @@ -11539,7 +11646,7 @@ type StopRequest struct { func (x *StopRequest) Reset() { *x = StopRequest{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[120] + mi := &file_lightning_proto_msgTypes[121] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -11552,7 +11659,7 @@ func (x *StopRequest) String() string { func (*StopRequest) ProtoMessage() {} func (x *StopRequest) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[120] + mi := &file_lightning_proto_msgTypes[121] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -11565,7 +11672,7 @@ func (x *StopRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use StopRequest.ProtoReflect.Descriptor instead. func (*StopRequest) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{120} + return file_lightning_proto_rawDescGZIP(), []int{121} } type StopResponse struct { @@ -11577,7 +11684,7 @@ type StopResponse struct { func (x *StopResponse) Reset() { *x = StopResponse{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[121] + mi := &file_lightning_proto_msgTypes[122] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -11590,7 +11697,7 @@ func (x *StopResponse) String() string { func (*StopResponse) ProtoMessage() {} func (x *StopResponse) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[121] + mi := &file_lightning_proto_msgTypes[122] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -11603,7 +11710,7 @@ func (x *StopResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use StopResponse.ProtoReflect.Descriptor instead. func (*StopResponse) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{121} + return file_lightning_proto_rawDescGZIP(), []int{122} } type GraphTopologySubscription struct { @@ -11615,7 +11722,7 @@ type GraphTopologySubscription struct { func (x *GraphTopologySubscription) Reset() { *x = GraphTopologySubscription{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[122] + mi := &file_lightning_proto_msgTypes[123] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -11628,7 +11735,7 @@ func (x *GraphTopologySubscription) String() string { func (*GraphTopologySubscription) ProtoMessage() {} func (x *GraphTopologySubscription) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[122] + mi := &file_lightning_proto_msgTypes[123] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -11641,7 +11748,7 @@ func (x *GraphTopologySubscription) ProtoReflect() protoreflect.Message { // Deprecated: Use GraphTopologySubscription.ProtoReflect.Descriptor instead. func (*GraphTopologySubscription) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{122} + return file_lightning_proto_rawDescGZIP(), []int{123} } type GraphTopologyUpdate struct { @@ -11657,7 +11764,7 @@ type GraphTopologyUpdate struct { func (x *GraphTopologyUpdate) Reset() { *x = GraphTopologyUpdate{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[123] + mi := &file_lightning_proto_msgTypes[124] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -11670,7 +11777,7 @@ func (x *GraphTopologyUpdate) String() string { func (*GraphTopologyUpdate) ProtoMessage() {} func (x *GraphTopologyUpdate) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[123] + mi := &file_lightning_proto_msgTypes[124] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -11683,7 +11790,7 @@ func (x *GraphTopologyUpdate) ProtoReflect() protoreflect.Message { // Deprecated: Use GraphTopologyUpdate.ProtoReflect.Descriptor instead. func (*GraphTopologyUpdate) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{123} + return file_lightning_proto_rawDescGZIP(), []int{124} } func (x *GraphTopologyUpdate) GetNodeUpdates() []*NodeUpdate { @@ -11732,7 +11839,7 @@ type NodeUpdate struct { func (x *NodeUpdate) Reset() { *x = NodeUpdate{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[124] + mi := &file_lightning_proto_msgTypes[125] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -11745,7 +11852,7 @@ func (x *NodeUpdate) String() string { func (*NodeUpdate) ProtoMessage() {} func (x *NodeUpdate) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[124] + mi := &file_lightning_proto_msgTypes[125] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -11758,7 +11865,7 @@ func (x *NodeUpdate) ProtoReflect() protoreflect.Message { // Deprecated: Use NodeUpdate.ProtoReflect.Descriptor instead. func (*NodeUpdate) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{124} + return file_lightning_proto_rawDescGZIP(), []int{125} } // Deprecated: Marked as deprecated in lightning.proto. @@ -11831,7 +11938,7 @@ type ChannelEdgeUpdate struct { func (x *ChannelEdgeUpdate) Reset() { *x = ChannelEdgeUpdate{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[125] + mi := &file_lightning_proto_msgTypes[126] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -11844,7 +11951,7 @@ func (x *ChannelEdgeUpdate) String() string { func (*ChannelEdgeUpdate) ProtoMessage() {} func (x *ChannelEdgeUpdate) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[125] + mi := &file_lightning_proto_msgTypes[126] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -11857,7 +11964,7 @@ func (x *ChannelEdgeUpdate) ProtoReflect() protoreflect.Message { // Deprecated: Use ChannelEdgeUpdate.ProtoReflect.Descriptor instead. func (*ChannelEdgeUpdate) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{125} + return file_lightning_proto_rawDescGZIP(), []int{126} } func (x *ChannelEdgeUpdate) GetChanId() uint64 { @@ -11919,7 +12026,7 @@ type ClosedChannelUpdate struct { func (x *ClosedChannelUpdate) Reset() { *x = ClosedChannelUpdate{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[126] + mi := &file_lightning_proto_msgTypes[127] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -11932,7 +12039,7 @@ func (x *ClosedChannelUpdate) String() string { func (*ClosedChannelUpdate) ProtoMessage() {} func (x *ClosedChannelUpdate) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[126] + mi := &file_lightning_proto_msgTypes[127] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -11945,7 +12052,7 @@ func (x *ClosedChannelUpdate) ProtoReflect() protoreflect.Message { // Deprecated: Use ClosedChannelUpdate.ProtoReflect.Descriptor instead. func (*ClosedChannelUpdate) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{126} + return file_lightning_proto_rawDescGZIP(), []int{127} } func (x *ClosedChannelUpdate) GetChanId() uint64 { @@ -11997,7 +12104,7 @@ type HopHint struct { func (x *HopHint) Reset() { *x = HopHint{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[127] + mi := &file_lightning_proto_msgTypes[128] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -12010,7 +12117,7 @@ func (x *HopHint) String() string { func (*HopHint) ProtoMessage() {} func (x *HopHint) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[127] + mi := &file_lightning_proto_msgTypes[128] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -12023,7 +12130,7 @@ func (x *HopHint) ProtoReflect() protoreflect.Message { // Deprecated: Use HopHint.ProtoReflect.Descriptor instead. func (*HopHint) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{127} + return file_lightning_proto_rawDescGZIP(), []int{128} } func (x *HopHint) GetNodeId() string { @@ -12072,7 +12179,7 @@ type SetID struct { func (x *SetID) Reset() { *x = SetID{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[128] + mi := &file_lightning_proto_msgTypes[129] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -12085,7 +12192,7 @@ func (x *SetID) String() string { func (*SetID) ProtoMessage() {} func (x *SetID) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[128] + mi := &file_lightning_proto_msgTypes[129] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -12098,7 +12205,7 @@ func (x *SetID) ProtoReflect() protoreflect.Message { // Deprecated: Use SetID.ProtoReflect.Descriptor instead. func (*SetID) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{128} + return file_lightning_proto_rawDescGZIP(), []int{129} } func (x *SetID) GetSetId() []byte { @@ -12121,7 +12228,7 @@ type RouteHint struct { func (x *RouteHint) Reset() { *x = RouteHint{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[129] + mi := &file_lightning_proto_msgTypes[130] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -12134,7 +12241,7 @@ func (x *RouteHint) String() string { func (*RouteHint) ProtoMessage() {} func (x *RouteHint) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[129] + mi := &file_lightning_proto_msgTypes[130] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -12147,7 +12254,7 @@ func (x *RouteHint) ProtoReflect() protoreflect.Message { // Deprecated: Use RouteHint.ProtoReflect.Descriptor instead. func (*RouteHint) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{129} + return file_lightning_proto_rawDescGZIP(), []int{130} } func (x *RouteHint) GetHopHints() []*HopHint { @@ -12185,7 +12292,7 @@ type BlindedPaymentPath struct { func (x *BlindedPaymentPath) Reset() { *x = BlindedPaymentPath{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[130] + mi := &file_lightning_proto_msgTypes[131] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -12198,7 +12305,7 @@ func (x *BlindedPaymentPath) String() string { func (*BlindedPaymentPath) ProtoMessage() {} func (x *BlindedPaymentPath) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[130] + mi := &file_lightning_proto_msgTypes[131] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -12211,7 +12318,7 @@ func (x *BlindedPaymentPath) ProtoReflect() protoreflect.Message { // Deprecated: Use BlindedPaymentPath.ProtoReflect.Descriptor instead. func (*BlindedPaymentPath) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{130} + return file_lightning_proto_rawDescGZIP(), []int{131} } func (x *BlindedPaymentPath) GetBlindedPath() *BlindedPath { @@ -12281,7 +12388,7 @@ type BlindedPath struct { func (x *BlindedPath) Reset() { *x = BlindedPath{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[131] + mi := &file_lightning_proto_msgTypes[132] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -12294,7 +12401,7 @@ func (x *BlindedPath) String() string { func (*BlindedPath) ProtoMessage() {} func (x *BlindedPath) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[131] + mi := &file_lightning_proto_msgTypes[132] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -12307,7 +12414,7 @@ func (x *BlindedPath) ProtoReflect() protoreflect.Message { // Deprecated: Use BlindedPath.ProtoReflect.Descriptor instead. func (*BlindedPath) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{131} + return file_lightning_proto_rawDescGZIP(), []int{132} } func (x *BlindedPath) GetIntroductionNode() []byte { @@ -12345,7 +12452,7 @@ type BlindedHop struct { func (x *BlindedHop) Reset() { *x = BlindedHop{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[132] + mi := &file_lightning_proto_msgTypes[133] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -12358,7 +12465,7 @@ func (x *BlindedHop) String() string { func (*BlindedHop) ProtoMessage() {} func (x *BlindedHop) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[132] + mi := &file_lightning_proto_msgTypes[133] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -12371,7 +12478,7 @@ func (x *BlindedHop) ProtoReflect() protoreflect.Message { // Deprecated: Use BlindedHop.ProtoReflect.Descriptor instead. func (*BlindedHop) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{132} + return file_lightning_proto_rawDescGZIP(), []int{133} } func (x *BlindedHop) GetBlindedNode() []byte { @@ -12406,7 +12513,7 @@ type AMPInvoiceState struct { func (x *AMPInvoiceState) Reset() { *x = AMPInvoiceState{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[133] + mi := &file_lightning_proto_msgTypes[134] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -12419,7 +12526,7 @@ func (x *AMPInvoiceState) String() string { func (*AMPInvoiceState) ProtoMessage() {} func (x *AMPInvoiceState) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[133] + mi := &file_lightning_proto_msgTypes[134] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -12432,7 +12539,7 @@ func (x *AMPInvoiceState) ProtoReflect() protoreflect.Message { // Deprecated: Use AMPInvoiceState.ProtoReflect.Descriptor instead. func (*AMPInvoiceState) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{133} + return file_lightning_proto_rawDescGZIP(), []int{134} } func (x *AMPInvoiceState) GetState() InvoiceHTLCState { @@ -12599,7 +12706,7 @@ type Invoice struct { func (x *Invoice) Reset() { *x = Invoice{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[134] + mi := &file_lightning_proto_msgTypes[135] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -12612,7 +12719,7 @@ func (x *Invoice) String() string { func (*Invoice) ProtoMessage() {} func (x *Invoice) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[134] + mi := &file_lightning_proto_msgTypes[135] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -12625,7 +12732,7 @@ func (x *Invoice) ProtoReflect() protoreflect.Message { // Deprecated: Use Invoice.ProtoReflect.Descriptor instead. func (*Invoice) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{134} + return file_lightning_proto_rawDescGZIP(), []int{135} } func (x *Invoice) GetMemo() string { @@ -12858,7 +12965,7 @@ type BlindedPathConfig struct { func (x *BlindedPathConfig) Reset() { *x = BlindedPathConfig{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[135] + mi := &file_lightning_proto_msgTypes[136] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -12871,7 +12978,7 @@ func (x *BlindedPathConfig) String() string { func (*BlindedPathConfig) ProtoMessage() {} func (x *BlindedPathConfig) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[135] + mi := &file_lightning_proto_msgTypes[136] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -12884,7 +12991,7 @@ func (x *BlindedPathConfig) ProtoReflect() protoreflect.Message { // Deprecated: Use BlindedPathConfig.ProtoReflect.Descriptor instead. func (*BlindedPathConfig) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{135} + return file_lightning_proto_rawDescGZIP(), []int{136} } func (x *BlindedPathConfig) GetMinNumRealHops() uint32 { @@ -12950,7 +13057,7 @@ type InvoiceHTLC struct { func (x *InvoiceHTLC) Reset() { *x = InvoiceHTLC{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[136] + mi := &file_lightning_proto_msgTypes[137] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -12963,7 +13070,7 @@ func (x *InvoiceHTLC) String() string { func (*InvoiceHTLC) ProtoMessage() {} func (x *InvoiceHTLC) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[136] + mi := &file_lightning_proto_msgTypes[137] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -12976,7 +13083,7 @@ func (x *InvoiceHTLC) ProtoReflect() protoreflect.Message { // Deprecated: Use InvoiceHTLC.ProtoReflect.Descriptor instead. func (*InvoiceHTLC) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{136} + return file_lightning_proto_rawDescGZIP(), []int{137} } func (x *InvoiceHTLC) GetChanId() uint64 { @@ -13088,7 +13195,7 @@ type AMP struct { func (x *AMP) Reset() { *x = AMP{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[137] + mi := &file_lightning_proto_msgTypes[138] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -13101,7 +13208,7 @@ func (x *AMP) String() string { func (*AMP) ProtoMessage() {} func (x *AMP) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[137] + mi := &file_lightning_proto_msgTypes[138] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -13114,7 +13221,7 @@ func (x *AMP) ProtoReflect() protoreflect.Message { // Deprecated: Use AMP.ProtoReflect.Descriptor instead. func (*AMP) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{137} + return file_lightning_proto_rawDescGZIP(), []int{138} } func (x *AMP) GetRootShare() []byte { @@ -13176,7 +13283,7 @@ type AddInvoiceResponse struct { func (x *AddInvoiceResponse) Reset() { *x = AddInvoiceResponse{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[138] + mi := &file_lightning_proto_msgTypes[139] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -13189,7 +13296,7 @@ func (x *AddInvoiceResponse) String() string { func (*AddInvoiceResponse) ProtoMessage() {} func (x *AddInvoiceResponse) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[138] + mi := &file_lightning_proto_msgTypes[139] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -13202,7 +13309,7 @@ func (x *AddInvoiceResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use AddInvoiceResponse.ProtoReflect.Descriptor instead. func (*AddInvoiceResponse) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{138} + return file_lightning_proto_rawDescGZIP(), []int{139} } func (x *AddInvoiceResponse) GetRHash() []byte { @@ -13253,7 +13360,7 @@ type PaymentHash struct { func (x *PaymentHash) Reset() { *x = PaymentHash{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[139] + mi := &file_lightning_proto_msgTypes[140] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -13266,7 +13373,7 @@ func (x *PaymentHash) String() string { func (*PaymentHash) ProtoMessage() {} func (x *PaymentHash) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[139] + mi := &file_lightning_proto_msgTypes[140] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -13279,7 +13386,7 @@ func (x *PaymentHash) ProtoReflect() protoreflect.Message { // Deprecated: Use PaymentHash.ProtoReflect.Descriptor instead. func (*PaymentHash) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{139} + return file_lightning_proto_rawDescGZIP(), []int{140} } // Deprecated: Marked as deprecated in lightning.proto. @@ -13324,7 +13431,7 @@ type ListInvoiceRequest struct { func (x *ListInvoiceRequest) Reset() { *x = ListInvoiceRequest{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[140] + mi := &file_lightning_proto_msgTypes[141] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -13337,7 +13444,7 @@ func (x *ListInvoiceRequest) String() string { func (*ListInvoiceRequest) ProtoMessage() {} func (x *ListInvoiceRequest) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[140] + mi := &file_lightning_proto_msgTypes[141] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -13350,7 +13457,7 @@ func (x *ListInvoiceRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ListInvoiceRequest.ProtoReflect.Descriptor instead. func (*ListInvoiceRequest) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{140} + return file_lightning_proto_rawDescGZIP(), []int{141} } func (x *ListInvoiceRequest) GetPendingOnly() bool { @@ -13414,7 +13521,7 @@ type ListInvoiceResponse struct { func (x *ListInvoiceResponse) Reset() { *x = ListInvoiceResponse{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[141] + mi := &file_lightning_proto_msgTypes[142] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -13427,7 +13534,7 @@ func (x *ListInvoiceResponse) String() string { func (*ListInvoiceResponse) ProtoMessage() {} func (x *ListInvoiceResponse) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[141] + mi := &file_lightning_proto_msgTypes[142] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -13440,7 +13547,7 @@ func (x *ListInvoiceResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ListInvoiceResponse.ProtoReflect.Descriptor instead. func (*ListInvoiceResponse) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{141} + return file_lightning_proto_rawDescGZIP(), []int{142} } func (x *ListInvoiceResponse) GetInvoices() []*Invoice { @@ -13484,7 +13591,7 @@ type InvoiceSubscription struct { func (x *InvoiceSubscription) Reset() { *x = InvoiceSubscription{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[142] + mi := &file_lightning_proto_msgTypes[143] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -13497,7 +13604,7 @@ func (x *InvoiceSubscription) String() string { func (*InvoiceSubscription) ProtoMessage() {} func (x *InvoiceSubscription) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[142] + mi := &file_lightning_proto_msgTypes[143] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -13510,7 +13617,7 @@ func (x *InvoiceSubscription) ProtoReflect() protoreflect.Message { // Deprecated: Use InvoiceSubscription.ProtoReflect.Descriptor instead. func (*InvoiceSubscription) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{142} + return file_lightning_proto_rawDescGZIP(), []int{143} } func (x *InvoiceSubscription) GetAddIndex() uint64 { @@ -13577,7 +13684,7 @@ type Payment struct { func (x *Payment) Reset() { *x = Payment{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[143] + mi := &file_lightning_proto_msgTypes[144] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -13590,7 +13697,7 @@ func (x *Payment) String() string { func (*Payment) ProtoMessage() {} func (x *Payment) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[143] + mi := &file_lightning_proto_msgTypes[144] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -13603,7 +13710,7 @@ func (x *Payment) ProtoReflect() protoreflect.Message { // Deprecated: Use Payment.ProtoReflect.Descriptor instead. func (*Payment) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{143} + return file_lightning_proto_rawDescGZIP(), []int{144} } func (x *Payment) GetPaymentHash() string { @@ -13746,7 +13853,7 @@ type HTLCAttempt struct { func (x *HTLCAttempt) Reset() { *x = HTLCAttempt{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[144] + mi := &file_lightning_proto_msgTypes[145] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -13759,7 +13866,7 @@ func (x *HTLCAttempt) String() string { func (*HTLCAttempt) ProtoMessage() {} func (x *HTLCAttempt) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[144] + mi := &file_lightning_proto_msgTypes[145] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -13772,7 +13879,7 @@ func (x *HTLCAttempt) ProtoReflect() protoreflect.Message { // Deprecated: Use HTLCAttempt.ProtoReflect.Descriptor instead. func (*HTLCAttempt) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{144} + return file_lightning_proto_rawDescGZIP(), []int{145} } func (x *HTLCAttempt) GetAttemptId() uint64 { @@ -13862,7 +13969,7 @@ type ListPaymentsRequest struct { func (x *ListPaymentsRequest) Reset() { *x = ListPaymentsRequest{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[145] + mi := &file_lightning_proto_msgTypes[146] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -13875,7 +13982,7 @@ func (x *ListPaymentsRequest) String() string { func (*ListPaymentsRequest) ProtoMessage() {} func (x *ListPaymentsRequest) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[145] + mi := &file_lightning_proto_msgTypes[146] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -13888,7 +13995,7 @@ func (x *ListPaymentsRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ListPaymentsRequest.ProtoReflect.Descriptor instead. func (*ListPaymentsRequest) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{145} + return file_lightning_proto_rawDescGZIP(), []int{146} } func (x *ListPaymentsRequest) GetIncludeIncomplete() bool { @@ -13963,7 +14070,7 @@ type ListPaymentsResponse struct { func (x *ListPaymentsResponse) Reset() { *x = ListPaymentsResponse{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[146] + mi := &file_lightning_proto_msgTypes[147] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -13976,7 +14083,7 @@ func (x *ListPaymentsResponse) String() string { func (*ListPaymentsResponse) ProtoMessage() {} func (x *ListPaymentsResponse) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[146] + mi := &file_lightning_proto_msgTypes[147] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -13989,7 +14096,7 @@ func (x *ListPaymentsResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ListPaymentsResponse.ProtoReflect.Descriptor instead. func (*ListPaymentsResponse) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{146} + return file_lightning_proto_rawDescGZIP(), []int{147} } func (x *ListPaymentsResponse) GetPayments() []*Payment { @@ -14034,7 +14141,7 @@ type DeletePaymentRequest struct { func (x *DeletePaymentRequest) Reset() { *x = DeletePaymentRequest{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[147] + mi := &file_lightning_proto_msgTypes[148] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -14047,7 +14154,7 @@ func (x *DeletePaymentRequest) String() string { func (*DeletePaymentRequest) ProtoMessage() {} func (x *DeletePaymentRequest) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[147] + mi := &file_lightning_proto_msgTypes[148] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -14060,7 +14167,7 @@ func (x *DeletePaymentRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use DeletePaymentRequest.ProtoReflect.Descriptor instead. func (*DeletePaymentRequest) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{147} + return file_lightning_proto_rawDescGZIP(), []int{148} } func (x *DeletePaymentRequest) GetPaymentHash() []byte { @@ -14094,7 +14201,7 @@ type DeleteAllPaymentsRequest struct { func (x *DeleteAllPaymentsRequest) Reset() { *x = DeleteAllPaymentsRequest{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[148] + mi := &file_lightning_proto_msgTypes[149] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -14107,7 +14214,7 @@ func (x *DeleteAllPaymentsRequest) String() string { func (*DeleteAllPaymentsRequest) ProtoMessage() {} func (x *DeleteAllPaymentsRequest) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[148] + mi := &file_lightning_proto_msgTypes[149] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -14120,7 +14227,7 @@ func (x *DeleteAllPaymentsRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use DeleteAllPaymentsRequest.ProtoReflect.Descriptor instead. func (*DeleteAllPaymentsRequest) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{148} + return file_lightning_proto_rawDescGZIP(), []int{149} } func (x *DeleteAllPaymentsRequest) GetFailedPaymentsOnly() bool { @@ -14153,7 +14260,7 @@ type DeletePaymentResponse struct { func (x *DeletePaymentResponse) Reset() { *x = DeletePaymentResponse{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[149] + mi := &file_lightning_proto_msgTypes[150] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -14166,7 +14273,7 @@ func (x *DeletePaymentResponse) String() string { func (*DeletePaymentResponse) ProtoMessage() {} func (x *DeletePaymentResponse) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[149] + mi := &file_lightning_proto_msgTypes[150] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -14179,7 +14286,7 @@ func (x *DeletePaymentResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use DeletePaymentResponse.ProtoReflect.Descriptor instead. func (*DeletePaymentResponse) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{149} + return file_lightning_proto_rawDescGZIP(), []int{150} } type DeleteAllPaymentsResponse struct { @@ -14191,7 +14298,7 @@ type DeleteAllPaymentsResponse struct { func (x *DeleteAllPaymentsResponse) Reset() { *x = DeleteAllPaymentsResponse{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[150] + mi := &file_lightning_proto_msgTypes[151] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -14204,7 +14311,7 @@ func (x *DeleteAllPaymentsResponse) String() string { func (*DeleteAllPaymentsResponse) ProtoMessage() {} func (x *DeleteAllPaymentsResponse) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[150] + mi := &file_lightning_proto_msgTypes[151] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -14217,7 +14324,7 @@ func (x *DeleteAllPaymentsResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use DeleteAllPaymentsResponse.ProtoReflect.Descriptor instead. func (*DeleteAllPaymentsResponse) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{150} + return file_lightning_proto_rawDescGZIP(), []int{151} } type AbandonChannelRequest struct { @@ -14236,7 +14343,7 @@ type AbandonChannelRequest struct { func (x *AbandonChannelRequest) Reset() { *x = AbandonChannelRequest{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[151] + mi := &file_lightning_proto_msgTypes[152] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -14249,7 +14356,7 @@ func (x *AbandonChannelRequest) String() string { func (*AbandonChannelRequest) ProtoMessage() {} func (x *AbandonChannelRequest) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[151] + mi := &file_lightning_proto_msgTypes[152] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -14262,7 +14369,7 @@ func (x *AbandonChannelRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use AbandonChannelRequest.ProtoReflect.Descriptor instead. func (*AbandonChannelRequest) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{151} + return file_lightning_proto_rawDescGZIP(), []int{152} } func (x *AbandonChannelRequest) GetChannelPoint() *ChannelPoint { @@ -14295,7 +14402,7 @@ type AbandonChannelResponse struct { func (x *AbandonChannelResponse) Reset() { *x = AbandonChannelResponse{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[152] + mi := &file_lightning_proto_msgTypes[153] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -14308,7 +14415,7 @@ func (x *AbandonChannelResponse) String() string { func (*AbandonChannelResponse) ProtoMessage() {} func (x *AbandonChannelResponse) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[152] + mi := &file_lightning_proto_msgTypes[153] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -14321,7 +14428,7 @@ func (x *AbandonChannelResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use AbandonChannelResponse.ProtoReflect.Descriptor instead. func (*AbandonChannelResponse) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{152} + return file_lightning_proto_rawDescGZIP(), []int{153} } type DebugLevelRequest struct { @@ -14336,7 +14443,7 @@ type DebugLevelRequest struct { func (x *DebugLevelRequest) Reset() { *x = DebugLevelRequest{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[153] + mi := &file_lightning_proto_msgTypes[154] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -14349,7 +14456,7 @@ func (x *DebugLevelRequest) String() string { func (*DebugLevelRequest) ProtoMessage() {} func (x *DebugLevelRequest) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[153] + mi := &file_lightning_proto_msgTypes[154] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -14362,7 +14469,7 @@ func (x *DebugLevelRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use DebugLevelRequest.ProtoReflect.Descriptor instead. func (*DebugLevelRequest) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{153} + return file_lightning_proto_rawDescGZIP(), []int{154} } func (x *DebugLevelRequest) GetShow() bool { @@ -14390,7 +14497,7 @@ type DebugLevelResponse struct { func (x *DebugLevelResponse) Reset() { *x = DebugLevelResponse{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[154] + mi := &file_lightning_proto_msgTypes[155] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -14403,7 +14510,7 @@ func (x *DebugLevelResponse) String() string { func (*DebugLevelResponse) ProtoMessage() {} func (x *DebugLevelResponse) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[154] + mi := &file_lightning_proto_msgTypes[155] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -14416,7 +14523,7 @@ func (x *DebugLevelResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use DebugLevelResponse.ProtoReflect.Descriptor instead. func (*DebugLevelResponse) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{154} + return file_lightning_proto_rawDescGZIP(), []int{155} } func (x *DebugLevelResponse) GetSubSystems() string { @@ -14438,7 +14545,7 @@ type PayReqString struct { func (x *PayReqString) Reset() { *x = PayReqString{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[155] + mi := &file_lightning_proto_msgTypes[156] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -14451,7 +14558,7 @@ func (x *PayReqString) String() string { func (*PayReqString) ProtoMessage() {} func (x *PayReqString) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[155] + mi := &file_lightning_proto_msgTypes[156] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -14464,7 +14571,7 @@ func (x *PayReqString) ProtoReflect() protoreflect.Message { // Deprecated: Use PayReqString.ProtoReflect.Descriptor instead. func (*PayReqString) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{155} + return file_lightning_proto_rawDescGZIP(), []int{156} } func (x *PayReqString) GetPayReq() string { @@ -14498,7 +14605,7 @@ type PayReq struct { func (x *PayReq) Reset() { *x = PayReq{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[156] + mi := &file_lightning_proto_msgTypes[157] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -14511,7 +14618,7 @@ func (x *PayReq) String() string { func (*PayReq) ProtoMessage() {} func (x *PayReq) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[156] + mi := &file_lightning_proto_msgTypes[157] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -14524,7 +14631,7 @@ func (x *PayReq) ProtoReflect() protoreflect.Message { // Deprecated: Use PayReq.ProtoReflect.Descriptor instead. func (*PayReq) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{156} + return file_lightning_proto_rawDescGZIP(), []int{157} } func (x *PayReq) GetDestination() string { @@ -14638,7 +14745,7 @@ type Feature struct { func (x *Feature) Reset() { *x = Feature{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[157] + mi := &file_lightning_proto_msgTypes[158] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -14651,7 +14758,7 @@ func (x *Feature) String() string { func (*Feature) ProtoMessage() {} func (x *Feature) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[157] + mi := &file_lightning_proto_msgTypes[158] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -14664,7 +14771,7 @@ func (x *Feature) ProtoReflect() protoreflect.Message { // Deprecated: Use Feature.ProtoReflect.Descriptor instead. func (*Feature) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{157} + return file_lightning_proto_rawDescGZIP(), []int{158} } func (x *Feature) GetName() string { @@ -14697,7 +14804,7 @@ type FeeReportRequest struct { func (x *FeeReportRequest) Reset() { *x = FeeReportRequest{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[158] + mi := &file_lightning_proto_msgTypes[159] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -14710,7 +14817,7 @@ func (x *FeeReportRequest) String() string { func (*FeeReportRequest) ProtoMessage() {} func (x *FeeReportRequest) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[158] + mi := &file_lightning_proto_msgTypes[159] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -14723,7 +14830,7 @@ func (x *FeeReportRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use FeeReportRequest.ProtoReflect.Descriptor instead. func (*FeeReportRequest) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{158} + return file_lightning_proto_rawDescGZIP(), []int{159} } type ChannelFeeReport struct { @@ -14753,7 +14860,7 @@ type ChannelFeeReport struct { func (x *ChannelFeeReport) Reset() { *x = ChannelFeeReport{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[159] + mi := &file_lightning_proto_msgTypes[160] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -14766,7 +14873,7 @@ func (x *ChannelFeeReport) String() string { func (*ChannelFeeReport) ProtoMessage() {} func (x *ChannelFeeReport) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[159] + mi := &file_lightning_proto_msgTypes[160] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -14779,7 +14886,7 @@ func (x *ChannelFeeReport) ProtoReflect() protoreflect.Message { // Deprecated: Use ChannelFeeReport.ProtoReflect.Descriptor instead. func (*ChannelFeeReport) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{159} + return file_lightning_proto_rawDescGZIP(), []int{160} } func (x *ChannelFeeReport) GetChanId() uint64 { @@ -14853,7 +14960,7 @@ type FeeReportResponse struct { func (x *FeeReportResponse) Reset() { *x = FeeReportResponse{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[160] + mi := &file_lightning_proto_msgTypes[161] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -14866,7 +14973,7 @@ func (x *FeeReportResponse) String() string { func (*FeeReportResponse) ProtoMessage() {} func (x *FeeReportResponse) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[160] + mi := &file_lightning_proto_msgTypes[161] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -14879,7 +14986,7 @@ func (x *FeeReportResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use FeeReportResponse.ProtoReflect.Descriptor instead. func (*FeeReportResponse) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{160} + return file_lightning_proto_rawDescGZIP(), []int{161} } func (x *FeeReportResponse) GetChannelFees() []*ChannelFeeReport { @@ -14926,7 +15033,7 @@ type InboundFee struct { func (x *InboundFee) Reset() { *x = InboundFee{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[161] + mi := &file_lightning_proto_msgTypes[162] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -14939,7 +15046,7 @@ func (x *InboundFee) String() string { func (*InboundFee) ProtoMessage() {} func (x *InboundFee) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[161] + mi := &file_lightning_proto_msgTypes[162] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -14952,7 +15059,7 @@ func (x *InboundFee) ProtoReflect() protoreflect.Message { // Deprecated: Use InboundFee.ProtoReflect.Descriptor instead. func (*InboundFee) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{161} + return file_lightning_proto_rawDescGZIP(), []int{162} } func (x *InboundFee) GetBaseFeeMsat() int32 { @@ -15004,7 +15111,7 @@ type PolicyUpdateRequest struct { func (x *PolicyUpdateRequest) Reset() { *x = PolicyUpdateRequest{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[162] + mi := &file_lightning_proto_msgTypes[163] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -15017,7 +15124,7 @@ func (x *PolicyUpdateRequest) String() string { func (*PolicyUpdateRequest) ProtoMessage() {} func (x *PolicyUpdateRequest) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[162] + mi := &file_lightning_proto_msgTypes[163] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -15030,7 +15137,7 @@ func (x *PolicyUpdateRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use PolicyUpdateRequest.ProtoReflect.Descriptor instead. func (*PolicyUpdateRequest) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{162} + return file_lightning_proto_rawDescGZIP(), []int{163} } func (m *PolicyUpdateRequest) GetScope() isPolicyUpdateRequest_Scope { @@ -15144,7 +15251,7 @@ type FailedUpdate struct { func (x *FailedUpdate) Reset() { *x = FailedUpdate{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[163] + mi := &file_lightning_proto_msgTypes[164] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -15157,7 +15264,7 @@ func (x *FailedUpdate) String() string { func (*FailedUpdate) ProtoMessage() {} func (x *FailedUpdate) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[163] + mi := &file_lightning_proto_msgTypes[164] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -15170,7 +15277,7 @@ func (x *FailedUpdate) ProtoReflect() protoreflect.Message { // Deprecated: Use FailedUpdate.ProtoReflect.Descriptor instead. func (*FailedUpdate) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{163} + return file_lightning_proto_rawDescGZIP(), []int{164} } func (x *FailedUpdate) GetOutpoint() *OutPoint { @@ -15206,7 +15313,7 @@ type PolicyUpdateResponse struct { func (x *PolicyUpdateResponse) Reset() { *x = PolicyUpdateResponse{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[164] + mi := &file_lightning_proto_msgTypes[165] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -15219,7 +15326,7 @@ func (x *PolicyUpdateResponse) String() string { func (*PolicyUpdateResponse) ProtoMessage() {} func (x *PolicyUpdateResponse) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[164] + mi := &file_lightning_proto_msgTypes[165] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -15232,7 +15339,7 @@ func (x *PolicyUpdateResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use PolicyUpdateResponse.ProtoReflect.Descriptor instead. func (*PolicyUpdateResponse) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{164} + return file_lightning_proto_rawDescGZIP(), []int{165} } func (x *PolicyUpdateResponse) GetFailedUpdates() []*FailedUpdate { @@ -15269,7 +15376,7 @@ type ForwardingHistoryRequest struct { func (x *ForwardingHistoryRequest) Reset() { *x = ForwardingHistoryRequest{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[165] + mi := &file_lightning_proto_msgTypes[166] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -15282,7 +15389,7 @@ func (x *ForwardingHistoryRequest) String() string { func (*ForwardingHistoryRequest) ProtoMessage() {} func (x *ForwardingHistoryRequest) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[165] + mi := &file_lightning_proto_msgTypes[166] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -15295,7 +15402,7 @@ func (x *ForwardingHistoryRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ForwardingHistoryRequest.ProtoReflect.Descriptor instead. func (*ForwardingHistoryRequest) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{165} + return file_lightning_proto_rawDescGZIP(), []int{166} } func (x *ForwardingHistoryRequest) GetStartTime() uint64 { @@ -15376,7 +15483,7 @@ type ForwardingEvent struct { func (x *ForwardingEvent) Reset() { *x = ForwardingEvent{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[166] + mi := &file_lightning_proto_msgTypes[167] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -15389,7 +15496,7 @@ func (x *ForwardingEvent) String() string { func (*ForwardingEvent) ProtoMessage() {} func (x *ForwardingEvent) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[166] + mi := &file_lightning_proto_msgTypes[167] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -15402,7 +15509,7 @@ func (x *ForwardingEvent) ProtoReflect() protoreflect.Message { // Deprecated: Use ForwardingEvent.ProtoReflect.Descriptor instead. func (*ForwardingEvent) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{166} + return file_lightning_proto_rawDescGZIP(), []int{167} } // Deprecated: Marked as deprecated in lightning.proto. @@ -15506,7 +15613,7 @@ type ForwardingHistoryResponse struct { func (x *ForwardingHistoryResponse) Reset() { *x = ForwardingHistoryResponse{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[167] + mi := &file_lightning_proto_msgTypes[168] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -15519,7 +15626,7 @@ func (x *ForwardingHistoryResponse) String() string { func (*ForwardingHistoryResponse) ProtoMessage() {} func (x *ForwardingHistoryResponse) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[167] + mi := &file_lightning_proto_msgTypes[168] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -15532,7 +15639,7 @@ func (x *ForwardingHistoryResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ForwardingHistoryResponse.ProtoReflect.Descriptor instead. func (*ForwardingHistoryResponse) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{167} + return file_lightning_proto_rawDescGZIP(), []int{168} } func (x *ForwardingHistoryResponse) GetForwardingEvents() []*ForwardingEvent { @@ -15561,7 +15668,7 @@ type ExportChannelBackupRequest struct { func (x *ExportChannelBackupRequest) Reset() { *x = ExportChannelBackupRequest{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[168] + mi := &file_lightning_proto_msgTypes[169] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -15574,7 +15681,7 @@ func (x *ExportChannelBackupRequest) String() string { func (*ExportChannelBackupRequest) ProtoMessage() {} func (x *ExportChannelBackupRequest) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[168] + mi := &file_lightning_proto_msgTypes[169] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -15587,7 +15694,7 @@ func (x *ExportChannelBackupRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ExportChannelBackupRequest.ProtoReflect.Descriptor instead. func (*ExportChannelBackupRequest) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{168} + return file_lightning_proto_rawDescGZIP(), []int{169} } func (x *ExportChannelBackupRequest) GetChanPoint() *ChannelPoint { @@ -15614,7 +15721,7 @@ type ChannelBackup struct { func (x *ChannelBackup) Reset() { *x = ChannelBackup{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[169] + mi := &file_lightning_proto_msgTypes[170] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -15627,7 +15734,7 @@ func (x *ChannelBackup) String() string { func (*ChannelBackup) ProtoMessage() {} func (x *ChannelBackup) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[169] + mi := &file_lightning_proto_msgTypes[170] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -15640,7 +15747,7 @@ func (x *ChannelBackup) ProtoReflect() protoreflect.Message { // Deprecated: Use ChannelBackup.ProtoReflect.Descriptor instead. func (*ChannelBackup) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{169} + return file_lightning_proto_rawDescGZIP(), []int{170} } func (x *ChannelBackup) GetChanPoint() *ChannelPoint { @@ -15674,7 +15781,7 @@ type MultiChanBackup struct { func (x *MultiChanBackup) Reset() { *x = MultiChanBackup{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[170] + mi := &file_lightning_proto_msgTypes[171] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -15687,7 +15794,7 @@ func (x *MultiChanBackup) String() string { func (*MultiChanBackup) ProtoMessage() {} func (x *MultiChanBackup) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[170] + mi := &file_lightning_proto_msgTypes[171] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -15700,7 +15807,7 @@ func (x *MultiChanBackup) ProtoReflect() protoreflect.Message { // Deprecated: Use MultiChanBackup.ProtoReflect.Descriptor instead. func (*MultiChanBackup) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{170} + return file_lightning_proto_rawDescGZIP(), []int{171} } func (x *MultiChanBackup) GetChanPoints() []*ChannelPoint { @@ -15726,7 +15833,7 @@ type ChanBackupExportRequest struct { func (x *ChanBackupExportRequest) Reset() { *x = ChanBackupExportRequest{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[171] + mi := &file_lightning_proto_msgTypes[172] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -15739,7 +15846,7 @@ func (x *ChanBackupExportRequest) String() string { func (*ChanBackupExportRequest) ProtoMessage() {} func (x *ChanBackupExportRequest) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[171] + mi := &file_lightning_proto_msgTypes[172] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -15752,7 +15859,7 @@ func (x *ChanBackupExportRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ChanBackupExportRequest.ProtoReflect.Descriptor instead. func (*ChanBackupExportRequest) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{171} + return file_lightning_proto_rawDescGZIP(), []int{172} } type ChanBackupSnapshot struct { @@ -15771,7 +15878,7 @@ type ChanBackupSnapshot struct { func (x *ChanBackupSnapshot) Reset() { *x = ChanBackupSnapshot{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[172] + mi := &file_lightning_proto_msgTypes[173] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -15784,7 +15891,7 @@ func (x *ChanBackupSnapshot) String() string { func (*ChanBackupSnapshot) ProtoMessage() {} func (x *ChanBackupSnapshot) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[172] + mi := &file_lightning_proto_msgTypes[173] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -15797,7 +15904,7 @@ func (x *ChanBackupSnapshot) ProtoReflect() protoreflect.Message { // Deprecated: Use ChanBackupSnapshot.ProtoReflect.Descriptor instead. func (*ChanBackupSnapshot) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{172} + return file_lightning_proto_rawDescGZIP(), []int{173} } func (x *ChanBackupSnapshot) GetSingleChanBackups() *ChannelBackups { @@ -15826,7 +15933,7 @@ type ChannelBackups struct { func (x *ChannelBackups) Reset() { *x = ChannelBackups{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[173] + mi := &file_lightning_proto_msgTypes[174] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -15839,7 +15946,7 @@ func (x *ChannelBackups) String() string { func (*ChannelBackups) ProtoMessage() {} func (x *ChannelBackups) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[173] + mi := &file_lightning_proto_msgTypes[174] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -15852,7 +15959,7 @@ func (x *ChannelBackups) ProtoReflect() protoreflect.Message { // Deprecated: Use ChannelBackups.ProtoReflect.Descriptor instead. func (*ChannelBackups) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{173} + return file_lightning_proto_rawDescGZIP(), []int{174} } func (x *ChannelBackups) GetChanBackups() []*ChannelBackup { @@ -15877,7 +15984,7 @@ type RestoreChanBackupRequest struct { func (x *RestoreChanBackupRequest) Reset() { *x = RestoreChanBackupRequest{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[174] + mi := &file_lightning_proto_msgTypes[175] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -15890,7 +15997,7 @@ func (x *RestoreChanBackupRequest) String() string { func (*RestoreChanBackupRequest) ProtoMessage() {} func (x *RestoreChanBackupRequest) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[174] + mi := &file_lightning_proto_msgTypes[175] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -15903,7 +16010,7 @@ func (x *RestoreChanBackupRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use RestoreChanBackupRequest.ProtoReflect.Descriptor instead. func (*RestoreChanBackupRequest) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{174} + return file_lightning_proto_rawDescGZIP(), []int{175} } func (m *RestoreChanBackupRequest) GetBackup() isRestoreChanBackupRequest_Backup { @@ -15955,7 +16062,7 @@ type RestoreBackupResponse struct { func (x *RestoreBackupResponse) Reset() { *x = RestoreBackupResponse{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[175] + mi := &file_lightning_proto_msgTypes[176] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -15968,7 +16075,7 @@ func (x *RestoreBackupResponse) String() string { func (*RestoreBackupResponse) ProtoMessage() {} func (x *RestoreBackupResponse) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[175] + mi := &file_lightning_proto_msgTypes[176] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -15981,7 +16088,7 @@ func (x *RestoreBackupResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use RestoreBackupResponse.ProtoReflect.Descriptor instead. func (*RestoreBackupResponse) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{175} + return file_lightning_proto_rawDescGZIP(), []int{176} } type ChannelBackupSubscription struct { @@ -15993,7 +16100,7 @@ type ChannelBackupSubscription struct { func (x *ChannelBackupSubscription) Reset() { *x = ChannelBackupSubscription{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[176] + mi := &file_lightning_proto_msgTypes[177] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -16006,7 +16113,7 @@ func (x *ChannelBackupSubscription) String() string { func (*ChannelBackupSubscription) ProtoMessage() {} func (x *ChannelBackupSubscription) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[176] + mi := &file_lightning_proto_msgTypes[177] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -16019,7 +16126,7 @@ func (x *ChannelBackupSubscription) ProtoReflect() protoreflect.Message { // Deprecated: Use ChannelBackupSubscription.ProtoReflect.Descriptor instead. func (*ChannelBackupSubscription) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{176} + return file_lightning_proto_rawDescGZIP(), []int{177} } type VerifyChanBackupResponse struct { @@ -16031,7 +16138,7 @@ type VerifyChanBackupResponse struct { func (x *VerifyChanBackupResponse) Reset() { *x = VerifyChanBackupResponse{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[177] + mi := &file_lightning_proto_msgTypes[178] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -16044,7 +16151,7 @@ func (x *VerifyChanBackupResponse) String() string { func (*VerifyChanBackupResponse) ProtoMessage() {} func (x *VerifyChanBackupResponse) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[177] + mi := &file_lightning_proto_msgTypes[178] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -16057,7 +16164,7 @@ func (x *VerifyChanBackupResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use VerifyChanBackupResponse.ProtoReflect.Descriptor instead. func (*VerifyChanBackupResponse) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{177} + return file_lightning_proto_rawDescGZIP(), []int{178} } type MacaroonPermission struct { @@ -16074,7 +16181,7 @@ type MacaroonPermission struct { func (x *MacaroonPermission) Reset() { *x = MacaroonPermission{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[178] + mi := &file_lightning_proto_msgTypes[179] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -16087,7 +16194,7 @@ func (x *MacaroonPermission) String() string { func (*MacaroonPermission) ProtoMessage() {} func (x *MacaroonPermission) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[178] + mi := &file_lightning_proto_msgTypes[179] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -16100,7 +16207,7 @@ func (x *MacaroonPermission) ProtoReflect() protoreflect.Message { // Deprecated: Use MacaroonPermission.ProtoReflect.Descriptor instead. func (*MacaroonPermission) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{178} + return file_lightning_proto_rawDescGZIP(), []int{179} } func (x *MacaroonPermission) GetEntity() string { @@ -16134,7 +16241,7 @@ type BakeMacaroonRequest struct { func (x *BakeMacaroonRequest) Reset() { *x = BakeMacaroonRequest{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[179] + mi := &file_lightning_proto_msgTypes[180] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -16147,7 +16254,7 @@ func (x *BakeMacaroonRequest) String() string { func (*BakeMacaroonRequest) ProtoMessage() {} func (x *BakeMacaroonRequest) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[179] + mi := &file_lightning_proto_msgTypes[180] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -16160,7 +16267,7 @@ func (x *BakeMacaroonRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use BakeMacaroonRequest.ProtoReflect.Descriptor instead. func (*BakeMacaroonRequest) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{179} + return file_lightning_proto_rawDescGZIP(), []int{180} } func (x *BakeMacaroonRequest) GetPermissions() []*MacaroonPermission { @@ -16196,7 +16303,7 @@ type BakeMacaroonResponse struct { func (x *BakeMacaroonResponse) Reset() { *x = BakeMacaroonResponse{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[180] + mi := &file_lightning_proto_msgTypes[181] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -16209,7 +16316,7 @@ func (x *BakeMacaroonResponse) String() string { func (*BakeMacaroonResponse) ProtoMessage() {} func (x *BakeMacaroonResponse) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[180] + mi := &file_lightning_proto_msgTypes[181] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -16222,7 +16329,7 @@ func (x *BakeMacaroonResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use BakeMacaroonResponse.ProtoReflect.Descriptor instead. func (*BakeMacaroonResponse) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{180} + return file_lightning_proto_rawDescGZIP(), []int{181} } func (x *BakeMacaroonResponse) GetMacaroon() string { @@ -16241,7 +16348,7 @@ type ListMacaroonIDsRequest struct { func (x *ListMacaroonIDsRequest) Reset() { *x = ListMacaroonIDsRequest{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[181] + mi := &file_lightning_proto_msgTypes[182] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -16254,7 +16361,7 @@ func (x *ListMacaroonIDsRequest) String() string { func (*ListMacaroonIDsRequest) ProtoMessage() {} func (x *ListMacaroonIDsRequest) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[181] + mi := &file_lightning_proto_msgTypes[182] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -16267,7 +16374,7 @@ func (x *ListMacaroonIDsRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ListMacaroonIDsRequest.ProtoReflect.Descriptor instead. func (*ListMacaroonIDsRequest) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{181} + return file_lightning_proto_rawDescGZIP(), []int{182} } type ListMacaroonIDsResponse struct { @@ -16282,7 +16389,7 @@ type ListMacaroonIDsResponse struct { func (x *ListMacaroonIDsResponse) Reset() { *x = ListMacaroonIDsResponse{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[182] + mi := &file_lightning_proto_msgTypes[183] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -16295,7 +16402,7 @@ func (x *ListMacaroonIDsResponse) String() string { func (*ListMacaroonIDsResponse) ProtoMessage() {} func (x *ListMacaroonIDsResponse) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[182] + mi := &file_lightning_proto_msgTypes[183] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -16308,7 +16415,7 @@ func (x *ListMacaroonIDsResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ListMacaroonIDsResponse.ProtoReflect.Descriptor instead. func (*ListMacaroonIDsResponse) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{182} + return file_lightning_proto_rawDescGZIP(), []int{183} } func (x *ListMacaroonIDsResponse) GetRootKeyIds() []uint64 { @@ -16330,7 +16437,7 @@ type DeleteMacaroonIDRequest struct { func (x *DeleteMacaroonIDRequest) Reset() { *x = DeleteMacaroonIDRequest{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[183] + mi := &file_lightning_proto_msgTypes[184] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -16343,7 +16450,7 @@ func (x *DeleteMacaroonIDRequest) String() string { func (*DeleteMacaroonIDRequest) ProtoMessage() {} func (x *DeleteMacaroonIDRequest) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[183] + mi := &file_lightning_proto_msgTypes[184] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -16356,7 +16463,7 @@ func (x *DeleteMacaroonIDRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use DeleteMacaroonIDRequest.ProtoReflect.Descriptor instead. func (*DeleteMacaroonIDRequest) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{183} + return file_lightning_proto_rawDescGZIP(), []int{184} } func (x *DeleteMacaroonIDRequest) GetRootKeyId() uint64 { @@ -16378,7 +16485,7 @@ type DeleteMacaroonIDResponse struct { func (x *DeleteMacaroonIDResponse) Reset() { *x = DeleteMacaroonIDResponse{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[184] + mi := &file_lightning_proto_msgTypes[185] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -16391,7 +16498,7 @@ func (x *DeleteMacaroonIDResponse) String() string { func (*DeleteMacaroonIDResponse) ProtoMessage() {} func (x *DeleteMacaroonIDResponse) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[184] + mi := &file_lightning_proto_msgTypes[185] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -16404,7 +16511,7 @@ func (x *DeleteMacaroonIDResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use DeleteMacaroonIDResponse.ProtoReflect.Descriptor instead. func (*DeleteMacaroonIDResponse) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{184} + return file_lightning_proto_rawDescGZIP(), []int{185} } func (x *DeleteMacaroonIDResponse) GetDeleted() bool { @@ -16426,7 +16533,7 @@ type MacaroonPermissionList struct { func (x *MacaroonPermissionList) Reset() { *x = MacaroonPermissionList{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[185] + mi := &file_lightning_proto_msgTypes[186] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -16439,7 +16546,7 @@ func (x *MacaroonPermissionList) String() string { func (*MacaroonPermissionList) ProtoMessage() {} func (x *MacaroonPermissionList) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[185] + mi := &file_lightning_proto_msgTypes[186] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -16452,7 +16559,7 @@ func (x *MacaroonPermissionList) ProtoReflect() protoreflect.Message { // Deprecated: Use MacaroonPermissionList.ProtoReflect.Descriptor instead. func (*MacaroonPermissionList) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{185} + return file_lightning_proto_rawDescGZIP(), []int{186} } func (x *MacaroonPermissionList) GetPermissions() []*MacaroonPermission { @@ -16471,7 +16578,7 @@ type ListPermissionsRequest struct { func (x *ListPermissionsRequest) Reset() { *x = ListPermissionsRequest{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[186] + mi := &file_lightning_proto_msgTypes[187] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -16484,7 +16591,7 @@ func (x *ListPermissionsRequest) String() string { func (*ListPermissionsRequest) ProtoMessage() {} func (x *ListPermissionsRequest) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[186] + mi := &file_lightning_proto_msgTypes[187] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -16497,7 +16604,7 @@ func (x *ListPermissionsRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ListPermissionsRequest.ProtoReflect.Descriptor instead. func (*ListPermissionsRequest) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{186} + return file_lightning_proto_rawDescGZIP(), []int{187} } type ListPermissionsResponse struct { @@ -16513,7 +16620,7 @@ type ListPermissionsResponse struct { func (x *ListPermissionsResponse) Reset() { *x = ListPermissionsResponse{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[187] + mi := &file_lightning_proto_msgTypes[188] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -16526,7 +16633,7 @@ func (x *ListPermissionsResponse) String() string { func (*ListPermissionsResponse) ProtoMessage() {} func (x *ListPermissionsResponse) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[187] + mi := &file_lightning_proto_msgTypes[188] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -16539,7 +16646,7 @@ func (x *ListPermissionsResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ListPermissionsResponse.ProtoReflect.Descriptor instead. func (*ListPermissionsResponse) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{187} + return file_lightning_proto_rawDescGZIP(), []int{188} } func (x *ListPermissionsResponse) GetMethodPermissions() map[string]*MacaroonPermissionList { @@ -16576,7 +16683,7 @@ type Failure struct { func (x *Failure) Reset() { *x = Failure{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[188] + mi := &file_lightning_proto_msgTypes[189] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -16589,7 +16696,7 @@ func (x *Failure) String() string { func (*Failure) ProtoMessage() {} func (x *Failure) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[188] + mi := &file_lightning_proto_msgTypes[189] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -16602,7 +16709,7 @@ func (x *Failure) ProtoReflect() protoreflect.Message { // Deprecated: Use Failure.ProtoReflect.Descriptor instead. func (*Failure) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{188} + return file_lightning_proto_rawDescGZIP(), []int{189} } func (x *Failure) GetCode() Failure_FailureCode { @@ -16716,7 +16823,7 @@ type ChannelUpdate struct { func (x *ChannelUpdate) Reset() { *x = ChannelUpdate{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[189] + mi := &file_lightning_proto_msgTypes[190] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -16729,7 +16836,7 @@ func (x *ChannelUpdate) String() string { func (*ChannelUpdate) ProtoMessage() {} func (x *ChannelUpdate) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[189] + mi := &file_lightning_proto_msgTypes[190] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -16742,7 +16849,7 @@ func (x *ChannelUpdate) ProtoReflect() protoreflect.Message { // Deprecated: Use ChannelUpdate.ProtoReflect.Descriptor instead. func (*ChannelUpdate) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{189} + return file_lightning_proto_rawDescGZIP(), []int{190} } func (x *ChannelUpdate) GetSignature() []byte { @@ -16842,7 +16949,7 @@ type MacaroonId struct { func (x *MacaroonId) Reset() { *x = MacaroonId{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[190] + mi := &file_lightning_proto_msgTypes[191] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -16855,7 +16962,7 @@ func (x *MacaroonId) String() string { func (*MacaroonId) ProtoMessage() {} func (x *MacaroonId) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[190] + mi := &file_lightning_proto_msgTypes[191] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -16868,7 +16975,7 @@ func (x *MacaroonId) ProtoReflect() protoreflect.Message { // Deprecated: Use MacaroonId.ProtoReflect.Descriptor instead. func (*MacaroonId) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{190} + return file_lightning_proto_rawDescGZIP(), []int{191} } func (x *MacaroonId) GetNonce() []byte { @@ -16904,7 +17011,7 @@ type Op struct { func (x *Op) Reset() { *x = Op{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[191] + mi := &file_lightning_proto_msgTypes[192] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -16917,7 +17024,7 @@ func (x *Op) String() string { func (*Op) ProtoMessage() {} func (x *Op) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[191] + mi := &file_lightning_proto_msgTypes[192] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -16930,7 +17037,7 @@ func (x *Op) ProtoReflect() protoreflect.Message { // Deprecated: Use Op.ProtoReflect.Descriptor instead. func (*Op) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{191} + return file_lightning_proto_rawDescGZIP(), []int{192} } func (x *Op) GetEntity() string { @@ -16960,7 +17067,7 @@ type CheckMacPermRequest struct { func (x *CheckMacPermRequest) Reset() { *x = CheckMacPermRequest{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[192] + mi := &file_lightning_proto_msgTypes[193] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -16973,7 +17080,7 @@ func (x *CheckMacPermRequest) String() string { func (*CheckMacPermRequest) ProtoMessage() {} func (x *CheckMacPermRequest) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[192] + mi := &file_lightning_proto_msgTypes[193] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -16986,7 +17093,7 @@ func (x *CheckMacPermRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use CheckMacPermRequest.ProtoReflect.Descriptor instead. func (*CheckMacPermRequest) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{192} + return file_lightning_proto_rawDescGZIP(), []int{193} } func (x *CheckMacPermRequest) GetMacaroon() []byte { @@ -17021,7 +17128,7 @@ type CheckMacPermResponse struct { func (x *CheckMacPermResponse) Reset() { *x = CheckMacPermResponse{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[193] + mi := &file_lightning_proto_msgTypes[194] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -17034,7 +17141,7 @@ func (x *CheckMacPermResponse) String() string { func (*CheckMacPermResponse) ProtoMessage() {} func (x *CheckMacPermResponse) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[193] + mi := &file_lightning_proto_msgTypes[194] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -17047,7 +17154,7 @@ func (x *CheckMacPermResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use CheckMacPermResponse.ProtoReflect.Descriptor instead. func (*CheckMacPermResponse) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{193} + return file_lightning_proto_rawDescGZIP(), []int{194} } func (x *CheckMacPermResponse) GetValid() bool { @@ -17101,7 +17208,7 @@ type RPCMiddlewareRequest struct { func (x *RPCMiddlewareRequest) Reset() { *x = RPCMiddlewareRequest{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[194] + mi := &file_lightning_proto_msgTypes[195] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -17114,7 +17221,7 @@ func (x *RPCMiddlewareRequest) String() string { func (*RPCMiddlewareRequest) ProtoMessage() {} func (x *RPCMiddlewareRequest) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[194] + mi := &file_lightning_proto_msgTypes[195] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -17127,7 +17234,7 @@ func (x *RPCMiddlewareRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use RPCMiddlewareRequest.ProtoReflect.Descriptor instead. func (*RPCMiddlewareRequest) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{194} + return file_lightning_proto_rawDescGZIP(), []int{195} } func (x *RPCMiddlewareRequest) GetRequestId() uint64 { @@ -17255,7 +17362,7 @@ type StreamAuth struct { func (x *StreamAuth) Reset() { *x = StreamAuth{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[195] + mi := &file_lightning_proto_msgTypes[196] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -17268,7 +17375,7 @@ func (x *StreamAuth) String() string { func (*StreamAuth) ProtoMessage() {} func (x *StreamAuth) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[195] + mi := &file_lightning_proto_msgTypes[196] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -17281,7 +17388,7 @@ func (x *StreamAuth) ProtoReflect() protoreflect.Message { // Deprecated: Use StreamAuth.ProtoReflect.Descriptor instead. func (*StreamAuth) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{195} + return file_lightning_proto_rawDescGZIP(), []int{196} } func (x *StreamAuth) GetMethodFullUri() string { @@ -17318,7 +17425,7 @@ type RPCMessage struct { func (x *RPCMessage) Reset() { *x = RPCMessage{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[196] + mi := &file_lightning_proto_msgTypes[197] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -17331,7 +17438,7 @@ func (x *RPCMessage) String() string { func (*RPCMessage) ProtoMessage() {} func (x *RPCMessage) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[196] + mi := &file_lightning_proto_msgTypes[197] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -17344,7 +17451,7 @@ func (x *RPCMessage) ProtoReflect() protoreflect.Message { // Deprecated: Use RPCMessage.ProtoReflect.Descriptor instead. func (*RPCMessage) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{196} + return file_lightning_proto_rawDescGZIP(), []int{197} } func (x *RPCMessage) GetMethodFullUri() string { @@ -17405,7 +17512,7 @@ type RPCMiddlewareResponse struct { func (x *RPCMiddlewareResponse) Reset() { *x = RPCMiddlewareResponse{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[197] + mi := &file_lightning_proto_msgTypes[198] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -17418,7 +17525,7 @@ func (x *RPCMiddlewareResponse) String() string { func (*RPCMiddlewareResponse) ProtoMessage() {} func (x *RPCMiddlewareResponse) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[197] + mi := &file_lightning_proto_msgTypes[198] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -17431,7 +17538,7 @@ func (x *RPCMiddlewareResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use RPCMiddlewareResponse.ProtoReflect.Descriptor instead. func (*RPCMiddlewareResponse) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{197} + return file_lightning_proto_rawDescGZIP(), []int{198} } func (x *RPCMiddlewareResponse) GetRefMsgId() uint64 { @@ -17517,7 +17624,7 @@ type MiddlewareRegistration struct { func (x *MiddlewareRegistration) Reset() { *x = MiddlewareRegistration{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[198] + mi := &file_lightning_proto_msgTypes[199] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -17530,7 +17637,7 @@ func (x *MiddlewareRegistration) String() string { func (*MiddlewareRegistration) ProtoMessage() {} func (x *MiddlewareRegistration) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[198] + mi := &file_lightning_proto_msgTypes[199] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -17543,7 +17650,7 @@ func (x *MiddlewareRegistration) ProtoReflect() protoreflect.Message { // Deprecated: Use MiddlewareRegistration.ProtoReflect.Descriptor instead. func (*MiddlewareRegistration) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{198} + return file_lightning_proto_rawDescGZIP(), []int{199} } func (x *MiddlewareRegistration) GetMiddlewareName() string { @@ -17590,7 +17697,7 @@ type InterceptFeedback struct { func (x *InterceptFeedback) Reset() { *x = InterceptFeedback{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[199] + mi := &file_lightning_proto_msgTypes[200] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -17603,7 +17710,7 @@ func (x *InterceptFeedback) String() string { func (*InterceptFeedback) ProtoMessage() {} func (x *InterceptFeedback) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[199] + mi := &file_lightning_proto_msgTypes[200] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -17616,7 +17723,7 @@ func (x *InterceptFeedback) ProtoReflect() protoreflect.Message { // Deprecated: Use InterceptFeedback.ProtoReflect.Descriptor instead. func (*InterceptFeedback) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{199} + return file_lightning_proto_rawDescGZIP(), []int{200} } func (x *InterceptFeedback) GetError() string { @@ -17677,7 +17784,7 @@ type PendingChannelsResponse_PendingChannel struct { func (x *PendingChannelsResponse_PendingChannel) Reset() { *x = PendingChannelsResponse_PendingChannel{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[206] + mi := &file_lightning_proto_msgTypes[207] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -17690,7 +17797,7 @@ func (x *PendingChannelsResponse_PendingChannel) String() string { func (*PendingChannelsResponse_PendingChannel) ProtoMessage() {} func (x *PendingChannelsResponse_PendingChannel) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[206] + mi := &file_lightning_proto_msgTypes[207] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -17703,7 +17810,7 @@ func (x *PendingChannelsResponse_PendingChannel) ProtoReflect() protoreflect.Mes // Deprecated: Use PendingChannelsResponse_PendingChannel.ProtoReflect.Descriptor instead. func (*PendingChannelsResponse_PendingChannel) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{89, 0} + return file_lightning_proto_rawDescGZIP(), []int{90, 0} } func (x *PendingChannelsResponse_PendingChannel) GetRemoteNodePub() string { @@ -17838,7 +17945,7 @@ type PendingChannelsResponse_PendingOpenChannel struct { func (x *PendingChannelsResponse_PendingOpenChannel) Reset() { *x = PendingChannelsResponse_PendingOpenChannel{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[207] + mi := &file_lightning_proto_msgTypes[208] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -17851,7 +17958,7 @@ func (x *PendingChannelsResponse_PendingOpenChannel) String() string { func (*PendingChannelsResponse_PendingOpenChannel) ProtoMessage() {} func (x *PendingChannelsResponse_PendingOpenChannel) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[207] + mi := &file_lightning_proto_msgTypes[208] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -17864,7 +17971,7 @@ func (x *PendingChannelsResponse_PendingOpenChannel) ProtoReflect() protoreflect // Deprecated: Use PendingChannelsResponse_PendingOpenChannel.ProtoReflect.Descriptor instead. func (*PendingChannelsResponse_PendingOpenChannel) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{89, 1} + return file_lightning_proto_rawDescGZIP(), []int{90, 1} } func (x *PendingChannelsResponse_PendingOpenChannel) GetChannel() *PendingChannelsResponse_PendingChannel { @@ -17924,7 +18031,7 @@ type PendingChannelsResponse_WaitingCloseChannel struct { func (x *PendingChannelsResponse_WaitingCloseChannel) Reset() { *x = PendingChannelsResponse_WaitingCloseChannel{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[208] + mi := &file_lightning_proto_msgTypes[209] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -17937,7 +18044,7 @@ func (x *PendingChannelsResponse_WaitingCloseChannel) String() string { func (*PendingChannelsResponse_WaitingCloseChannel) ProtoMessage() {} func (x *PendingChannelsResponse_WaitingCloseChannel) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[208] + mi := &file_lightning_proto_msgTypes[209] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -17950,7 +18057,7 @@ func (x *PendingChannelsResponse_WaitingCloseChannel) ProtoReflect() protoreflec // Deprecated: Use PendingChannelsResponse_WaitingCloseChannel.ProtoReflect.Descriptor instead. func (*PendingChannelsResponse_WaitingCloseChannel) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{89, 2} + return file_lightning_proto_rawDescGZIP(), []int{90, 2} } func (x *PendingChannelsResponse_WaitingCloseChannel) GetChannel() *PendingChannelsResponse_PendingChannel { @@ -18013,7 +18120,7 @@ type PendingChannelsResponse_Commitments struct { func (x *PendingChannelsResponse_Commitments) Reset() { *x = PendingChannelsResponse_Commitments{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[209] + mi := &file_lightning_proto_msgTypes[210] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -18026,7 +18133,7 @@ func (x *PendingChannelsResponse_Commitments) String() string { func (*PendingChannelsResponse_Commitments) ProtoMessage() {} func (x *PendingChannelsResponse_Commitments) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[209] + mi := &file_lightning_proto_msgTypes[210] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -18039,7 +18146,7 @@ func (x *PendingChannelsResponse_Commitments) ProtoReflect() protoreflect.Messag // Deprecated: Use PendingChannelsResponse_Commitments.ProtoReflect.Descriptor instead. func (*PendingChannelsResponse_Commitments) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{89, 3} + return file_lightning_proto_rawDescGZIP(), []int{90, 3} } func (x *PendingChannelsResponse_Commitments) GetLocalTxid() string { @@ -18098,7 +18205,7 @@ type PendingChannelsResponse_ClosedChannel struct { func (x *PendingChannelsResponse_ClosedChannel) Reset() { *x = PendingChannelsResponse_ClosedChannel{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[210] + mi := &file_lightning_proto_msgTypes[211] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -18111,7 +18218,7 @@ func (x *PendingChannelsResponse_ClosedChannel) String() string { func (*PendingChannelsResponse_ClosedChannel) ProtoMessage() {} func (x *PendingChannelsResponse_ClosedChannel) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[210] + mi := &file_lightning_proto_msgTypes[211] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -18124,7 +18231,7 @@ func (x *PendingChannelsResponse_ClosedChannel) ProtoReflect() protoreflect.Mess // Deprecated: Use PendingChannelsResponse_ClosedChannel.ProtoReflect.Descriptor instead. func (*PendingChannelsResponse_ClosedChannel) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{89, 4} + return file_lightning_proto_rawDescGZIP(), []int{90, 4} } func (x *PendingChannelsResponse_ClosedChannel) GetChannel() *PendingChannelsResponse_PendingChannel { @@ -18167,7 +18274,7 @@ type PendingChannelsResponse_ForceClosedChannel struct { func (x *PendingChannelsResponse_ForceClosedChannel) Reset() { *x = PendingChannelsResponse_ForceClosedChannel{} if protoimpl.UnsafeEnabled { - mi := &file_lightning_proto_msgTypes[211] + mi := &file_lightning_proto_msgTypes[212] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -18180,7 +18287,7 @@ func (x *PendingChannelsResponse_ForceClosedChannel) String() string { func (*PendingChannelsResponse_ForceClosedChannel) ProtoMessage() {} func (x *PendingChannelsResponse_ForceClosedChannel) ProtoReflect() protoreflect.Message { - mi := &file_lightning_proto_msgTypes[211] + mi := &file_lightning_proto_msgTypes[212] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -18193,7 +18300,7 @@ func (x *PendingChannelsResponse_ForceClosedChannel) ProtoReflect() protoreflect // Deprecated: Use PendingChannelsResponse_ForceClosedChannel.ProtoReflect.Descriptor instead. func (*PendingChannelsResponse_ForceClosedChannel) Descriptor() ([]byte, []int) { - return file_lightning_proto_rawDescGZIP(), []int{89, 5} + return file_lightning_proto_rawDescGZIP(), []int{90, 5} } func (x *PendingChannelsResponse_ForceClosedChannel) GetChannel() *PendingChannelsResponse_PendingChannel { @@ -19055,2316 +19162,2338 @@ var file_lightning_proto_rawDesc = []byte{ 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x5f, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x0c, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, - 0x6c, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x22, 0x51, 0x0a, 0x12, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, - 0x6c, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x21, 0x0a, 0x0c, - 0x63, 0x6c, 0x6f, 0x73, 0x69, 0x6e, 0x67, 0x5f, 0x74, 0x78, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x0c, 0x52, 0x0b, 0x63, 0x6c, 0x6f, 0x73, 0x69, 0x6e, 0x67, 0x54, 0x78, 0x69, 0x64, 0x12, - 0x18, 0x0a, 0x07, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, - 0x52, 0x07, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x22, 0xbf, 0x02, 0x0a, 0x13, 0x43, 0x6c, - 0x6f, 0x73, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x12, 0x38, 0x0a, 0x0d, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x5f, 0x70, 0x6f, 0x69, - 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, - 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x0c, 0x63, - 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x66, - 0x6f, 0x72, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x66, 0x6f, 0x72, 0x63, - 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, 0x63, 0x6f, 0x6e, 0x66, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x43, 0x6f, - 0x6e, 0x66, 0x12, 0x24, 0x0a, 0x0c, 0x73, 0x61, 0x74, 0x5f, 0x70, 0x65, 0x72, 0x5f, 0x62, 0x79, - 0x74, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x42, 0x02, 0x18, 0x01, 0x52, 0x0a, 0x73, 0x61, - 0x74, 0x50, 0x65, 0x72, 0x42, 0x79, 0x74, 0x65, 0x12, 0x29, 0x0a, 0x10, 0x64, 0x65, 0x6c, 0x69, - 0x76, 0x65, 0x72, 0x79, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x05, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x0f, 0x64, 0x65, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x79, 0x41, 0x64, 0x64, 0x72, - 0x65, 0x73, 0x73, 0x12, 0x22, 0x0a, 0x0d, 0x73, 0x61, 0x74, 0x5f, 0x70, 0x65, 0x72, 0x5f, 0x76, - 0x62, 0x79, 0x74, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x73, 0x61, 0x74, 0x50, - 0x65, 0x72, 0x56, 0x62, 0x79, 0x74, 0x65, 0x12, 0x29, 0x0a, 0x11, 0x6d, 0x61, 0x78, 0x5f, 0x66, - 0x65, 0x65, 0x5f, 0x70, 0x65, 0x72, 0x5f, 0x76, 0x62, 0x79, 0x74, 0x65, 0x18, 0x07, 0x20, 0x01, - 0x28, 0x04, 0x52, 0x0e, 0x6d, 0x61, 0x78, 0x46, 0x65, 0x65, 0x50, 0x65, 0x72, 0x56, 0x62, 0x79, - 0x74, 0x65, 0x12, 0x17, 0x0a, 0x07, 0x6e, 0x6f, 0x5f, 0x77, 0x61, 0x69, 0x74, 0x18, 0x08, 0x20, - 0x01, 0x28, 0x08, 0x52, 0x06, 0x6e, 0x6f, 0x57, 0x61, 0x69, 0x74, 0x22, 0xd3, 0x01, 0x0a, 0x11, - 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x55, 0x70, 0x64, 0x61, 0x74, - 0x65, 0x12, 0x3b, 0x0a, 0x0d, 0x63, 0x6c, 0x6f, 0x73, 0x65, 0x5f, 0x70, 0x65, 0x6e, 0x64, 0x69, - 0x6e, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, - 0x2e, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x48, 0x00, - 0x52, 0x0c, 0x63, 0x6c, 0x6f, 0x73, 0x65, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x12, 0x3a, - 0x0a, 0x0a, 0x63, 0x68, 0x61, 0x6e, 0x5f, 0x63, 0x6c, 0x6f, 0x73, 0x65, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, - 0x65, 0x6c, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x48, 0x00, 0x52, - 0x09, 0x63, 0x68, 0x61, 0x6e, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x12, 0x3b, 0x0a, 0x0d, 0x63, 0x6c, - 0x6f, 0x73, 0x65, 0x5f, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x14, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, - 0x74, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x48, 0x00, 0x52, 0x0c, 0x63, 0x6c, 0x6f, 0x73, 0x65, - 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x42, 0x08, 0x0a, 0x06, 0x75, 0x70, 0x64, 0x61, 0x74, - 0x65, 0x22, 0x46, 0x0a, 0x0d, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x55, 0x70, 0x64, 0x61, - 0x74, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x78, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, - 0x52, 0x04, 0x74, 0x78, 0x69, 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, - 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x6f, 0x75, - 0x74, 0x70, 0x75, 0x74, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x22, 0x0f, 0x0a, 0x0d, 0x49, 0x6e, 0x73, - 0x74, 0x61, 0x6e, 0x74, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x22, 0x79, 0x0a, 0x13, 0x52, 0x65, - 0x61, 0x64, 0x79, 0x46, 0x6f, 0x72, 0x50, 0x73, 0x62, 0x74, 0x46, 0x75, 0x6e, 0x64, 0x69, 0x6e, - 0x67, 0x12, 0x27, 0x0a, 0x0f, 0x66, 0x75, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x61, 0x64, 0x64, - 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x66, 0x75, 0x6e, 0x64, - 0x69, 0x6e, 0x67, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x25, 0x0a, 0x0e, 0x66, 0x75, - 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x03, 0x52, 0x0d, 0x66, 0x75, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x41, 0x6d, 0x6f, 0x75, 0x6e, - 0x74, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x73, 0x62, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, - 0x04, 0x70, 0x73, 0x62, 0x74, 0x22, 0xc9, 0x02, 0x0a, 0x17, 0x42, 0x61, 0x74, 0x63, 0x68, 0x4f, - 0x70, 0x65, 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x12, 0x33, 0x0a, 0x08, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x18, 0x01, 0x20, - 0x03, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x42, 0x61, 0x74, 0x63, - 0x68, 0x4f, 0x70, 0x65, 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x08, 0x63, 0x68, - 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, - 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x74, 0x61, 0x72, - 0x67, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x12, 0x22, 0x0a, 0x0d, 0x73, 0x61, 0x74, 0x5f, 0x70, - 0x65, 0x72, 0x5f, 0x76, 0x62, 0x79, 0x74, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b, - 0x73, 0x61, 0x74, 0x50, 0x65, 0x72, 0x56, 0x62, 0x79, 0x74, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x6d, - 0x69, 0x6e, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, - 0x6d, 0x69, 0x6e, 0x43, 0x6f, 0x6e, 0x66, 0x73, 0x12, 0x2b, 0x0a, 0x11, 0x73, 0x70, 0x65, 0x6e, - 0x64, 0x5f, 0x75, 0x6e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x65, 0x64, 0x18, 0x05, 0x20, - 0x01, 0x28, 0x08, 0x52, 0x10, 0x73, 0x70, 0x65, 0x6e, 0x64, 0x55, 0x6e, 0x63, 0x6f, 0x6e, 0x66, - 0x69, 0x72, 0x6d, 0x65, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x18, 0x06, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x12, 0x54, 0x0a, 0x17, 0x63, - 0x6f, 0x69, 0x6e, 0x5f, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x73, 0x74, - 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1c, 0x2e, 0x6c, - 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x6f, 0x69, 0x6e, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x69, - 0x6f, 0x6e, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x52, 0x15, 0x63, 0x6f, 0x69, 0x6e, - 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, - 0x79, 0x22, 0x89, 0x06, 0x0a, 0x10, 0x42, 0x61, 0x74, 0x63, 0x68, 0x4f, 0x70, 0x65, 0x6e, 0x43, - 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x12, 0x1f, 0x0a, 0x0b, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x70, - 0x75, 0x62, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x6e, 0x6f, 0x64, - 0x65, 0x50, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x12, 0x30, 0x0a, 0x14, 0x6c, 0x6f, 0x63, 0x61, 0x6c, - 0x5f, 0x66, 0x75, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x12, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x46, 0x75, 0x6e, 0x64, - 0x69, 0x6e, 0x67, 0x41, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x70, 0x75, 0x73, - 0x68, 0x5f, 0x73, 0x61, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x70, 0x75, 0x73, - 0x68, 0x53, 0x61, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x18, - 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x12, 0x22, - 0x0a, 0x0d, 0x6d, 0x69, 0x6e, 0x5f, 0x68, 0x74, 0x6c, 0x63, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, - 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b, 0x6d, 0x69, 0x6e, 0x48, 0x74, 0x6c, 0x63, 0x4d, 0x73, - 0x61, 0x74, 0x12, 0x28, 0x0a, 0x10, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x5f, 0x63, 0x73, 0x76, - 0x5f, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0e, 0x72, 0x65, - 0x6d, 0x6f, 0x74, 0x65, 0x43, 0x73, 0x76, 0x44, 0x65, 0x6c, 0x61, 0x79, 0x12, 0x23, 0x0a, 0x0d, - 0x63, 0x6c, 0x6f, 0x73, 0x65, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x07, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x0c, 0x63, 0x6c, 0x6f, 0x73, 0x65, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, - 0x73, 0x12, 0x26, 0x0a, 0x0f, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x63, 0x68, 0x61, - 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0d, 0x70, 0x65, 0x6e, 0x64, - 0x69, 0x6e, 0x67, 0x43, 0x68, 0x61, 0x6e, 0x49, 0x64, 0x12, 0x3e, 0x0a, 0x0f, 0x63, 0x6f, 0x6d, - 0x6d, 0x69, 0x74, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x09, 0x20, 0x01, - 0x28, 0x0e, 0x32, 0x15, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x69, - 0x74, 0x6d, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x52, 0x0e, 0x63, 0x6f, 0x6d, 0x6d, 0x69, - 0x74, 0x6d, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x43, 0x0a, 0x1f, 0x72, 0x65, 0x6d, - 0x6f, 0x74, 0x65, 0x5f, 0x6d, 0x61, 0x78, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x5f, 0x69, 0x6e, - 0x5f, 0x66, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x0a, 0x20, 0x01, - 0x28, 0x04, 0x52, 0x1a, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x4d, 0x61, 0x78, 0x56, 0x61, 0x6c, - 0x75, 0x65, 0x49, 0x6e, 0x46, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x4d, 0x73, 0x61, 0x74, 0x12, 0x28, - 0x0a, 0x10, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x5f, 0x6d, 0x61, 0x78, 0x5f, 0x68, 0x74, 0x6c, - 0x63, 0x73, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0e, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, - 0x4d, 0x61, 0x78, 0x48, 0x74, 0x6c, 0x63, 0x73, 0x12, 0x22, 0x0a, 0x0d, 0x6d, 0x61, 0x78, 0x5f, - 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x5f, 0x63, 0x73, 0x76, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0d, 0x52, - 0x0b, 0x6d, 0x61, 0x78, 0x4c, 0x6f, 0x63, 0x61, 0x6c, 0x43, 0x73, 0x76, 0x12, 0x1b, 0x0a, 0x09, - 0x7a, 0x65, 0x72, 0x6f, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x08, 0x52, - 0x08, 0x7a, 0x65, 0x72, 0x6f, 0x43, 0x6f, 0x6e, 0x66, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x63, 0x69, - 0x64, 0x5f, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x73, - 0x63, 0x69, 0x64, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x12, 0x19, 0x0a, 0x08, 0x62, 0x61, 0x73, 0x65, - 0x5f, 0x66, 0x65, 0x65, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, 0x62, 0x61, 0x73, 0x65, - 0x46, 0x65, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x66, 0x65, 0x65, 0x5f, 0x72, 0x61, 0x74, 0x65, 0x18, - 0x10, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, 0x66, 0x65, 0x65, 0x52, 0x61, 0x74, 0x65, 0x12, 0x20, - 0x0a, 0x0c, 0x75, 0x73, 0x65, 0x5f, 0x62, 0x61, 0x73, 0x65, 0x5f, 0x66, 0x65, 0x65, 0x18, 0x11, - 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x75, 0x73, 0x65, 0x42, 0x61, 0x73, 0x65, 0x46, 0x65, 0x65, - 0x12, 0x20, 0x0a, 0x0c, 0x75, 0x73, 0x65, 0x5f, 0x66, 0x65, 0x65, 0x5f, 0x72, 0x61, 0x74, 0x65, - 0x18, 0x12, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x75, 0x73, 0x65, 0x46, 0x65, 0x65, 0x52, 0x61, - 0x74, 0x65, 0x12, 0x35, 0x0a, 0x17, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x5f, 0x63, 0x68, 0x61, - 0x6e, 0x5f, 0x72, 0x65, 0x73, 0x65, 0x72, 0x76, 0x65, 0x5f, 0x73, 0x61, 0x74, 0x18, 0x13, 0x20, - 0x01, 0x28, 0x04, 0x52, 0x14, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x52, - 0x65, 0x73, 0x65, 0x72, 0x76, 0x65, 0x53, 0x61, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6d, 0x65, 0x6d, - 0x6f, 0x18, 0x14, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6d, 0x65, 0x6d, 0x6f, 0x22, 0x5b, 0x0a, - 0x18, 0x42, 0x61, 0x74, 0x63, 0x68, 0x4f, 0x70, 0x65, 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, - 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3f, 0x0a, 0x10, 0x70, 0x65, 0x6e, - 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x18, 0x01, 0x20, - 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x65, 0x6e, 0x64, - 0x69, 0x6e, 0x67, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x0f, 0x70, 0x65, 0x6e, 0x64, 0x69, - 0x6e, 0x67, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x22, 0xcb, 0x08, 0x0a, 0x12, 0x4f, - 0x70, 0x65, 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x12, 0x22, 0x0a, 0x0d, 0x73, 0x61, 0x74, 0x5f, 0x70, 0x65, 0x72, 0x5f, 0x76, 0x62, 0x79, - 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x73, 0x61, 0x74, 0x50, 0x65, 0x72, - 0x56, 0x62, 0x79, 0x74, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x70, 0x75, - 0x62, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x6e, 0x6f, 0x64, 0x65, - 0x50, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x12, 0x30, 0x0a, 0x12, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x70, - 0x75, 0x62, 0x6b, 0x65, 0x79, 0x5f, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x09, 0x42, 0x02, 0x18, 0x01, 0x52, 0x10, 0x6e, 0x6f, 0x64, 0x65, 0x50, 0x75, 0x62, 0x6b, - 0x65, 0x79, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x12, 0x30, 0x0a, 0x14, 0x6c, 0x6f, 0x63, 0x61, + 0x6c, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x22, 0x94, 0x01, 0x0a, 0x0b, 0x43, 0x6c, 0x6f, 0x73, 0x65, + 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, + 0x5f, 0x73, 0x61, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x61, 0x6d, 0x6f, 0x75, + 0x6e, 0x74, 0x53, 0x61, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x70, 0x6b, 0x5f, 0x73, 0x63, 0x72, 0x69, + 0x70, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x70, 0x6b, 0x53, 0x63, 0x72, 0x69, + 0x70, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x69, 0x73, 0x5f, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x69, 0x73, 0x4c, 0x6f, 0x63, 0x61, 0x6c, 0x12, 0x2e, 0x0a, + 0x13, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x5f, + 0x64, 0x61, 0x74, 0x61, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x11, 0x63, 0x75, 0x73, 0x74, + 0x6f, 0x6d, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x44, 0x61, 0x74, 0x61, 0x22, 0x9a, 0x02, + 0x0a, 0x12, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x55, 0x70, + 0x64, 0x61, 0x74, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x63, 0x6c, 0x6f, 0x73, 0x69, 0x6e, 0x67, 0x5f, + 0x74, 0x78, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x63, 0x6c, 0x6f, 0x73, + 0x69, 0x6e, 0x67, 0x54, 0x78, 0x69, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x75, 0x63, 0x63, 0x65, + 0x73, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, + 0x73, 0x12, 0x40, 0x0a, 0x12, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x5f, 0x63, 0x6c, 0x6f, 0x73, 0x65, + 0x5f, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, + 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x4f, 0x75, 0x74, 0x70, 0x75, + 0x74, 0x52, 0x10, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x4f, 0x75, 0x74, + 0x70, 0x75, 0x74, 0x12, 0x42, 0x0a, 0x13, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x5f, 0x63, 0x6c, + 0x6f, 0x73, 0x65, 0x5f, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x12, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x4f, 0x75, + 0x74, 0x70, 0x75, 0x74, 0x52, 0x11, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x43, 0x6c, 0x6f, 0x73, + 0x65, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x12, 0x41, 0x0a, 0x12, 0x61, 0x64, 0x64, 0x69, 0x74, + 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x5f, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x73, 0x18, 0x05, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x6c, 0x6f, 0x73, + 0x65, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x52, 0x11, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, + 0x6e, 0x61, 0x6c, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x73, 0x22, 0xbf, 0x02, 0x0a, 0x13, 0x43, + 0x6c, 0x6f, 0x73, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x12, 0x38, 0x0a, 0x0d, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x5f, 0x70, 0x6f, + 0x69, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x6c, 0x6e, 0x72, 0x70, + 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x0c, + 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x14, 0x0a, 0x05, + 0x66, 0x6f, 0x72, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x66, 0x6f, 0x72, + 0x63, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, 0x63, 0x6f, 0x6e, + 0x66, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x43, + 0x6f, 0x6e, 0x66, 0x12, 0x24, 0x0a, 0x0c, 0x73, 0x61, 0x74, 0x5f, 0x70, 0x65, 0x72, 0x5f, 0x62, + 0x79, 0x74, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x42, 0x02, 0x18, 0x01, 0x52, 0x0a, 0x73, + 0x61, 0x74, 0x50, 0x65, 0x72, 0x42, 0x79, 0x74, 0x65, 0x12, 0x29, 0x0a, 0x10, 0x64, 0x65, 0x6c, + 0x69, 0x76, 0x65, 0x72, 0x79, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x05, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0f, 0x64, 0x65, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x79, 0x41, 0x64, 0x64, + 0x72, 0x65, 0x73, 0x73, 0x12, 0x22, 0x0a, 0x0d, 0x73, 0x61, 0x74, 0x5f, 0x70, 0x65, 0x72, 0x5f, + 0x76, 0x62, 0x79, 0x74, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x73, 0x61, 0x74, + 0x50, 0x65, 0x72, 0x56, 0x62, 0x79, 0x74, 0x65, 0x12, 0x29, 0x0a, 0x11, 0x6d, 0x61, 0x78, 0x5f, + 0x66, 0x65, 0x65, 0x5f, 0x70, 0x65, 0x72, 0x5f, 0x76, 0x62, 0x79, 0x74, 0x65, 0x18, 0x07, 0x20, + 0x01, 0x28, 0x04, 0x52, 0x0e, 0x6d, 0x61, 0x78, 0x46, 0x65, 0x65, 0x50, 0x65, 0x72, 0x56, 0x62, + 0x79, 0x74, 0x65, 0x12, 0x17, 0x0a, 0x07, 0x6e, 0x6f, 0x5f, 0x77, 0x61, 0x69, 0x74, 0x18, 0x08, + 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x6e, 0x6f, 0x57, 0x61, 0x69, 0x74, 0x22, 0xd3, 0x01, 0x0a, + 0x11, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x55, 0x70, 0x64, 0x61, + 0x74, 0x65, 0x12, 0x3b, 0x0a, 0x0d, 0x63, 0x6c, 0x6f, 0x73, 0x65, 0x5f, 0x70, 0x65, 0x6e, 0x64, + 0x69, 0x6e, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x6c, 0x6e, 0x72, 0x70, + 0x63, 0x2e, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x48, + 0x00, 0x52, 0x0c, 0x63, 0x6c, 0x6f, 0x73, 0x65, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x12, + 0x3a, 0x0a, 0x0a, 0x63, 0x68, 0x61, 0x6e, 0x5f, 0x63, 0x6c, 0x6f, 0x73, 0x65, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, + 0x6e, 0x65, 0x6c, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x48, 0x00, + 0x52, 0x09, 0x63, 0x68, 0x61, 0x6e, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x12, 0x3b, 0x0a, 0x0d, 0x63, + 0x6c, 0x6f, 0x73, 0x65, 0x5f, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x49, 0x6e, 0x73, 0x74, 0x61, + 0x6e, 0x74, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x48, 0x00, 0x52, 0x0c, 0x63, 0x6c, 0x6f, 0x73, + 0x65, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x42, 0x08, 0x0a, 0x06, 0x75, 0x70, 0x64, 0x61, + 0x74, 0x65, 0x22, 0x46, 0x0a, 0x0d, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x55, 0x70, 0x64, + 0x61, 0x74, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x78, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0c, 0x52, 0x04, 0x74, 0x78, 0x69, 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x6f, 0x75, 0x74, 0x70, 0x75, + 0x74, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x6f, + 0x75, 0x74, 0x70, 0x75, 0x74, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x22, 0x0f, 0x0a, 0x0d, 0x49, 0x6e, + 0x73, 0x74, 0x61, 0x6e, 0x74, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x22, 0x79, 0x0a, 0x13, 0x52, + 0x65, 0x61, 0x64, 0x79, 0x46, 0x6f, 0x72, 0x50, 0x73, 0x62, 0x74, 0x46, 0x75, 0x6e, 0x64, 0x69, + 0x6e, 0x67, 0x12, 0x27, 0x0a, 0x0f, 0x66, 0x75, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x61, 0x64, + 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x66, 0x75, 0x6e, + 0x64, 0x69, 0x6e, 0x67, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x25, 0x0a, 0x0e, 0x66, + 0x75, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x03, 0x52, 0x0d, 0x66, 0x75, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x41, 0x6d, 0x6f, 0x75, + 0x6e, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x73, 0x62, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, + 0x52, 0x04, 0x70, 0x73, 0x62, 0x74, 0x22, 0xc9, 0x02, 0x0a, 0x17, 0x42, 0x61, 0x74, 0x63, 0x68, + 0x4f, 0x70, 0x65, 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x12, 0x33, 0x0a, 0x08, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x18, 0x01, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x42, 0x61, 0x74, + 0x63, 0x68, 0x4f, 0x70, 0x65, 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x08, 0x63, + 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x74, 0x61, 0x72, 0x67, 0x65, + 0x74, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x74, 0x61, + 0x72, 0x67, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x12, 0x22, 0x0a, 0x0d, 0x73, 0x61, 0x74, 0x5f, + 0x70, 0x65, 0x72, 0x5f, 0x76, 0x62, 0x79, 0x74, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, + 0x0b, 0x73, 0x61, 0x74, 0x50, 0x65, 0x72, 0x56, 0x62, 0x79, 0x74, 0x65, 0x12, 0x1b, 0x0a, 0x09, + 0x6d, 0x69, 0x6e, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, + 0x08, 0x6d, 0x69, 0x6e, 0x43, 0x6f, 0x6e, 0x66, 0x73, 0x12, 0x2b, 0x0a, 0x11, 0x73, 0x70, 0x65, + 0x6e, 0x64, 0x5f, 0x75, 0x6e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x65, 0x64, 0x18, 0x05, + 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x73, 0x70, 0x65, 0x6e, 0x64, 0x55, 0x6e, 0x63, 0x6f, 0x6e, + 0x66, 0x69, 0x72, 0x6d, 0x65, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x18, + 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x12, 0x54, 0x0a, 0x17, + 0x63, 0x6f, 0x69, 0x6e, 0x5f, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x73, + 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1c, 0x2e, + 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x6f, 0x69, 0x6e, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x52, 0x15, 0x63, 0x6f, 0x69, + 0x6e, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, + 0x67, 0x79, 0x22, 0x89, 0x06, 0x0a, 0x10, 0x42, 0x61, 0x74, 0x63, 0x68, 0x4f, 0x70, 0x65, 0x6e, + 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x12, 0x1f, 0x0a, 0x0b, 0x6e, 0x6f, 0x64, 0x65, 0x5f, + 0x70, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x6e, 0x6f, + 0x64, 0x65, 0x50, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x12, 0x30, 0x0a, 0x14, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x5f, 0x66, 0x75, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, - 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x12, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x46, 0x75, 0x6e, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x12, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x46, 0x75, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x41, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x70, 0x75, - 0x73, 0x68, 0x5f, 0x73, 0x61, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x70, 0x75, - 0x73, 0x68, 0x53, 0x61, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, - 0x63, 0x6f, 0x6e, 0x66, 0x18, 0x06, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x74, 0x61, 0x72, 0x67, - 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x12, 0x24, 0x0a, 0x0c, 0x73, 0x61, 0x74, 0x5f, 0x70, 0x65, - 0x72, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x03, 0x42, 0x02, 0x18, 0x01, - 0x52, 0x0a, 0x73, 0x61, 0x74, 0x50, 0x65, 0x72, 0x42, 0x79, 0x74, 0x65, 0x12, 0x18, 0x0a, 0x07, - 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x70, - 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x12, 0x22, 0x0a, 0x0d, 0x6d, 0x69, 0x6e, 0x5f, 0x68, 0x74, - 0x6c, 0x63, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x09, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b, 0x6d, - 0x69, 0x6e, 0x48, 0x74, 0x6c, 0x63, 0x4d, 0x73, 0x61, 0x74, 0x12, 0x28, 0x0a, 0x10, 0x72, 0x65, - 0x6d, 0x6f, 0x74, 0x65, 0x5f, 0x63, 0x73, 0x76, 0x5f, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x18, 0x0a, - 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0e, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x43, 0x73, 0x76, 0x44, - 0x65, 0x6c, 0x61, 0x79, 0x12, 0x1b, 0x0a, 0x09, 0x6d, 0x69, 0x6e, 0x5f, 0x63, 0x6f, 0x6e, 0x66, - 0x73, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x6d, 0x69, 0x6e, 0x43, 0x6f, 0x6e, 0x66, - 0x73, 0x12, 0x2b, 0x0a, 0x11, 0x73, 0x70, 0x65, 0x6e, 0x64, 0x5f, 0x75, 0x6e, 0x63, 0x6f, 0x6e, - 0x66, 0x69, 0x72, 0x6d, 0x65, 0x64, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x73, 0x70, - 0x65, 0x6e, 0x64, 0x55, 0x6e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x65, 0x64, 0x12, 0x23, - 0x0a, 0x0d, 0x63, 0x6c, 0x6f, 0x73, 0x65, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, - 0x0d, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x63, 0x6c, 0x6f, 0x73, 0x65, 0x41, 0x64, 0x64, 0x72, - 0x65, 0x73, 0x73, 0x12, 0x35, 0x0a, 0x0c, 0x66, 0x75, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x73, - 0x68, 0x69, 0x6d, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x6c, 0x6e, 0x72, 0x70, - 0x63, 0x2e, 0x46, 0x75, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x53, 0x68, 0x69, 0x6d, 0x52, 0x0b, 0x66, - 0x75, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x53, 0x68, 0x69, 0x6d, 0x12, 0x43, 0x0a, 0x1f, 0x72, 0x65, + 0x73, 0x68, 0x5f, 0x73, 0x61, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x70, 0x75, + 0x73, 0x68, 0x53, 0x61, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x12, + 0x22, 0x0a, 0x0d, 0x6d, 0x69, 0x6e, 0x5f, 0x68, 0x74, 0x6c, 0x63, 0x5f, 0x6d, 0x73, 0x61, 0x74, + 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b, 0x6d, 0x69, 0x6e, 0x48, 0x74, 0x6c, 0x63, 0x4d, + 0x73, 0x61, 0x74, 0x12, 0x28, 0x0a, 0x10, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x5f, 0x63, 0x73, + 0x76, 0x5f, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0e, 0x72, + 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x43, 0x73, 0x76, 0x44, 0x65, 0x6c, 0x61, 0x79, 0x12, 0x23, 0x0a, + 0x0d, 0x63, 0x6c, 0x6f, 0x73, 0x65, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x07, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x63, 0x6c, 0x6f, 0x73, 0x65, 0x41, 0x64, 0x64, 0x72, 0x65, + 0x73, 0x73, 0x12, 0x26, 0x0a, 0x0f, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x63, 0x68, + 0x61, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0d, 0x70, 0x65, 0x6e, + 0x64, 0x69, 0x6e, 0x67, 0x43, 0x68, 0x61, 0x6e, 0x49, 0x64, 0x12, 0x3e, 0x0a, 0x0f, 0x63, 0x6f, + 0x6d, 0x6d, 0x69, 0x74, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x09, 0x20, + 0x01, 0x28, 0x0e, 0x32, 0x15, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, + 0x69, 0x74, 0x6d, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x52, 0x0e, 0x63, 0x6f, 0x6d, 0x6d, + 0x69, 0x74, 0x6d, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x43, 0x0a, 0x1f, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x5f, 0x6d, 0x61, 0x78, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x5f, 0x69, - 0x6e, 0x5f, 0x66, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x0f, 0x20, + 0x6e, 0x5f, 0x66, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x04, 0x52, 0x1a, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x4d, 0x61, 0x78, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x49, 0x6e, 0x46, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x4d, 0x73, 0x61, 0x74, 0x12, 0x28, 0x0a, 0x10, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x5f, 0x6d, 0x61, 0x78, 0x5f, 0x68, 0x74, - 0x6c, 0x63, 0x73, 0x18, 0x10, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0e, 0x72, 0x65, 0x6d, 0x6f, 0x74, + 0x6c, 0x63, 0x73, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0e, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x4d, 0x61, 0x78, 0x48, 0x74, 0x6c, 0x63, 0x73, 0x12, 0x22, 0x0a, 0x0d, 0x6d, 0x61, 0x78, - 0x5f, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x5f, 0x63, 0x73, 0x76, 0x18, 0x11, 0x20, 0x01, 0x28, 0x0d, - 0x52, 0x0b, 0x6d, 0x61, 0x78, 0x4c, 0x6f, 0x63, 0x61, 0x6c, 0x43, 0x73, 0x76, 0x12, 0x3e, 0x0a, - 0x0f, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, - 0x18, 0x12, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x15, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, - 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x6d, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x52, 0x0e, 0x63, - 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x6d, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1b, 0x0a, - 0x09, 0x7a, 0x65, 0x72, 0x6f, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x18, 0x13, 0x20, 0x01, 0x28, 0x08, + 0x5f, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x5f, 0x63, 0x73, 0x76, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0d, + 0x52, 0x0b, 0x6d, 0x61, 0x78, 0x4c, 0x6f, 0x63, 0x61, 0x6c, 0x43, 0x73, 0x76, 0x12, 0x1b, 0x0a, + 0x09, 0x7a, 0x65, 0x72, 0x6f, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x7a, 0x65, 0x72, 0x6f, 0x43, 0x6f, 0x6e, 0x66, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x63, - 0x69, 0x64, 0x5f, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x18, 0x14, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, + 0x69, 0x64, 0x5f, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x73, 0x63, 0x69, 0x64, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x12, 0x19, 0x0a, 0x08, 0x62, 0x61, 0x73, - 0x65, 0x5f, 0x66, 0x65, 0x65, 0x18, 0x15, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, 0x62, 0x61, 0x73, + 0x65, 0x5f, 0x66, 0x65, 0x65, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, 0x62, 0x61, 0x73, 0x65, 0x46, 0x65, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x66, 0x65, 0x65, 0x5f, 0x72, 0x61, 0x74, 0x65, - 0x18, 0x16, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, 0x66, 0x65, 0x65, 0x52, 0x61, 0x74, 0x65, 0x12, + 0x18, 0x10, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, 0x66, 0x65, 0x65, 0x52, 0x61, 0x74, 0x65, 0x12, 0x20, 0x0a, 0x0c, 0x75, 0x73, 0x65, 0x5f, 0x62, 0x61, 0x73, 0x65, 0x5f, 0x66, 0x65, 0x65, 0x18, - 0x17, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x75, 0x73, 0x65, 0x42, 0x61, 0x73, 0x65, 0x46, 0x65, + 0x11, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x75, 0x73, 0x65, 0x42, 0x61, 0x73, 0x65, 0x46, 0x65, 0x65, 0x12, 0x20, 0x0a, 0x0c, 0x75, 0x73, 0x65, 0x5f, 0x66, 0x65, 0x65, 0x5f, 0x72, 0x61, 0x74, - 0x65, 0x18, 0x18, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x75, 0x73, 0x65, 0x46, 0x65, 0x65, 0x52, + 0x65, 0x18, 0x12, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x75, 0x73, 0x65, 0x46, 0x65, 0x65, 0x52, 0x61, 0x74, 0x65, 0x12, 0x35, 0x0a, 0x17, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x5f, 0x63, 0x68, - 0x61, 0x6e, 0x5f, 0x72, 0x65, 0x73, 0x65, 0x72, 0x76, 0x65, 0x5f, 0x73, 0x61, 0x74, 0x18, 0x19, + 0x61, 0x6e, 0x5f, 0x72, 0x65, 0x73, 0x65, 0x72, 0x76, 0x65, 0x5f, 0x73, 0x61, 0x74, 0x18, 0x13, 0x20, 0x01, 0x28, 0x04, 0x52, 0x14, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x43, 0x68, 0x61, 0x6e, - 0x52, 0x65, 0x73, 0x65, 0x72, 0x76, 0x65, 0x53, 0x61, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x66, 0x75, - 0x6e, 0x64, 0x5f, 0x6d, 0x61, 0x78, 0x18, 0x1a, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x66, 0x75, - 0x6e, 0x64, 0x4d, 0x61, 0x78, 0x12, 0x12, 0x0a, 0x04, 0x6d, 0x65, 0x6d, 0x6f, 0x18, 0x1b, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x04, 0x6d, 0x65, 0x6d, 0x6f, 0x12, 0x2d, 0x0a, 0x09, 0x6f, 0x75, 0x74, - 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x18, 0x1c, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x6c, - 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4f, 0x75, 0x74, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x09, 0x6f, - 0x75, 0x74, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x22, 0xf3, 0x01, 0x0a, 0x10, 0x4f, 0x70, 0x65, - 0x6e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x39, 0x0a, - 0x0c, 0x63, 0x68, 0x61, 0x6e, 0x5f, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x65, 0x6e, 0x64, - 0x69, 0x6e, 0x67, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x48, 0x00, 0x52, 0x0b, 0x63, 0x68, 0x61, - 0x6e, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x12, 0x37, 0x0a, 0x09, 0x63, 0x68, 0x61, 0x6e, - 0x5f, 0x6f, 0x70, 0x65, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x6c, 0x6e, - 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4f, 0x70, 0x65, 0x6e, 0x55, - 0x70, 0x64, 0x61, 0x74, 0x65, 0x48, 0x00, 0x52, 0x08, 0x63, 0x68, 0x61, 0x6e, 0x4f, 0x70, 0x65, - 0x6e, 0x12, 0x39, 0x0a, 0x09, 0x70, 0x73, 0x62, 0x74, 0x5f, 0x66, 0x75, 0x6e, 0x64, 0x18, 0x05, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x65, 0x61, - 0x64, 0x79, 0x46, 0x6f, 0x72, 0x50, 0x73, 0x62, 0x74, 0x46, 0x75, 0x6e, 0x64, 0x69, 0x6e, 0x67, - 0x48, 0x00, 0x52, 0x08, 0x70, 0x73, 0x62, 0x74, 0x46, 0x75, 0x6e, 0x64, 0x12, 0x26, 0x0a, 0x0f, - 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x5f, 0x69, 0x64, 0x18, - 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0d, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x43, 0x68, - 0x61, 0x6e, 0x49, 0x64, 0x42, 0x08, 0x0a, 0x06, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x22, 0x48, - 0x0a, 0x0a, 0x4b, 0x65, 0x79, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x6f, 0x72, 0x12, 0x1d, 0x0a, 0x0a, - 0x6b, 0x65, 0x79, 0x5f, 0x66, 0x61, 0x6d, 0x69, 0x6c, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, - 0x52, 0x09, 0x6b, 0x65, 0x79, 0x46, 0x61, 0x6d, 0x69, 0x6c, 0x79, 0x12, 0x1b, 0x0a, 0x09, 0x6b, - 0x65, 0x79, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, - 0x6b, 0x65, 0x79, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x22, 0x5f, 0x0a, 0x0d, 0x4b, 0x65, 0x79, 0x44, - 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x12, 0x22, 0x0a, 0x0d, 0x72, 0x61, 0x77, - 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, - 0x52, 0x0b, 0x72, 0x61, 0x77, 0x4b, 0x65, 0x79, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x2a, 0x0a, - 0x07, 0x6b, 0x65, 0x79, 0x5f, 0x6c, 0x6f, 0x63, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, - 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4b, 0x65, 0x79, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x6f, - 0x72, 0x52, 0x06, 0x6b, 0x65, 0x79, 0x4c, 0x6f, 0x63, 0x22, 0x88, 0x02, 0x0a, 0x0d, 0x43, 0x68, - 0x61, 0x6e, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x53, 0x68, 0x69, 0x6d, 0x12, 0x10, 0x0a, 0x03, 0x61, - 0x6d, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x03, 0x61, 0x6d, 0x74, 0x12, 0x32, 0x0a, - 0x0a, 0x63, 0x68, 0x61, 0x6e, 0x5f, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x13, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, - 0x6c, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x09, 0x63, 0x68, 0x61, 0x6e, 0x50, 0x6f, 0x69, 0x6e, - 0x74, 0x12, 0x31, 0x0a, 0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4b, 0x65, 0x79, - 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x52, 0x08, 0x6c, 0x6f, 0x63, 0x61, - 0x6c, 0x4b, 0x65, 0x79, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x5f, 0x6b, - 0x65, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, - 0x4b, 0x65, 0x79, 0x12, 0x26, 0x0a, 0x0f, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x63, - 0x68, 0x61, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0d, 0x70, 0x65, - 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x43, 0x68, 0x61, 0x6e, 0x49, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x74, - 0x68, 0x61, 0x77, 0x5f, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, - 0x52, 0x0a, 0x74, 0x68, 0x61, 0x77, 0x48, 0x65, 0x69, 0x67, 0x68, 0x74, 0x12, 0x16, 0x0a, 0x06, - 0x6d, 0x75, 0x73, 0x69, 0x67, 0x32, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x6d, 0x75, - 0x73, 0x69, 0x67, 0x32, 0x22, 0x6e, 0x0a, 0x08, 0x50, 0x73, 0x62, 0x74, 0x53, 0x68, 0x69, 0x6d, - 0x12, 0x26, 0x0a, 0x0f, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x63, 0x68, 0x61, 0x6e, - 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0d, 0x70, 0x65, 0x6e, 0x64, 0x69, - 0x6e, 0x67, 0x43, 0x68, 0x61, 0x6e, 0x49, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x62, 0x61, 0x73, 0x65, - 0x5f, 0x70, 0x73, 0x62, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x62, 0x61, 0x73, - 0x65, 0x50, 0x73, 0x62, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x6e, 0x6f, 0x5f, 0x70, 0x75, 0x62, 0x6c, - 0x69, 0x73, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x6e, 0x6f, 0x50, 0x75, 0x62, - 0x6c, 0x69, 0x73, 0x68, 0x22, 0x85, 0x01, 0x0a, 0x0b, 0x46, 0x75, 0x6e, 0x64, 0x69, 0x6e, 0x67, - 0x53, 0x68, 0x69, 0x6d, 0x12, 0x3e, 0x0a, 0x0f, 0x63, 0x68, 0x61, 0x6e, 0x5f, 0x70, 0x6f, 0x69, - 0x6e, 0x74, 0x5f, 0x73, 0x68, 0x69, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, - 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x53, - 0x68, 0x69, 0x6d, 0x48, 0x00, 0x52, 0x0d, 0x63, 0x68, 0x61, 0x6e, 0x50, 0x6f, 0x69, 0x6e, 0x74, - 0x53, 0x68, 0x69, 0x6d, 0x12, 0x2e, 0x0a, 0x09, 0x70, 0x73, 0x62, 0x74, 0x5f, 0x73, 0x68, 0x69, - 0x6d, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, - 0x50, 0x73, 0x62, 0x74, 0x53, 0x68, 0x69, 0x6d, 0x48, 0x00, 0x52, 0x08, 0x70, 0x73, 0x62, 0x74, - 0x53, 0x68, 0x69, 0x6d, 0x42, 0x06, 0x0a, 0x04, 0x73, 0x68, 0x69, 0x6d, 0x22, 0x3b, 0x0a, 0x11, - 0x46, 0x75, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x53, 0x68, 0x69, 0x6d, 0x43, 0x61, 0x6e, 0x63, 0x65, - 0x6c, 0x12, 0x26, 0x0a, 0x0f, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x63, 0x68, 0x61, + 0x52, 0x65, 0x73, 0x65, 0x72, 0x76, 0x65, 0x53, 0x61, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6d, 0x65, + 0x6d, 0x6f, 0x18, 0x14, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6d, 0x65, 0x6d, 0x6f, 0x22, 0x5b, + 0x0a, 0x18, 0x42, 0x61, 0x74, 0x63, 0x68, 0x4f, 0x70, 0x65, 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x6e, + 0x65, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3f, 0x0a, 0x10, 0x70, 0x65, + 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x18, 0x01, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x65, 0x6e, + 0x64, 0x69, 0x6e, 0x67, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x0f, 0x70, 0x65, 0x6e, 0x64, + 0x69, 0x6e, 0x67, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x22, 0xcb, 0x08, 0x0a, 0x12, + 0x4f, 0x70, 0x65, 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x12, 0x22, 0x0a, 0x0d, 0x73, 0x61, 0x74, 0x5f, 0x70, 0x65, 0x72, 0x5f, 0x76, 0x62, + 0x79, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x73, 0x61, 0x74, 0x50, 0x65, + 0x72, 0x56, 0x62, 0x79, 0x74, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x70, + 0x75, 0x62, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x6e, 0x6f, 0x64, + 0x65, 0x50, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x12, 0x30, 0x0a, 0x12, 0x6e, 0x6f, 0x64, 0x65, 0x5f, + 0x70, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x5f, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x09, 0x42, 0x02, 0x18, 0x01, 0x52, 0x10, 0x6e, 0x6f, 0x64, 0x65, 0x50, 0x75, 0x62, + 0x6b, 0x65, 0x79, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x12, 0x30, 0x0a, 0x14, 0x6c, 0x6f, 0x63, + 0x61, 0x6c, 0x5f, 0x66, 0x75, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x61, 0x6d, 0x6f, 0x75, 0x6e, + 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x12, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x46, 0x75, + 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x41, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x70, + 0x75, 0x73, 0x68, 0x5f, 0x73, 0x61, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x70, + 0x75, 0x73, 0x68, 0x53, 0x61, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, + 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x18, 0x06, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x74, 0x61, 0x72, + 0x67, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x12, 0x24, 0x0a, 0x0c, 0x73, 0x61, 0x74, 0x5f, 0x70, + 0x65, 0x72, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x03, 0x42, 0x02, 0x18, + 0x01, 0x52, 0x0a, 0x73, 0x61, 0x74, 0x50, 0x65, 0x72, 0x42, 0x79, 0x74, 0x65, 0x12, 0x18, 0x0a, + 0x07, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, + 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x12, 0x22, 0x0a, 0x0d, 0x6d, 0x69, 0x6e, 0x5f, 0x68, + 0x74, 0x6c, 0x63, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x09, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b, + 0x6d, 0x69, 0x6e, 0x48, 0x74, 0x6c, 0x63, 0x4d, 0x73, 0x61, 0x74, 0x12, 0x28, 0x0a, 0x10, 0x72, + 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x5f, 0x63, 0x73, 0x76, 0x5f, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x18, + 0x0a, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0e, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x43, 0x73, 0x76, + 0x44, 0x65, 0x6c, 0x61, 0x79, 0x12, 0x1b, 0x0a, 0x09, 0x6d, 0x69, 0x6e, 0x5f, 0x63, 0x6f, 0x6e, + 0x66, 0x73, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x6d, 0x69, 0x6e, 0x43, 0x6f, 0x6e, + 0x66, 0x73, 0x12, 0x2b, 0x0a, 0x11, 0x73, 0x70, 0x65, 0x6e, 0x64, 0x5f, 0x75, 0x6e, 0x63, 0x6f, + 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x65, 0x64, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x73, + 0x70, 0x65, 0x6e, 0x64, 0x55, 0x6e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x65, 0x64, 0x12, + 0x23, 0x0a, 0x0d, 0x63, 0x6c, 0x6f, 0x73, 0x65, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, + 0x18, 0x0d, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x63, 0x6c, 0x6f, 0x73, 0x65, 0x41, 0x64, 0x64, + 0x72, 0x65, 0x73, 0x73, 0x12, 0x35, 0x0a, 0x0c, 0x66, 0x75, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x5f, + 0x73, 0x68, 0x69, 0x6d, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x6c, 0x6e, 0x72, + 0x70, 0x63, 0x2e, 0x46, 0x75, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x53, 0x68, 0x69, 0x6d, 0x52, 0x0b, + 0x66, 0x75, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x53, 0x68, 0x69, 0x6d, 0x12, 0x43, 0x0a, 0x1f, 0x72, + 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x5f, 0x6d, 0x61, 0x78, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x5f, + 0x69, 0x6e, 0x5f, 0x66, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x0f, + 0x20, 0x01, 0x28, 0x04, 0x52, 0x1a, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x4d, 0x61, 0x78, 0x56, + 0x61, 0x6c, 0x75, 0x65, 0x49, 0x6e, 0x46, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x4d, 0x73, 0x61, 0x74, + 0x12, 0x28, 0x0a, 0x10, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x5f, 0x6d, 0x61, 0x78, 0x5f, 0x68, + 0x74, 0x6c, 0x63, 0x73, 0x18, 0x10, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0e, 0x72, 0x65, 0x6d, 0x6f, + 0x74, 0x65, 0x4d, 0x61, 0x78, 0x48, 0x74, 0x6c, 0x63, 0x73, 0x12, 0x22, 0x0a, 0x0d, 0x6d, 0x61, + 0x78, 0x5f, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x5f, 0x63, 0x73, 0x76, 0x18, 0x11, 0x20, 0x01, 0x28, + 0x0d, 0x52, 0x0b, 0x6d, 0x61, 0x78, 0x4c, 0x6f, 0x63, 0x61, 0x6c, 0x43, 0x73, 0x76, 0x12, 0x3e, + 0x0a, 0x0f, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x74, 0x79, 0x70, + 0x65, 0x18, 0x12, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x15, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, + 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x6d, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x52, 0x0e, + 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x6d, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1b, + 0x0a, 0x09, 0x7a, 0x65, 0x72, 0x6f, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x18, 0x13, 0x20, 0x01, 0x28, + 0x08, 0x52, 0x08, 0x7a, 0x65, 0x72, 0x6f, 0x43, 0x6f, 0x6e, 0x66, 0x12, 0x1d, 0x0a, 0x0a, 0x73, + 0x63, 0x69, 0x64, 0x5f, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x18, 0x14, 0x20, 0x01, 0x28, 0x08, 0x52, + 0x09, 0x73, 0x63, 0x69, 0x64, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x12, 0x19, 0x0a, 0x08, 0x62, 0x61, + 0x73, 0x65, 0x5f, 0x66, 0x65, 0x65, 0x18, 0x15, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, 0x62, 0x61, + 0x73, 0x65, 0x46, 0x65, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x66, 0x65, 0x65, 0x5f, 0x72, 0x61, 0x74, + 0x65, 0x18, 0x16, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, 0x66, 0x65, 0x65, 0x52, 0x61, 0x74, 0x65, + 0x12, 0x20, 0x0a, 0x0c, 0x75, 0x73, 0x65, 0x5f, 0x62, 0x61, 0x73, 0x65, 0x5f, 0x66, 0x65, 0x65, + 0x18, 0x17, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x75, 0x73, 0x65, 0x42, 0x61, 0x73, 0x65, 0x46, + 0x65, 0x65, 0x12, 0x20, 0x0a, 0x0c, 0x75, 0x73, 0x65, 0x5f, 0x66, 0x65, 0x65, 0x5f, 0x72, 0x61, + 0x74, 0x65, 0x18, 0x18, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x75, 0x73, 0x65, 0x46, 0x65, 0x65, + 0x52, 0x61, 0x74, 0x65, 0x12, 0x35, 0x0a, 0x17, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x5f, 0x63, + 0x68, 0x61, 0x6e, 0x5f, 0x72, 0x65, 0x73, 0x65, 0x72, 0x76, 0x65, 0x5f, 0x73, 0x61, 0x74, 0x18, + 0x19, 0x20, 0x01, 0x28, 0x04, 0x52, 0x14, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x43, 0x68, 0x61, + 0x6e, 0x52, 0x65, 0x73, 0x65, 0x72, 0x76, 0x65, 0x53, 0x61, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x66, + 0x75, 0x6e, 0x64, 0x5f, 0x6d, 0x61, 0x78, 0x18, 0x1a, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x66, + 0x75, 0x6e, 0x64, 0x4d, 0x61, 0x78, 0x12, 0x12, 0x0a, 0x04, 0x6d, 0x65, 0x6d, 0x6f, 0x18, 0x1b, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6d, 0x65, 0x6d, 0x6f, 0x12, 0x2d, 0x0a, 0x09, 0x6f, 0x75, + 0x74, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x18, 0x1c, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0f, 0x2e, + 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4f, 0x75, 0x74, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x09, + 0x6f, 0x75, 0x74, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x22, 0xf3, 0x01, 0x0a, 0x10, 0x4f, 0x70, + 0x65, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x39, + 0x0a, 0x0c, 0x63, 0x68, 0x61, 0x6e, 0x5f, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x65, 0x6e, + 0x64, 0x69, 0x6e, 0x67, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x48, 0x00, 0x52, 0x0b, 0x63, 0x68, + 0x61, 0x6e, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x12, 0x37, 0x0a, 0x09, 0x63, 0x68, 0x61, + 0x6e, 0x5f, 0x6f, 0x70, 0x65, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x6c, + 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4f, 0x70, 0x65, 0x6e, + 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x48, 0x00, 0x52, 0x08, 0x63, 0x68, 0x61, 0x6e, 0x4f, 0x70, + 0x65, 0x6e, 0x12, 0x39, 0x0a, 0x09, 0x70, 0x73, 0x62, 0x74, 0x5f, 0x66, 0x75, 0x6e, 0x64, 0x18, + 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x65, + 0x61, 0x64, 0x79, 0x46, 0x6f, 0x72, 0x50, 0x73, 0x62, 0x74, 0x46, 0x75, 0x6e, 0x64, 0x69, 0x6e, + 0x67, 0x48, 0x00, 0x52, 0x08, 0x70, 0x73, 0x62, 0x74, 0x46, 0x75, 0x6e, 0x64, 0x12, 0x26, 0x0a, + 0x0f, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x5f, 0x69, 0x64, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0d, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x43, + 0x68, 0x61, 0x6e, 0x49, 0x64, 0x42, 0x08, 0x0a, 0x06, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x22, + 0x48, 0x0a, 0x0a, 0x4b, 0x65, 0x79, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x6f, 0x72, 0x12, 0x1d, 0x0a, + 0x0a, 0x6b, 0x65, 0x79, 0x5f, 0x66, 0x61, 0x6d, 0x69, 0x6c, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x05, 0x52, 0x09, 0x6b, 0x65, 0x79, 0x46, 0x61, 0x6d, 0x69, 0x6c, 0x79, 0x12, 0x1b, 0x0a, 0x09, + 0x6b, 0x65, 0x79, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, + 0x08, 0x6b, 0x65, 0x79, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x22, 0x5f, 0x0a, 0x0d, 0x4b, 0x65, 0x79, + 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x12, 0x22, 0x0a, 0x0d, 0x72, 0x61, + 0x77, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0c, 0x52, 0x0b, 0x72, 0x61, 0x77, 0x4b, 0x65, 0x79, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x2a, + 0x0a, 0x07, 0x6b, 0x65, 0x79, 0x5f, 0x6c, 0x6f, 0x63, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x11, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4b, 0x65, 0x79, 0x4c, 0x6f, 0x63, 0x61, 0x74, + 0x6f, 0x72, 0x52, 0x06, 0x6b, 0x65, 0x79, 0x4c, 0x6f, 0x63, 0x22, 0x88, 0x02, 0x0a, 0x0d, 0x43, + 0x68, 0x61, 0x6e, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x53, 0x68, 0x69, 0x6d, 0x12, 0x10, 0x0a, 0x03, + 0x61, 0x6d, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x03, 0x61, 0x6d, 0x74, 0x12, 0x32, + 0x0a, 0x0a, 0x63, 0x68, 0x61, 0x6e, 0x5f, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, + 0x65, 0x6c, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x09, 0x63, 0x68, 0x61, 0x6e, 0x50, 0x6f, 0x69, + 0x6e, 0x74, 0x12, 0x31, 0x0a, 0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x5f, 0x6b, 0x65, 0x79, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4b, 0x65, + 0x79, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x52, 0x08, 0x6c, 0x6f, 0x63, + 0x61, 0x6c, 0x4b, 0x65, 0x79, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x5f, + 0x6b, 0x65, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x72, 0x65, 0x6d, 0x6f, 0x74, + 0x65, 0x4b, 0x65, 0x79, 0x12, 0x26, 0x0a, 0x0f, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x5f, + 0x63, 0x68, 0x61, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0d, 0x70, + 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x43, 0x68, 0x61, 0x6e, 0x49, 0x64, 0x12, 0x1f, 0x0a, 0x0b, + 0x74, 0x68, 0x61, 0x77, 0x5f, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, + 0x0d, 0x52, 0x0a, 0x74, 0x68, 0x61, 0x77, 0x48, 0x65, 0x69, 0x67, 0x68, 0x74, 0x12, 0x16, 0x0a, + 0x06, 0x6d, 0x75, 0x73, 0x69, 0x67, 0x32, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x6d, + 0x75, 0x73, 0x69, 0x67, 0x32, 0x22, 0x6e, 0x0a, 0x08, 0x50, 0x73, 0x62, 0x74, 0x53, 0x68, 0x69, + 0x6d, 0x12, 0x26, 0x0a, 0x0f, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0d, 0x70, 0x65, 0x6e, 0x64, - 0x69, 0x6e, 0x67, 0x43, 0x68, 0x61, 0x6e, 0x49, 0x64, 0x22, 0x81, 0x01, 0x0a, 0x11, 0x46, 0x75, - 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x50, 0x73, 0x62, 0x74, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x12, - 0x1f, 0x0a, 0x0b, 0x66, 0x75, 0x6e, 0x64, 0x65, 0x64, 0x5f, 0x70, 0x73, 0x62, 0x74, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x66, 0x75, 0x6e, 0x64, 0x65, 0x64, 0x50, 0x73, 0x62, 0x74, - 0x12, 0x26, 0x0a, 0x0f, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x63, 0x68, 0x61, 0x6e, - 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0d, 0x70, 0x65, 0x6e, 0x64, 0x69, - 0x6e, 0x67, 0x43, 0x68, 0x61, 0x6e, 0x49, 0x64, 0x12, 0x23, 0x0a, 0x0d, 0x73, 0x6b, 0x69, 0x70, - 0x5f, 0x66, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, - 0x0c, 0x73, 0x6b, 0x69, 0x70, 0x46, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x22, 0x80, 0x01, - 0x0a, 0x13, 0x46, 0x75, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x50, 0x73, 0x62, 0x74, 0x46, 0x69, 0x6e, - 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x5f, - 0x70, 0x73, 0x62, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x73, 0x69, 0x67, 0x6e, - 0x65, 0x64, 0x50, 0x73, 0x62, 0x74, 0x12, 0x26, 0x0a, 0x0f, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, - 0x67, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, - 0x0d, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x43, 0x68, 0x61, 0x6e, 0x49, 0x64, 0x12, 0x20, - 0x0a, 0x0c, 0x66, 0x69, 0x6e, 0x61, 0x6c, 0x5f, 0x72, 0x61, 0x77, 0x5f, 0x74, 0x78, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x66, 0x69, 0x6e, 0x61, 0x6c, 0x52, 0x61, 0x77, 0x54, 0x78, - 0x22, 0x99, 0x02, 0x0a, 0x14, 0x46, 0x75, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x54, 0x72, 0x61, 0x6e, - 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x73, 0x67, 0x12, 0x39, 0x0a, 0x0d, 0x73, 0x68, 0x69, - 0x6d, 0x5f, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x12, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x75, 0x6e, 0x64, 0x69, 0x6e, 0x67, - 0x53, 0x68, 0x69, 0x6d, 0x48, 0x00, 0x52, 0x0c, 0x73, 0x68, 0x69, 0x6d, 0x52, 0x65, 0x67, 0x69, - 0x73, 0x74, 0x65, 0x72, 0x12, 0x3b, 0x0a, 0x0b, 0x73, 0x68, 0x69, 0x6d, 0x5f, 0x63, 0x61, 0x6e, - 0x63, 0x65, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x6c, 0x6e, 0x72, 0x70, - 0x63, 0x2e, 0x46, 0x75, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x53, 0x68, 0x69, 0x6d, 0x43, 0x61, 0x6e, - 0x63, 0x65, 0x6c, 0x48, 0x00, 0x52, 0x0a, 0x73, 0x68, 0x69, 0x6d, 0x43, 0x61, 0x6e, 0x63, 0x65, - 0x6c, 0x12, 0x3b, 0x0a, 0x0b, 0x70, 0x73, 0x62, 0x74, 0x5f, 0x76, 0x65, 0x72, 0x69, 0x66, 0x79, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x46, + 0x69, 0x6e, 0x67, 0x43, 0x68, 0x61, 0x6e, 0x49, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x62, 0x61, 0x73, + 0x65, 0x5f, 0x70, 0x73, 0x62, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x62, 0x61, + 0x73, 0x65, 0x50, 0x73, 0x62, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x6e, 0x6f, 0x5f, 0x70, 0x75, 0x62, + 0x6c, 0x69, 0x73, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x6e, 0x6f, 0x50, 0x75, + 0x62, 0x6c, 0x69, 0x73, 0x68, 0x22, 0x85, 0x01, 0x0a, 0x0b, 0x46, 0x75, 0x6e, 0x64, 0x69, 0x6e, + 0x67, 0x53, 0x68, 0x69, 0x6d, 0x12, 0x3e, 0x0a, 0x0f, 0x63, 0x68, 0x61, 0x6e, 0x5f, 0x70, 0x6f, + 0x69, 0x6e, 0x74, 0x5f, 0x73, 0x68, 0x69, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, + 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x50, 0x6f, 0x69, 0x6e, 0x74, + 0x53, 0x68, 0x69, 0x6d, 0x48, 0x00, 0x52, 0x0d, 0x63, 0x68, 0x61, 0x6e, 0x50, 0x6f, 0x69, 0x6e, + 0x74, 0x53, 0x68, 0x69, 0x6d, 0x12, 0x2e, 0x0a, 0x09, 0x70, 0x73, 0x62, 0x74, 0x5f, 0x73, 0x68, + 0x69, 0x6d, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, + 0x2e, 0x50, 0x73, 0x62, 0x74, 0x53, 0x68, 0x69, 0x6d, 0x48, 0x00, 0x52, 0x08, 0x70, 0x73, 0x62, + 0x74, 0x53, 0x68, 0x69, 0x6d, 0x42, 0x06, 0x0a, 0x04, 0x73, 0x68, 0x69, 0x6d, 0x22, 0x3b, 0x0a, + 0x11, 0x46, 0x75, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x53, 0x68, 0x69, 0x6d, 0x43, 0x61, 0x6e, 0x63, + 0x65, 0x6c, 0x12, 0x26, 0x0a, 0x0f, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x63, 0x68, + 0x61, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0d, 0x70, 0x65, 0x6e, + 0x64, 0x69, 0x6e, 0x67, 0x43, 0x68, 0x61, 0x6e, 0x49, 0x64, 0x22, 0x81, 0x01, 0x0a, 0x11, 0x46, 0x75, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x50, 0x73, 0x62, 0x74, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, - 0x48, 0x00, 0x52, 0x0a, 0x70, 0x73, 0x62, 0x74, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x12, 0x41, - 0x0a, 0x0d, 0x70, 0x73, 0x62, 0x74, 0x5f, 0x66, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x18, - 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x75, - 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x50, 0x73, 0x62, 0x74, 0x46, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x7a, - 0x65, 0x48, 0x00, 0x52, 0x0c, 0x70, 0x73, 0x62, 0x74, 0x46, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x7a, - 0x65, 0x42, 0x09, 0x0a, 0x07, 0x74, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x22, 0x16, 0x0a, 0x14, - 0x46, 0x75, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x53, 0x74, 0x61, 0x74, 0x65, 0x53, 0x74, 0x65, 0x70, - 0x52, 0x65, 0x73, 0x70, 0x22, 0xcc, 0x01, 0x0a, 0x0b, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, - 0x48, 0x54, 0x4c, 0x43, 0x12, 0x1a, 0x0a, 0x08, 0x69, 0x6e, 0x63, 0x6f, 0x6d, 0x69, 0x6e, 0x67, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x69, 0x6e, 0x63, 0x6f, 0x6d, 0x69, 0x6e, 0x67, - 0x12, 0x16, 0x0a, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, - 0x52, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x6f, 0x75, 0x74, 0x70, - 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6f, 0x75, 0x74, 0x70, - 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x27, 0x0a, 0x0f, 0x6d, 0x61, 0x74, 0x75, 0x72, 0x69, 0x74, 0x79, - 0x5f, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0e, 0x6d, - 0x61, 0x74, 0x75, 0x72, 0x69, 0x74, 0x79, 0x48, 0x65, 0x69, 0x67, 0x68, 0x74, 0x12, 0x2e, 0x0a, - 0x13, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x5f, 0x74, 0x69, 0x6c, 0x5f, 0x6d, 0x61, 0x74, 0x75, - 0x72, 0x69, 0x74, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x52, 0x11, 0x62, 0x6c, 0x6f, 0x63, - 0x6b, 0x73, 0x54, 0x69, 0x6c, 0x4d, 0x61, 0x74, 0x75, 0x72, 0x69, 0x74, 0x79, 0x12, 0x14, 0x0a, - 0x05, 0x73, 0x74, 0x61, 0x67, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x73, 0x74, - 0x61, 0x67, 0x65, 0x22, 0x3e, 0x0a, 0x16, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x43, 0x68, - 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x24, 0x0a, - 0x0e, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x5f, 0x72, 0x61, 0x77, 0x5f, 0x74, 0x78, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x52, 0x61, - 0x77, 0x54, 0x78, 0x22, 0x91, 0x14, 0x0a, 0x17, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x43, - 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, - 0x2e, 0x0a, 0x13, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x6c, 0x69, 0x6d, 0x62, 0x6f, 0x5f, 0x62, - 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x11, 0x74, 0x6f, - 0x74, 0x61, 0x6c, 0x4c, 0x69, 0x6d, 0x62, 0x6f, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x12, - 0x65, 0x0a, 0x15, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x6f, 0x70, 0x65, 0x6e, 0x5f, - 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x31, - 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x43, 0x68, - 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x50, - 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x4f, 0x70, 0x65, 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, - 0x6c, 0x52, 0x13, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x4f, 0x70, 0x65, 0x6e, 0x43, 0x68, - 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x12, 0x6a, 0x0a, 0x18, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, - 0x67, 0x5f, 0x63, 0x6c, 0x6f, 0x73, 0x69, 0x6e, 0x67, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, - 0x6c, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, - 0x2e, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x64, 0x43, - 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x42, 0x02, 0x18, 0x01, 0x52, 0x16, 0x70, 0x65, 0x6e, 0x64, - 0x69, 0x6e, 0x67, 0x43, 0x6c, 0x6f, 0x73, 0x69, 0x6e, 0x67, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, - 0x6c, 0x73, 0x12, 0x76, 0x0a, 0x1e, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x66, 0x6f, - 0x72, 0x63, 0x65, 0x5f, 0x63, 0x6c, 0x6f, 0x73, 0x69, 0x6e, 0x67, 0x5f, 0x63, 0x68, 0x61, 0x6e, - 0x6e, 0x65, 0x6c, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x31, 0x2e, 0x6c, 0x6e, 0x72, - 0x70, 0x63, 0x2e, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, - 0x6c, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x46, 0x6f, 0x72, 0x63, 0x65, - 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x64, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x1b, 0x70, - 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x46, 0x6f, 0x72, 0x63, 0x65, 0x43, 0x6c, 0x6f, 0x73, 0x69, - 0x6e, 0x67, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x12, 0x68, 0x0a, 0x16, 0x77, 0x61, - 0x69, 0x74, 0x69, 0x6e, 0x67, 0x5f, 0x63, 0x6c, 0x6f, 0x73, 0x65, 0x5f, 0x63, 0x68, 0x61, 0x6e, - 0x6e, 0x65, 0x6c, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x32, 0x2e, 0x6c, 0x6e, 0x72, - 0x70, 0x63, 0x2e, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, - 0x6c, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x57, 0x61, 0x69, 0x74, 0x69, - 0x6e, 0x67, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x14, - 0x77, 0x61, 0x69, 0x74, 0x69, 0x6e, 0x67, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x43, 0x68, 0x61, 0x6e, - 0x6e, 0x65, 0x6c, 0x73, 0x1a, 0xe3, 0x04, 0x0a, 0x0e, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, - 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x12, 0x26, 0x0a, 0x0f, 0x72, 0x65, 0x6d, 0x6f, 0x74, - 0x65, 0x5f, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x70, 0x75, 0x62, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x0d, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x4e, 0x6f, 0x64, 0x65, 0x50, 0x75, 0x62, 0x12, - 0x23, 0x0a, 0x0d, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x5f, 0x70, 0x6f, 0x69, 0x6e, 0x74, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x50, - 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x63, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x63, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, - 0x12, 0x23, 0x0a, 0x0d, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x5f, 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, - 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0c, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x42, 0x61, - 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x5f, - 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0d, 0x72, - 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x12, 0x33, 0x0a, 0x16, - 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x5f, 0x72, 0x65, 0x73, 0x65, 0x72, - 0x76, 0x65, 0x5f, 0x73, 0x61, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, 0x52, 0x13, 0x6c, 0x6f, - 0x63, 0x61, 0x6c, 0x43, 0x68, 0x61, 0x6e, 0x52, 0x65, 0x73, 0x65, 0x72, 0x76, 0x65, 0x53, 0x61, - 0x74, 0x12, 0x35, 0x0a, 0x17, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x5f, 0x63, 0x68, 0x61, 0x6e, - 0x5f, 0x72, 0x65, 0x73, 0x65, 0x72, 0x76, 0x65, 0x5f, 0x73, 0x61, 0x74, 0x18, 0x07, 0x20, 0x01, - 0x28, 0x03, 0x52, 0x14, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x52, 0x65, - 0x73, 0x65, 0x72, 0x76, 0x65, 0x53, 0x61, 0x74, 0x12, 0x2e, 0x0a, 0x09, 0x69, 0x6e, 0x69, 0x74, - 0x69, 0x61, 0x74, 0x6f, 0x72, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x10, 0x2e, 0x6c, 0x6e, - 0x72, 0x70, 0x63, 0x2e, 0x49, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x74, 0x6f, 0x72, 0x52, 0x09, 0x69, - 0x6e, 0x69, 0x74, 0x69, 0x61, 0x74, 0x6f, 0x72, 0x12, 0x3e, 0x0a, 0x0f, 0x63, 0x6f, 0x6d, 0x6d, - 0x69, 0x74, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, - 0x0e, 0x32, 0x15, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, - 0x6d, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x52, 0x0e, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, - 0x6d, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x36, 0x0a, 0x17, 0x6e, 0x75, 0x6d, 0x5f, - 0x66, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x70, 0x61, 0x63, 0x6b, 0x61, - 0x67, 0x65, 0x73, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x03, 0x52, 0x15, 0x6e, 0x75, 0x6d, 0x46, 0x6f, - 0x72, 0x77, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x73, - 0x12, 0x2a, 0x0a, 0x11, 0x63, 0x68, 0x61, 0x6e, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x5f, - 0x66, 0x6c, 0x61, 0x67, 0x73, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x63, 0x68, 0x61, - 0x6e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x46, 0x6c, 0x61, 0x67, 0x73, 0x12, 0x18, 0x0a, 0x07, - 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x70, - 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6d, 0x65, 0x6d, 0x6f, 0x18, 0x0d, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6d, 0x65, 0x6d, 0x6f, 0x12, 0x2e, 0x0a, 0x13, 0x63, 0x75, - 0x73, 0x74, 0x6f, 0x6d, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x5f, 0x64, 0x61, 0x74, - 0x61, 0x18, 0x22, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x11, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x43, - 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x44, 0x61, 0x74, 0x61, 0x1a, 0xf9, 0x01, 0x0a, 0x12, 0x50, - 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x4f, 0x70, 0x65, 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, - 0x6c, 0x12, 0x47, 0x0a, 0x07, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x65, 0x6e, 0x64, 0x69, + 0x12, 0x1f, 0x0a, 0x0b, 0x66, 0x75, 0x6e, 0x64, 0x65, 0x64, 0x5f, 0x70, 0x73, 0x62, 0x74, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x66, 0x75, 0x6e, 0x64, 0x65, 0x64, 0x50, 0x73, 0x62, + 0x74, 0x12, 0x26, 0x0a, 0x0f, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x63, 0x68, 0x61, + 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0d, 0x70, 0x65, 0x6e, 0x64, + 0x69, 0x6e, 0x67, 0x43, 0x68, 0x61, 0x6e, 0x49, 0x64, 0x12, 0x23, 0x0a, 0x0d, 0x73, 0x6b, 0x69, + 0x70, 0x5f, 0x66, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, + 0x52, 0x0c, 0x73, 0x6b, 0x69, 0x70, 0x46, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x22, 0x80, + 0x01, 0x0a, 0x13, 0x46, 0x75, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x50, 0x73, 0x62, 0x74, 0x46, 0x69, + 0x6e, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, + 0x5f, 0x70, 0x73, 0x62, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x73, 0x69, 0x67, + 0x6e, 0x65, 0x64, 0x50, 0x73, 0x62, 0x74, 0x12, 0x26, 0x0a, 0x0f, 0x70, 0x65, 0x6e, 0x64, 0x69, + 0x6e, 0x67, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, + 0x52, 0x0d, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x43, 0x68, 0x61, 0x6e, 0x49, 0x64, 0x12, + 0x20, 0x0a, 0x0c, 0x66, 0x69, 0x6e, 0x61, 0x6c, 0x5f, 0x72, 0x61, 0x77, 0x5f, 0x74, 0x78, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x66, 0x69, 0x6e, 0x61, 0x6c, 0x52, 0x61, 0x77, 0x54, + 0x78, 0x22, 0x99, 0x02, 0x0a, 0x14, 0x46, 0x75, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x54, 0x72, 0x61, + 0x6e, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x73, 0x67, 0x12, 0x39, 0x0a, 0x0d, 0x73, 0x68, + 0x69, 0x6d, 0x5f, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x12, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x75, 0x6e, 0x64, 0x69, 0x6e, + 0x67, 0x53, 0x68, 0x69, 0x6d, 0x48, 0x00, 0x52, 0x0c, 0x73, 0x68, 0x69, 0x6d, 0x52, 0x65, 0x67, + 0x69, 0x73, 0x74, 0x65, 0x72, 0x12, 0x3b, 0x0a, 0x0b, 0x73, 0x68, 0x69, 0x6d, 0x5f, 0x63, 0x61, + 0x6e, 0x63, 0x65, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x6c, 0x6e, 0x72, + 0x70, 0x63, 0x2e, 0x46, 0x75, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x53, 0x68, 0x69, 0x6d, 0x43, 0x61, + 0x6e, 0x63, 0x65, 0x6c, 0x48, 0x00, 0x52, 0x0a, 0x73, 0x68, 0x69, 0x6d, 0x43, 0x61, 0x6e, 0x63, + 0x65, 0x6c, 0x12, 0x3b, 0x0a, 0x0b, 0x70, 0x73, 0x62, 0x74, 0x5f, 0x76, 0x65, 0x72, 0x69, 0x66, + 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, + 0x46, 0x75, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x50, 0x73, 0x62, 0x74, 0x56, 0x65, 0x72, 0x69, 0x66, + 0x79, 0x48, 0x00, 0x52, 0x0a, 0x70, 0x73, 0x62, 0x74, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x12, + 0x41, 0x0a, 0x0d, 0x70, 0x73, 0x62, 0x74, 0x5f, 0x66, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x7a, 0x65, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x46, + 0x75, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x50, 0x73, 0x62, 0x74, 0x46, 0x69, 0x6e, 0x61, 0x6c, 0x69, + 0x7a, 0x65, 0x48, 0x00, 0x52, 0x0c, 0x70, 0x73, 0x62, 0x74, 0x46, 0x69, 0x6e, 0x61, 0x6c, 0x69, + 0x7a, 0x65, 0x42, 0x09, 0x0a, 0x07, 0x74, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x22, 0x16, 0x0a, + 0x14, 0x46, 0x75, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x53, 0x74, 0x61, 0x74, 0x65, 0x53, 0x74, 0x65, + 0x70, 0x52, 0x65, 0x73, 0x70, 0x22, 0xcc, 0x01, 0x0a, 0x0b, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, + 0x67, 0x48, 0x54, 0x4c, 0x43, 0x12, 0x1a, 0x0a, 0x08, 0x69, 0x6e, 0x63, 0x6f, 0x6d, 0x69, 0x6e, + 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x69, 0x6e, 0x63, 0x6f, 0x6d, 0x69, 0x6e, + 0x67, 0x12, 0x16, 0x0a, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x03, 0x52, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x6f, 0x75, 0x74, + 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6f, 0x75, 0x74, + 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x27, 0x0a, 0x0f, 0x6d, 0x61, 0x74, 0x75, 0x72, 0x69, 0x74, + 0x79, 0x5f, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0e, + 0x6d, 0x61, 0x74, 0x75, 0x72, 0x69, 0x74, 0x79, 0x48, 0x65, 0x69, 0x67, 0x68, 0x74, 0x12, 0x2e, + 0x0a, 0x13, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x5f, 0x74, 0x69, 0x6c, 0x5f, 0x6d, 0x61, 0x74, + 0x75, 0x72, 0x69, 0x74, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x52, 0x11, 0x62, 0x6c, 0x6f, + 0x63, 0x6b, 0x73, 0x54, 0x69, 0x6c, 0x4d, 0x61, 0x74, 0x75, 0x72, 0x69, 0x74, 0x79, 0x12, 0x14, + 0x0a, 0x05, 0x73, 0x74, 0x61, 0x67, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x73, + 0x74, 0x61, 0x67, 0x65, 0x22, 0x3e, 0x0a, 0x16, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x43, + 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x24, + 0x0a, 0x0e, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x5f, 0x72, 0x61, 0x77, 0x5f, 0x74, 0x78, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x52, + 0x61, 0x77, 0x54, 0x78, 0x22, 0x91, 0x14, 0x0a, 0x17, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, + 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x2e, 0x0a, 0x13, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x6c, 0x69, 0x6d, 0x62, 0x6f, 0x5f, + 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x11, 0x74, + 0x6f, 0x74, 0x61, 0x6c, 0x4c, 0x69, 0x6d, 0x62, 0x6f, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, + 0x12, 0x65, 0x0a, 0x15, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x6f, 0x70, 0x65, 0x6e, + 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x31, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x43, + 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, + 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x4f, 0x70, 0x65, 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x6e, + 0x65, 0x6c, 0x52, 0x13, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x4f, 0x70, 0x65, 0x6e, 0x43, + 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x12, 0x6a, 0x0a, 0x18, 0x70, 0x65, 0x6e, 0x64, 0x69, + 0x6e, 0x67, 0x5f, 0x63, 0x6c, 0x6f, 0x73, 0x69, 0x6e, 0x67, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x6e, + 0x65, 0x6c, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x6c, 0x6e, 0x72, 0x70, + 0x63, 0x2e, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, + 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x64, + 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x42, 0x02, 0x18, 0x01, 0x52, 0x16, 0x70, 0x65, 0x6e, + 0x64, 0x69, 0x6e, 0x67, 0x43, 0x6c, 0x6f, 0x73, 0x69, 0x6e, 0x67, 0x43, 0x68, 0x61, 0x6e, 0x6e, + 0x65, 0x6c, 0x73, 0x12, 0x76, 0x0a, 0x1e, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x66, + 0x6f, 0x72, 0x63, 0x65, 0x5f, 0x63, 0x6c, 0x6f, 0x73, 0x69, 0x6e, 0x67, 0x5f, 0x63, 0x68, 0x61, + 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x31, 0x2e, 0x6c, 0x6e, + 0x72, 0x70, 0x63, 0x2e, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x43, 0x68, 0x61, 0x6e, 0x6e, + 0x65, 0x6c, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x46, 0x6f, 0x72, 0x63, + 0x65, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x64, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x1b, + 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x46, 0x6f, 0x72, 0x63, 0x65, 0x43, 0x6c, 0x6f, 0x73, + 0x69, 0x6e, 0x67, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x12, 0x68, 0x0a, 0x16, 0x77, + 0x61, 0x69, 0x74, 0x69, 0x6e, 0x67, 0x5f, 0x63, 0x6c, 0x6f, 0x73, 0x65, 0x5f, 0x63, 0x68, 0x61, + 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x32, 0x2e, 0x6c, 0x6e, + 0x72, 0x70, 0x63, 0x2e, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x43, 0x68, 0x61, 0x6e, 0x6e, + 0x65, 0x6c, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x57, 0x61, 0x69, 0x74, + 0x69, 0x6e, 0x67, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, + 0x14, 0x77, 0x61, 0x69, 0x74, 0x69, 0x6e, 0x67, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x43, 0x68, 0x61, + 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x1a, 0xe3, 0x04, 0x0a, 0x0e, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, + 0x67, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x12, 0x26, 0x0a, 0x0f, 0x72, 0x65, 0x6d, 0x6f, + 0x74, 0x65, 0x5f, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x70, 0x75, 0x62, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0d, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x4e, 0x6f, 0x64, 0x65, 0x50, 0x75, 0x62, + 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x5f, 0x70, 0x6f, 0x69, 0x6e, + 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, + 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x63, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, + 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x63, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, + 0x79, 0x12, 0x23, 0x0a, 0x0d, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x5f, 0x62, 0x61, 0x6c, 0x61, 0x6e, + 0x63, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0c, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x42, + 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, + 0x5f, 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0d, + 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x12, 0x33, 0x0a, + 0x16, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x5f, 0x72, 0x65, 0x73, 0x65, + 0x72, 0x76, 0x65, 0x5f, 0x73, 0x61, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, 0x52, 0x13, 0x6c, + 0x6f, 0x63, 0x61, 0x6c, 0x43, 0x68, 0x61, 0x6e, 0x52, 0x65, 0x73, 0x65, 0x72, 0x76, 0x65, 0x53, + 0x61, 0x74, 0x12, 0x35, 0x0a, 0x17, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x5f, 0x63, 0x68, 0x61, + 0x6e, 0x5f, 0x72, 0x65, 0x73, 0x65, 0x72, 0x76, 0x65, 0x5f, 0x73, 0x61, 0x74, 0x18, 0x07, 0x20, + 0x01, 0x28, 0x03, 0x52, 0x14, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x52, + 0x65, 0x73, 0x65, 0x72, 0x76, 0x65, 0x53, 0x61, 0x74, 0x12, 0x2e, 0x0a, 0x09, 0x69, 0x6e, 0x69, + 0x74, 0x69, 0x61, 0x74, 0x6f, 0x72, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x10, 0x2e, 0x6c, + 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x49, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x74, 0x6f, 0x72, 0x52, 0x09, + 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x74, 0x6f, 0x72, 0x12, 0x3e, 0x0a, 0x0f, 0x63, 0x6f, 0x6d, + 0x6d, 0x69, 0x74, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x09, 0x20, 0x01, + 0x28, 0x0e, 0x32, 0x15, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x69, + 0x74, 0x6d, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x52, 0x0e, 0x63, 0x6f, 0x6d, 0x6d, 0x69, + 0x74, 0x6d, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x36, 0x0a, 0x17, 0x6e, 0x75, 0x6d, + 0x5f, 0x66, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x70, 0x61, 0x63, 0x6b, + 0x61, 0x67, 0x65, 0x73, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x03, 0x52, 0x15, 0x6e, 0x75, 0x6d, 0x46, + 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, + 0x73, 0x12, 0x2a, 0x0a, 0x11, 0x63, 0x68, 0x61, 0x6e, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, + 0x5f, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x63, 0x68, + 0x61, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x46, 0x6c, 0x61, 0x67, 0x73, 0x12, 0x18, 0x0a, + 0x07, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, + 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6d, 0x65, 0x6d, 0x6f, 0x18, + 0x0d, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6d, 0x65, 0x6d, 0x6f, 0x12, 0x2e, 0x0a, 0x13, 0x63, + 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x5f, 0x64, 0x61, + 0x74, 0x61, 0x18, 0x22, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x11, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, + 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x44, 0x61, 0x74, 0x61, 0x1a, 0xf9, 0x01, 0x0a, 0x12, + 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x4f, 0x70, 0x65, 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x6e, + 0x65, 0x6c, 0x12, 0x47, 0x0a, 0x07, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x65, 0x6e, 0x64, + 0x69, 0x6e, 0x67, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x2e, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x43, 0x68, 0x61, 0x6e, 0x6e, + 0x65, 0x6c, 0x52, 0x07, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x12, 0x1d, 0x0a, 0x0a, 0x63, + 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x5f, 0x66, 0x65, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, + 0x09, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x46, 0x65, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x6f, + 0x6d, 0x6d, 0x69, 0x74, 0x5f, 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, + 0x03, 0x52, 0x0c, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x57, 0x65, 0x69, 0x67, 0x68, 0x74, 0x12, + 0x1c, 0x0a, 0x0a, 0x66, 0x65, 0x65, 0x5f, 0x70, 0x65, 0x72, 0x5f, 0x6b, 0x77, 0x18, 0x06, 0x20, + 0x01, 0x28, 0x03, 0x52, 0x08, 0x66, 0x65, 0x65, 0x50, 0x65, 0x72, 0x4b, 0x77, 0x12, 0x32, 0x0a, + 0x15, 0x66, 0x75, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x65, 0x78, 0x70, 0x69, 0x72, 0x79, 0x5f, + 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x13, 0x66, 0x75, + 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x45, 0x78, 0x70, 0x69, 0x72, 0x79, 0x42, 0x6c, 0x6f, 0x63, 0x6b, + 0x73, 0x4a, 0x04, 0x08, 0x02, 0x10, 0x03, 0x1a, 0x9a, 0x02, 0x0a, 0x13, 0x57, 0x61, 0x69, 0x74, + 0x69, 0x6e, 0x67, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x12, + 0x47, 0x0a, 0x07, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x2d, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, + 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x2e, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, + 0x07, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x12, 0x23, 0x0a, 0x0d, 0x6c, 0x69, 0x6d, 0x62, + 0x6f, 0x5f, 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, + 0x0c, 0x6c, 0x69, 0x6d, 0x62, 0x6f, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x12, 0x4c, 0x0a, + 0x0b, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x2e, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, - 0x6c, 0x52, 0x07, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6f, - 0x6d, 0x6d, 0x69, 0x74, 0x5f, 0x66, 0x65, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, - 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x46, 0x65, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x6f, 0x6d, - 0x6d, 0x69, 0x74, 0x5f, 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, - 0x52, 0x0c, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x57, 0x65, 0x69, 0x67, 0x68, 0x74, 0x12, 0x1c, - 0x0a, 0x0a, 0x66, 0x65, 0x65, 0x5f, 0x70, 0x65, 0x72, 0x5f, 0x6b, 0x77, 0x18, 0x06, 0x20, 0x01, - 0x28, 0x03, 0x52, 0x08, 0x66, 0x65, 0x65, 0x50, 0x65, 0x72, 0x4b, 0x77, 0x12, 0x32, 0x0a, 0x15, - 0x66, 0x75, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x65, 0x78, 0x70, 0x69, 0x72, 0x79, 0x5f, 0x62, - 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x13, 0x66, 0x75, 0x6e, - 0x64, 0x69, 0x6e, 0x67, 0x45, 0x78, 0x70, 0x69, 0x72, 0x79, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x73, - 0x4a, 0x04, 0x08, 0x02, 0x10, 0x03, 0x1a, 0x9a, 0x02, 0x0a, 0x13, 0x57, 0x61, 0x69, 0x74, 0x69, - 0x6e, 0x67, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x12, 0x47, + 0x73, 0x65, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x0b, + 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x63, + 0x6c, 0x6f, 0x73, 0x69, 0x6e, 0x67, 0x5f, 0x74, 0x78, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0b, 0x63, 0x6c, 0x6f, 0x73, 0x69, 0x6e, 0x67, 0x54, 0x78, 0x69, 0x64, 0x12, 0x24, + 0x0a, 0x0e, 0x63, 0x6c, 0x6f, 0x73, 0x69, 0x6e, 0x67, 0x5f, 0x74, 0x78, 0x5f, 0x68, 0x65, 0x78, + 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x63, 0x6c, 0x6f, 0x73, 0x69, 0x6e, 0x67, 0x54, + 0x78, 0x48, 0x65, 0x78, 0x1a, 0xa3, 0x02, 0x0a, 0x0b, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x6d, + 0x65, 0x6e, 0x74, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x5f, 0x74, 0x78, + 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x54, + 0x78, 0x69, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x5f, 0x74, 0x78, + 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, + 0x54, 0x78, 0x69, 0x64, 0x12, 0x2e, 0x0a, 0x13, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x5f, 0x70, + 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x74, 0x78, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x11, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, + 0x54, 0x78, 0x69, 0x64, 0x12, 0x2f, 0x0a, 0x14, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x5f, 0x63, 0x6f, + 0x6d, 0x6d, 0x69, 0x74, 0x5f, 0x66, 0x65, 0x65, 0x5f, 0x73, 0x61, 0x74, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x04, 0x52, 0x11, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x46, + 0x65, 0x65, 0x53, 0x61, 0x74, 0x12, 0x31, 0x0a, 0x15, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x5f, + 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x5f, 0x66, 0x65, 0x65, 0x5f, 0x73, 0x61, 0x74, 0x18, 0x05, + 0x20, 0x01, 0x28, 0x04, 0x52, 0x12, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x43, 0x6f, 0x6d, 0x6d, + 0x69, 0x74, 0x46, 0x65, 0x65, 0x53, 0x61, 0x74, 0x12, 0x40, 0x0a, 0x1d, 0x72, 0x65, 0x6d, 0x6f, + 0x74, 0x65, 0x5f, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x63, 0x6f, 0x6d, 0x6d, 0x69, + 0x74, 0x5f, 0x66, 0x65, 0x65, 0x5f, 0x73, 0x61, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x04, 0x52, + 0x19, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x43, 0x6f, + 0x6d, 0x6d, 0x69, 0x74, 0x46, 0x65, 0x65, 0x53, 0x61, 0x74, 0x1a, 0x7b, 0x0a, 0x0d, 0x43, 0x6c, + 0x6f, 0x73, 0x65, 0x64, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x12, 0x47, 0x0a, 0x07, 0x63, + 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x6c, + 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x43, 0x68, 0x61, 0x6e, + 0x6e, 0x65, 0x6c, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x50, 0x65, 0x6e, + 0x64, 0x69, 0x6e, 0x67, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x07, 0x63, 0x68, 0x61, + 0x6e, 0x6e, 0x65, 0x6c, 0x12, 0x21, 0x0a, 0x0c, 0x63, 0x6c, 0x6f, 0x73, 0x69, 0x6e, 0x67, 0x5f, + 0x74, 0x78, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x6c, 0x6f, 0x73, + 0x69, 0x6e, 0x67, 0x54, 0x78, 0x69, 0x64, 0x1a, 0xee, 0x03, 0x0a, 0x12, 0x46, 0x6f, 0x72, 0x63, + 0x65, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x64, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x12, 0x47, 0x0a, 0x07, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x07, - 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x12, 0x23, 0x0a, 0x0d, 0x6c, 0x69, 0x6d, 0x62, 0x6f, - 0x5f, 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0c, - 0x6c, 0x69, 0x6d, 0x62, 0x6f, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x12, 0x4c, 0x0a, 0x0b, - 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x2a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, - 0x67, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x0b, 0x63, - 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x63, 0x6c, - 0x6f, 0x73, 0x69, 0x6e, 0x67, 0x5f, 0x74, 0x78, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x0b, 0x63, 0x6c, 0x6f, 0x73, 0x69, 0x6e, 0x67, 0x54, 0x78, 0x69, 0x64, 0x12, 0x24, 0x0a, - 0x0e, 0x63, 0x6c, 0x6f, 0x73, 0x69, 0x6e, 0x67, 0x5f, 0x74, 0x78, 0x5f, 0x68, 0x65, 0x78, 0x18, - 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x63, 0x6c, 0x6f, 0x73, 0x69, 0x6e, 0x67, 0x54, 0x78, - 0x48, 0x65, 0x78, 0x1a, 0xa3, 0x02, 0x0a, 0x0b, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x6d, 0x65, - 0x6e, 0x74, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x5f, 0x74, 0x78, 0x69, - 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x54, 0x78, - 0x69, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x5f, 0x74, 0x78, 0x69, - 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x54, - 0x78, 0x69, 0x64, 0x12, 0x2e, 0x0a, 0x13, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x5f, 0x70, 0x65, - 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x74, 0x78, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x11, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x54, - 0x78, 0x69, 0x64, 0x12, 0x2f, 0x0a, 0x14, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x5f, 0x63, 0x6f, 0x6d, - 0x6d, 0x69, 0x74, 0x5f, 0x66, 0x65, 0x65, 0x5f, 0x73, 0x61, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, - 0x04, 0x52, 0x11, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x46, 0x65, - 0x65, 0x53, 0x61, 0x74, 0x12, 0x31, 0x0a, 0x15, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x5f, 0x63, - 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x5f, 0x66, 0x65, 0x65, 0x5f, 0x73, 0x61, 0x74, 0x18, 0x05, 0x20, - 0x01, 0x28, 0x04, 0x52, 0x12, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x43, 0x6f, 0x6d, 0x6d, 0x69, - 0x74, 0x46, 0x65, 0x65, 0x53, 0x61, 0x74, 0x12, 0x40, 0x0a, 0x1d, 0x72, 0x65, 0x6d, 0x6f, 0x74, - 0x65, 0x5f, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, - 0x5f, 0x66, 0x65, 0x65, 0x5f, 0x73, 0x61, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x04, 0x52, 0x19, - 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6d, - 0x6d, 0x69, 0x74, 0x46, 0x65, 0x65, 0x53, 0x61, 0x74, 0x1a, 0x7b, 0x0a, 0x0d, 0x43, 0x6c, 0x6f, - 0x73, 0x65, 0x64, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x12, 0x47, 0x0a, 0x07, 0x63, 0x68, - 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x6c, 0x6e, - 0x72, 0x70, 0x63, 0x2e, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x43, 0x68, 0x61, 0x6e, 0x6e, - 0x65, 0x6c, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x50, 0x65, 0x6e, 0x64, - 0x69, 0x6e, 0x67, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x07, 0x63, 0x68, 0x61, 0x6e, - 0x6e, 0x65, 0x6c, 0x12, 0x21, 0x0a, 0x0c, 0x63, 0x6c, 0x6f, 0x73, 0x69, 0x6e, 0x67, 0x5f, 0x74, - 0x78, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x6c, 0x6f, 0x73, 0x69, - 0x6e, 0x67, 0x54, 0x78, 0x69, 0x64, 0x1a, 0xee, 0x03, 0x0a, 0x12, 0x46, 0x6f, 0x72, 0x63, 0x65, - 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x64, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x12, 0x47, 0x0a, - 0x07, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2d, + 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x12, 0x21, 0x0a, 0x0c, 0x63, 0x6c, 0x6f, 0x73, 0x69, + 0x6e, 0x67, 0x5f, 0x74, 0x78, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, + 0x6c, 0x6f, 0x73, 0x69, 0x6e, 0x67, 0x54, 0x78, 0x69, 0x64, 0x12, 0x23, 0x0a, 0x0d, 0x6c, 0x69, + 0x6d, 0x62, 0x6f, 0x5f, 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x03, 0x52, 0x0c, 0x6c, 0x69, 0x6d, 0x62, 0x6f, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x12, + 0x27, 0x0a, 0x0f, 0x6d, 0x61, 0x74, 0x75, 0x72, 0x69, 0x74, 0x79, 0x5f, 0x68, 0x65, 0x69, 0x67, + 0x68, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0e, 0x6d, 0x61, 0x74, 0x75, 0x72, 0x69, + 0x74, 0x79, 0x48, 0x65, 0x69, 0x67, 0x68, 0x74, 0x12, 0x2e, 0x0a, 0x13, 0x62, 0x6c, 0x6f, 0x63, + 0x6b, 0x73, 0x5f, 0x74, 0x69, 0x6c, 0x5f, 0x6d, 0x61, 0x74, 0x75, 0x72, 0x69, 0x74, 0x79, 0x18, + 0x05, 0x20, 0x01, 0x28, 0x05, 0x52, 0x11, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x54, 0x69, 0x6c, + 0x4d, 0x61, 0x74, 0x75, 0x72, 0x69, 0x74, 0x79, 0x12, 0x2b, 0x0a, 0x11, 0x72, 0x65, 0x63, 0x6f, + 0x76, 0x65, 0x72, 0x65, 0x64, 0x5f, 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x18, 0x06, 0x20, + 0x01, 0x28, 0x03, 0x52, 0x10, 0x72, 0x65, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x65, 0x64, 0x42, 0x61, + 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x12, 0x37, 0x0a, 0x0d, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, + 0x5f, 0x68, 0x74, 0x6c, 0x63, 0x73, 0x18, 0x08, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x6c, + 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x48, 0x54, 0x4c, 0x43, + 0x52, 0x0c, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x48, 0x74, 0x6c, 0x63, 0x73, 0x12, 0x55, + 0x0a, 0x06, 0x61, 0x6e, 0x63, 0x68, 0x6f, 0x72, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x3d, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x43, 0x68, - 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x50, - 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x07, 0x63, - 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x12, 0x21, 0x0a, 0x0c, 0x63, 0x6c, 0x6f, 0x73, 0x69, 0x6e, - 0x67, 0x5f, 0x74, 0x78, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x6c, - 0x6f, 0x73, 0x69, 0x6e, 0x67, 0x54, 0x78, 0x69, 0x64, 0x12, 0x23, 0x0a, 0x0d, 0x6c, 0x69, 0x6d, - 0x62, 0x6f, 0x5f, 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, - 0x52, 0x0c, 0x6c, 0x69, 0x6d, 0x62, 0x6f, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x12, 0x27, - 0x0a, 0x0f, 0x6d, 0x61, 0x74, 0x75, 0x72, 0x69, 0x74, 0x79, 0x5f, 0x68, 0x65, 0x69, 0x67, 0x68, - 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0e, 0x6d, 0x61, 0x74, 0x75, 0x72, 0x69, 0x74, - 0x79, 0x48, 0x65, 0x69, 0x67, 0x68, 0x74, 0x12, 0x2e, 0x0a, 0x13, 0x62, 0x6c, 0x6f, 0x63, 0x6b, - 0x73, 0x5f, 0x74, 0x69, 0x6c, 0x5f, 0x6d, 0x61, 0x74, 0x75, 0x72, 0x69, 0x74, 0x79, 0x18, 0x05, - 0x20, 0x01, 0x28, 0x05, 0x52, 0x11, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x54, 0x69, 0x6c, 0x4d, - 0x61, 0x74, 0x75, 0x72, 0x69, 0x74, 0x79, 0x12, 0x2b, 0x0a, 0x11, 0x72, 0x65, 0x63, 0x6f, 0x76, - 0x65, 0x72, 0x65, 0x64, 0x5f, 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x18, 0x06, 0x20, 0x01, - 0x28, 0x03, 0x52, 0x10, 0x72, 0x65, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x65, 0x64, 0x42, 0x61, 0x6c, - 0x61, 0x6e, 0x63, 0x65, 0x12, 0x37, 0x0a, 0x0d, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x5f, - 0x68, 0x74, 0x6c, 0x63, 0x73, 0x18, 0x08, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x6c, 0x6e, - 0x72, 0x70, 0x63, 0x2e, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x48, 0x54, 0x4c, 0x43, 0x52, - 0x0c, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x48, 0x74, 0x6c, 0x63, 0x73, 0x12, 0x55, 0x0a, - 0x06, 0x61, 0x6e, 0x63, 0x68, 0x6f, 0x72, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x3d, 0x2e, - 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x43, 0x68, 0x61, - 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x46, 0x6f, - 0x72, 0x63, 0x65, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x64, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, - 0x2e, 0x41, 0x6e, 0x63, 0x68, 0x6f, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x06, 0x61, 0x6e, - 0x63, 0x68, 0x6f, 0x72, 0x22, 0x31, 0x0a, 0x0b, 0x41, 0x6e, 0x63, 0x68, 0x6f, 0x72, 0x53, 0x74, - 0x61, 0x74, 0x65, 0x12, 0x09, 0x0a, 0x05, 0x4c, 0x49, 0x4d, 0x42, 0x4f, 0x10, 0x00, 0x12, 0x0d, - 0x0a, 0x09, 0x52, 0x45, 0x43, 0x4f, 0x56, 0x45, 0x52, 0x45, 0x44, 0x10, 0x01, 0x12, 0x08, 0x0a, - 0x04, 0x4c, 0x4f, 0x53, 0x54, 0x10, 0x02, 0x22, 0x1a, 0x0a, 0x18, 0x43, 0x68, 0x61, 0x6e, 0x6e, - 0x65, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, - 0x69, 0x6f, 0x6e, 0x22, 0xff, 0x04, 0x0a, 0x12, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x45, - 0x76, 0x65, 0x6e, 0x74, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x33, 0x0a, 0x0c, 0x6f, 0x70, - 0x65, 0x6e, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x0e, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, - 0x48, 0x00, 0x52, 0x0b, 0x6f, 0x70, 0x65, 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x12, - 0x43, 0x0a, 0x0e, 0x63, 0x6c, 0x6f, 0x73, 0x65, 0x64, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, - 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, - 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x53, 0x75, 0x6d, 0x6d, - 0x61, 0x72, 0x79, 0x48, 0x00, 0x52, 0x0d, 0x63, 0x6c, 0x6f, 0x73, 0x65, 0x64, 0x43, 0x68, 0x61, - 0x6e, 0x6e, 0x65, 0x6c, 0x12, 0x3c, 0x0a, 0x0e, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x63, - 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x6c, - 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x50, 0x6f, 0x69, 0x6e, - 0x74, 0x48, 0x00, 0x52, 0x0d, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x6e, - 0x65, 0x6c, 0x12, 0x40, 0x0a, 0x10, 0x69, 0x6e, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x63, - 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x6c, - 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x50, 0x6f, 0x69, 0x6e, - 0x74, 0x48, 0x00, 0x52, 0x0f, 0x69, 0x6e, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x43, 0x68, 0x61, - 0x6e, 0x6e, 0x65, 0x6c, 0x12, 0x48, 0x0a, 0x14, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x5f, - 0x6f, 0x70, 0x65, 0x6e, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x18, 0x06, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x65, 0x6e, 0x64, 0x69, - 0x6e, 0x67, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x48, 0x00, 0x52, 0x12, 0x70, 0x65, 0x6e, 0x64, - 0x69, 0x6e, 0x67, 0x4f, 0x70, 0x65, 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x12, 0x4b, - 0x0a, 0x16, 0x66, 0x75, 0x6c, 0x6c, 0x79, 0x5f, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x64, - 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, - 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x50, 0x6f, - 0x69, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x14, 0x66, 0x75, 0x6c, 0x6c, 0x79, 0x52, 0x65, 0x73, 0x6f, - 0x6c, 0x76, 0x65, 0x64, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x12, 0x38, 0x0a, 0x04, 0x74, - 0x79, 0x70, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x24, 0x2e, 0x6c, 0x6e, 0x72, 0x70, - 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x55, 0x70, - 0x64, 0x61, 0x74, 0x65, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x79, 0x70, 0x65, 0x52, - 0x04, 0x74, 0x79, 0x70, 0x65, 0x22, 0x92, 0x01, 0x0a, 0x0a, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, - 0x54, 0x79, 0x70, 0x65, 0x12, 0x10, 0x0a, 0x0c, 0x4f, 0x50, 0x45, 0x4e, 0x5f, 0x43, 0x48, 0x41, - 0x4e, 0x4e, 0x45, 0x4c, 0x10, 0x00, 0x12, 0x12, 0x0a, 0x0e, 0x43, 0x4c, 0x4f, 0x53, 0x45, 0x44, - 0x5f, 0x43, 0x48, 0x41, 0x4e, 0x4e, 0x45, 0x4c, 0x10, 0x01, 0x12, 0x12, 0x0a, 0x0e, 0x41, 0x43, - 0x54, 0x49, 0x56, 0x45, 0x5f, 0x43, 0x48, 0x41, 0x4e, 0x4e, 0x45, 0x4c, 0x10, 0x02, 0x12, 0x14, - 0x0a, 0x10, 0x49, 0x4e, 0x41, 0x43, 0x54, 0x49, 0x56, 0x45, 0x5f, 0x43, 0x48, 0x41, 0x4e, 0x4e, - 0x45, 0x4c, 0x10, 0x03, 0x12, 0x18, 0x0a, 0x14, 0x50, 0x45, 0x4e, 0x44, 0x49, 0x4e, 0x47, 0x5f, - 0x4f, 0x50, 0x45, 0x4e, 0x5f, 0x43, 0x48, 0x41, 0x4e, 0x4e, 0x45, 0x4c, 0x10, 0x04, 0x12, 0x1a, - 0x0a, 0x16, 0x46, 0x55, 0x4c, 0x4c, 0x59, 0x5f, 0x52, 0x45, 0x53, 0x4f, 0x4c, 0x56, 0x45, 0x44, - 0x5f, 0x43, 0x48, 0x41, 0x4e, 0x4e, 0x45, 0x4c, 0x10, 0x05, 0x42, 0x09, 0x0a, 0x07, 0x63, 0x68, - 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x22, 0x74, 0x0a, 0x14, 0x57, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x41, - 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x12, 0x2b, 0x0a, - 0x11, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x65, 0x64, 0x5f, 0x62, 0x61, 0x6c, 0x61, 0x6e, - 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x10, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x72, - 0x6d, 0x65, 0x64, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x12, 0x2f, 0x0a, 0x13, 0x75, 0x6e, - 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x65, 0x64, 0x5f, 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, - 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x12, 0x75, 0x6e, 0x63, 0x6f, 0x6e, 0x66, 0x69, - 0x72, 0x6d, 0x65, 0x64, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x22, 0x4d, 0x0a, 0x14, 0x57, - 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x1b, 0x0a, - 0x09, 0x6d, 0x69, 0x6e, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, - 0x52, 0x08, 0x6d, 0x69, 0x6e, 0x43, 0x6f, 0x6e, 0x66, 0x73, 0x22, 0xbd, 0x03, 0x0a, 0x15, 0x57, - 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x62, 0x61, - 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0c, 0x74, 0x6f, 0x74, - 0x61, 0x6c, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x12, 0x2b, 0x0a, 0x11, 0x63, 0x6f, 0x6e, - 0x66, 0x69, 0x72, 0x6d, 0x65, 0x64, 0x5f, 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x03, 0x52, 0x10, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x65, 0x64, 0x42, - 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x12, 0x2f, 0x0a, 0x13, 0x75, 0x6e, 0x63, 0x6f, 0x6e, 0x66, - 0x69, 0x72, 0x6d, 0x65, 0x64, 0x5f, 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x03, 0x52, 0x12, 0x75, 0x6e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x65, 0x64, - 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x6c, 0x6f, 0x63, 0x6b, 0x65, - 0x64, 0x5f, 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, - 0x0d, 0x6c, 0x6f, 0x63, 0x6b, 0x65, 0x64, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x12, 0x3f, - 0x0a, 0x1c, 0x72, 0x65, 0x73, 0x65, 0x72, 0x76, 0x65, 0x64, 0x5f, 0x62, 0x61, 0x6c, 0x61, 0x6e, - 0x63, 0x65, 0x5f, 0x61, 0x6e, 0x63, 0x68, 0x6f, 0x72, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x18, 0x06, - 0x20, 0x01, 0x28, 0x03, 0x52, 0x19, 0x72, 0x65, 0x73, 0x65, 0x72, 0x76, 0x65, 0x64, 0x42, 0x61, - 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x41, 0x6e, 0x63, 0x68, 0x6f, 0x72, 0x43, 0x68, 0x61, 0x6e, 0x12, - 0x59, 0x0a, 0x0f, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x62, 0x61, 0x6c, 0x61, 0x6e, - 0x63, 0x65, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x30, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, - 0x2e, 0x57, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x42, 0x61, - 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0e, 0x61, 0x63, 0x63, 0x6f, - 0x75, 0x6e, 0x74, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x1a, 0x5e, 0x0a, 0x13, 0x41, 0x63, - 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x45, 0x6e, 0x74, 0x72, - 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, - 0x6b, 0x65, 0x79, 0x12, 0x31, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x61, 0x6c, 0x6c, 0x65, - 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x52, - 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x2e, 0x0a, 0x06, 0x41, 0x6d, - 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x73, 0x61, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x04, 0x52, 0x03, 0x73, 0x61, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x04, 0x52, 0x04, 0x6d, 0x73, 0x61, 0x74, 0x22, 0x17, 0x0a, 0x15, 0x43, 0x68, - 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x22, 0xb0, 0x04, 0x0a, 0x16, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x42, - 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1c, - 0x0a, 0x07, 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x42, - 0x02, 0x18, 0x01, 0x52, 0x07, 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x12, 0x34, 0x0a, 0x14, - 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x6f, 0x70, 0x65, 0x6e, 0x5f, 0x62, 0x61, 0x6c, - 0x61, 0x6e, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x42, 0x02, 0x18, 0x01, 0x52, 0x12, - 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x4f, 0x70, 0x65, 0x6e, 0x42, 0x61, 0x6c, 0x61, 0x6e, - 0x63, 0x65, 0x12, 0x32, 0x0a, 0x0d, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x5f, 0x62, 0x61, 0x6c, 0x61, - 0x6e, 0x63, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x6c, 0x6e, 0x72, 0x70, - 0x63, 0x2e, 0x41, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x0c, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x42, - 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x12, 0x34, 0x0a, 0x0e, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, - 0x5f, 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0d, - 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x0d, 0x72, - 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x12, 0x45, 0x0a, 0x17, - 0x75, 0x6e, 0x73, 0x65, 0x74, 0x74, 0x6c, 0x65, 0x64, 0x5f, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x5f, - 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0d, 0x2e, - 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x15, 0x75, 0x6e, - 0x73, 0x65, 0x74, 0x74, 0x6c, 0x65, 0x64, 0x4c, 0x6f, 0x63, 0x61, 0x6c, 0x42, 0x61, 0x6c, 0x61, - 0x6e, 0x63, 0x65, 0x12, 0x47, 0x0a, 0x18, 0x75, 0x6e, 0x73, 0x65, 0x74, 0x74, 0x6c, 0x65, 0x64, - 0x5f, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x5f, 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x18, - 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x6d, - 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x16, 0x75, 0x6e, 0x73, 0x65, 0x74, 0x74, 0x6c, 0x65, 0x64, 0x52, - 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x12, 0x4a, 0x0a, 0x1a, - 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x6f, 0x70, 0x65, 0x6e, 0x5f, 0x6c, 0x6f, 0x63, - 0x61, 0x6c, 0x5f, 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x0d, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x52, - 0x17, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x4f, 0x70, 0x65, 0x6e, 0x4c, 0x6f, 0x63, 0x61, - 0x6c, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x12, 0x4c, 0x0a, 0x1b, 0x70, 0x65, 0x6e, 0x64, - 0x69, 0x6e, 0x67, 0x5f, 0x6f, 0x70, 0x65, 0x6e, 0x5f, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x5f, - 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0d, 0x2e, - 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x18, 0x70, 0x65, - 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x4f, 0x70, 0x65, 0x6e, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x42, - 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x12, 0x2e, 0x0a, 0x13, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, - 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x18, 0x09, 0x20, - 0x01, 0x28, 0x0c, 0x52, 0x11, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x43, 0x68, 0x61, 0x6e, 0x6e, - 0x65, 0x6c, 0x44, 0x61, 0x74, 0x61, 0x22, 0x9a, 0x07, 0x0a, 0x12, 0x51, 0x75, 0x65, 0x72, 0x79, - 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x17, 0x0a, - 0x07, 0x70, 0x75, 0x62, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, - 0x70, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x6d, 0x74, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x03, 0x52, 0x03, 0x61, 0x6d, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x61, 0x6d, 0x74, 0x5f, - 0x6d, 0x73, 0x61, 0x74, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x61, 0x6d, 0x74, 0x4d, - 0x73, 0x61, 0x74, 0x12, 0x28, 0x0a, 0x10, 0x66, 0x69, 0x6e, 0x61, 0x6c, 0x5f, 0x63, 0x6c, 0x74, - 0x76, 0x5f, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0e, 0x66, - 0x69, 0x6e, 0x61, 0x6c, 0x43, 0x6c, 0x74, 0x76, 0x44, 0x65, 0x6c, 0x74, 0x61, 0x12, 0x2c, 0x0a, - 0x09, 0x66, 0x65, 0x65, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x0f, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x65, 0x65, 0x4c, 0x69, 0x6d, 0x69, - 0x74, 0x52, 0x08, 0x66, 0x65, 0x65, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x23, 0x0a, 0x0d, 0x69, - 0x67, 0x6e, 0x6f, 0x72, 0x65, 0x64, 0x5f, 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x18, 0x06, 0x20, 0x03, - 0x28, 0x0c, 0x52, 0x0c, 0x69, 0x67, 0x6e, 0x6f, 0x72, 0x65, 0x64, 0x4e, 0x6f, 0x64, 0x65, 0x73, - 0x12, 0x3b, 0x0a, 0x0d, 0x69, 0x67, 0x6e, 0x6f, 0x72, 0x65, 0x64, 0x5f, 0x65, 0x64, 0x67, 0x65, - 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, - 0x45, 0x64, 0x67, 0x65, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x6f, 0x72, 0x42, 0x02, 0x18, 0x01, 0x52, - 0x0c, 0x69, 0x67, 0x6e, 0x6f, 0x72, 0x65, 0x64, 0x45, 0x64, 0x67, 0x65, 0x73, 0x12, 0x24, 0x0a, - 0x0e, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x70, 0x75, 0x62, 0x5f, 0x6b, 0x65, 0x79, 0x18, - 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x50, 0x75, 0x62, - 0x4b, 0x65, 0x79, 0x12, 0x2e, 0x0a, 0x13, 0x75, 0x73, 0x65, 0x5f, 0x6d, 0x69, 0x73, 0x73, 0x69, - 0x6f, 0x6e, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x18, 0x09, 0x20, 0x01, 0x28, 0x08, - 0x52, 0x11, 0x75, 0x73, 0x65, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x6e, 0x74, - 0x72, 0x6f, 0x6c, 0x12, 0x34, 0x0a, 0x0d, 0x69, 0x67, 0x6e, 0x6f, 0x72, 0x65, 0x64, 0x5f, 0x70, - 0x61, 0x69, 0x72, 0x73, 0x18, 0x0a, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x6c, 0x6e, 0x72, - 0x70, 0x63, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x50, 0x61, 0x69, 0x72, 0x52, 0x0c, 0x69, 0x67, 0x6e, - 0x6f, 0x72, 0x65, 0x64, 0x50, 0x61, 0x69, 0x72, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6c, 0x74, - 0x76, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x63, - 0x6c, 0x74, 0x76, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x60, 0x0a, 0x13, 0x64, 0x65, 0x73, 0x74, - 0x5f, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5f, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x18, - 0x0d, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x30, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x51, 0x75, - 0x65, 0x72, 0x79, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x2e, 0x44, 0x65, 0x73, 0x74, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x52, 0x65, 0x63, 0x6f, 0x72, - 0x64, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x11, 0x64, 0x65, 0x73, 0x74, 0x43, 0x75, 0x73, - 0x74, 0x6f, 0x6d, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x12, 0x2c, 0x0a, 0x10, 0x6f, 0x75, - 0x74, 0x67, 0x6f, 0x69, 0x6e, 0x67, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x0e, - 0x20, 0x01, 0x28, 0x04, 0x42, 0x02, 0x30, 0x01, 0x52, 0x0e, 0x6f, 0x75, 0x74, 0x67, 0x6f, 0x69, - 0x6e, 0x67, 0x43, 0x68, 0x61, 0x6e, 0x49, 0x64, 0x12, 0x26, 0x0a, 0x0f, 0x6c, 0x61, 0x73, 0x74, - 0x5f, 0x68, 0x6f, 0x70, 0x5f, 0x70, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x18, 0x0f, 0x20, 0x01, 0x28, - 0x0c, 0x52, 0x0d, 0x6c, 0x61, 0x73, 0x74, 0x48, 0x6f, 0x70, 0x50, 0x75, 0x62, 0x6b, 0x65, 0x79, - 0x12, 0x31, 0x0a, 0x0b, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x5f, 0x68, 0x69, 0x6e, 0x74, 0x73, 0x18, - 0x10, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x6f, - 0x75, 0x74, 0x65, 0x48, 0x69, 0x6e, 0x74, 0x52, 0x0a, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x48, 0x69, - 0x6e, 0x74, 0x73, 0x12, 0x4d, 0x0a, 0x15, 0x62, 0x6c, 0x69, 0x6e, 0x64, 0x65, 0x64, 0x5f, 0x70, - 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x73, 0x18, 0x13, 0x20, 0x03, - 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x42, 0x6c, 0x69, 0x6e, 0x64, - 0x65, 0x64, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x50, 0x61, 0x74, 0x68, 0x52, 0x13, 0x62, - 0x6c, 0x69, 0x6e, 0x64, 0x65, 0x64, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x50, 0x61, 0x74, - 0x68, 0x73, 0x12, 0x36, 0x0a, 0x0d, 0x64, 0x65, 0x73, 0x74, 0x5f, 0x66, 0x65, 0x61, 0x74, 0x75, - 0x72, 0x65, 0x73, 0x18, 0x11, 0x20, 0x03, 0x28, 0x0e, 0x32, 0x11, 0x2e, 0x6c, 0x6e, 0x72, 0x70, - 0x63, 0x2e, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x42, 0x69, 0x74, 0x52, 0x0c, 0x64, 0x65, - 0x73, 0x74, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x69, - 0x6d, 0x65, 0x5f, 0x70, 0x72, 0x65, 0x66, 0x18, 0x12, 0x20, 0x01, 0x28, 0x01, 0x52, 0x08, 0x74, - 0x69, 0x6d, 0x65, 0x50, 0x72, 0x65, 0x66, 0x1a, 0x44, 0x0a, 0x16, 0x44, 0x65, 0x73, 0x74, 0x43, + 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x46, + 0x6f, 0x72, 0x63, 0x65, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x64, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, + 0x6c, 0x2e, 0x41, 0x6e, 0x63, 0x68, 0x6f, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x06, 0x61, + 0x6e, 0x63, 0x68, 0x6f, 0x72, 0x22, 0x31, 0x0a, 0x0b, 0x41, 0x6e, 0x63, 0x68, 0x6f, 0x72, 0x53, + 0x74, 0x61, 0x74, 0x65, 0x12, 0x09, 0x0a, 0x05, 0x4c, 0x49, 0x4d, 0x42, 0x4f, 0x10, 0x00, 0x12, + 0x0d, 0x0a, 0x09, 0x52, 0x45, 0x43, 0x4f, 0x56, 0x45, 0x52, 0x45, 0x44, 0x10, 0x01, 0x12, 0x08, + 0x0a, 0x04, 0x4c, 0x4f, 0x53, 0x54, 0x10, 0x02, 0x22, 0x1a, 0x0a, 0x18, 0x43, 0x68, 0x61, 0x6e, + 0x6e, 0x65, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, + 0x74, 0x69, 0x6f, 0x6e, 0x22, 0xff, 0x04, 0x0a, 0x12, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, + 0x45, 0x76, 0x65, 0x6e, 0x74, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x33, 0x0a, 0x0c, 0x6f, + 0x70, 0x65, 0x6e, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x0e, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, + 0x6c, 0x48, 0x00, 0x52, 0x0b, 0x6f, 0x70, 0x65, 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, + 0x12, 0x43, 0x0a, 0x0e, 0x63, 0x6c, 0x6f, 0x73, 0x65, 0x64, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x6e, + 0x65, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, + 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x53, 0x75, 0x6d, + 0x6d, 0x61, 0x72, 0x79, 0x48, 0x00, 0x52, 0x0d, 0x63, 0x6c, 0x6f, 0x73, 0x65, 0x64, 0x43, 0x68, + 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x12, 0x3c, 0x0a, 0x0e, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x5f, + 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, + 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x50, 0x6f, 0x69, + 0x6e, 0x74, 0x48, 0x00, 0x52, 0x0d, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x43, 0x68, 0x61, 0x6e, + 0x6e, 0x65, 0x6c, 0x12, 0x40, 0x0a, 0x10, 0x69, 0x6e, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x5f, + 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, + 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x50, 0x6f, 0x69, + 0x6e, 0x74, 0x48, 0x00, 0x52, 0x0f, 0x69, 0x6e, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x43, 0x68, + 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x12, 0x48, 0x0a, 0x14, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, + 0x5f, 0x6f, 0x70, 0x65, 0x6e, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x18, 0x06, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x65, 0x6e, 0x64, + 0x69, 0x6e, 0x67, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x48, 0x00, 0x52, 0x12, 0x70, 0x65, 0x6e, + 0x64, 0x69, 0x6e, 0x67, 0x4f, 0x70, 0x65, 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x12, + 0x4b, 0x0a, 0x16, 0x66, 0x75, 0x6c, 0x6c, 0x79, 0x5f, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, + 0x64, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x13, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x50, + 0x6f, 0x69, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x14, 0x66, 0x75, 0x6c, 0x6c, 0x79, 0x52, 0x65, 0x73, + 0x6f, 0x6c, 0x76, 0x65, 0x64, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x12, 0x38, 0x0a, 0x04, + 0x74, 0x79, 0x70, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x24, 0x2e, 0x6c, 0x6e, 0x72, + 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x55, + 0x70, 0x64, 0x61, 0x74, 0x65, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x79, 0x70, 0x65, + 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x22, 0x92, 0x01, 0x0a, 0x0a, 0x55, 0x70, 0x64, 0x61, 0x74, + 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x10, 0x0a, 0x0c, 0x4f, 0x50, 0x45, 0x4e, 0x5f, 0x43, 0x48, + 0x41, 0x4e, 0x4e, 0x45, 0x4c, 0x10, 0x00, 0x12, 0x12, 0x0a, 0x0e, 0x43, 0x4c, 0x4f, 0x53, 0x45, + 0x44, 0x5f, 0x43, 0x48, 0x41, 0x4e, 0x4e, 0x45, 0x4c, 0x10, 0x01, 0x12, 0x12, 0x0a, 0x0e, 0x41, + 0x43, 0x54, 0x49, 0x56, 0x45, 0x5f, 0x43, 0x48, 0x41, 0x4e, 0x4e, 0x45, 0x4c, 0x10, 0x02, 0x12, + 0x14, 0x0a, 0x10, 0x49, 0x4e, 0x41, 0x43, 0x54, 0x49, 0x56, 0x45, 0x5f, 0x43, 0x48, 0x41, 0x4e, + 0x4e, 0x45, 0x4c, 0x10, 0x03, 0x12, 0x18, 0x0a, 0x14, 0x50, 0x45, 0x4e, 0x44, 0x49, 0x4e, 0x47, + 0x5f, 0x4f, 0x50, 0x45, 0x4e, 0x5f, 0x43, 0x48, 0x41, 0x4e, 0x4e, 0x45, 0x4c, 0x10, 0x04, 0x12, + 0x1a, 0x0a, 0x16, 0x46, 0x55, 0x4c, 0x4c, 0x59, 0x5f, 0x52, 0x45, 0x53, 0x4f, 0x4c, 0x56, 0x45, + 0x44, 0x5f, 0x43, 0x48, 0x41, 0x4e, 0x4e, 0x45, 0x4c, 0x10, 0x05, 0x42, 0x09, 0x0a, 0x07, 0x63, + 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x22, 0x74, 0x0a, 0x14, 0x57, 0x61, 0x6c, 0x6c, 0x65, 0x74, + 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x12, 0x2b, + 0x0a, 0x11, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x65, 0x64, 0x5f, 0x62, 0x61, 0x6c, 0x61, + 0x6e, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x10, 0x63, 0x6f, 0x6e, 0x66, 0x69, + 0x72, 0x6d, 0x65, 0x64, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x12, 0x2f, 0x0a, 0x13, 0x75, + 0x6e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x65, 0x64, 0x5f, 0x62, 0x61, 0x6c, 0x61, 0x6e, + 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x12, 0x75, 0x6e, 0x63, 0x6f, 0x6e, 0x66, + 0x69, 0x72, 0x6d, 0x65, 0x64, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x22, 0x4d, 0x0a, 0x14, + 0x57, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x1b, + 0x0a, 0x09, 0x6d, 0x69, 0x6e, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x05, 0x52, 0x08, 0x6d, 0x69, 0x6e, 0x43, 0x6f, 0x6e, 0x66, 0x73, 0x22, 0xbd, 0x03, 0x0a, 0x15, + 0x57, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x62, + 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0c, 0x74, 0x6f, + 0x74, 0x61, 0x6c, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x12, 0x2b, 0x0a, 0x11, 0x63, 0x6f, + 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x65, 0x64, 0x5f, 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x10, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x65, 0x64, + 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x12, 0x2f, 0x0a, 0x13, 0x75, 0x6e, 0x63, 0x6f, 0x6e, + 0x66, 0x69, 0x72, 0x6d, 0x65, 0x64, 0x5f, 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x03, 0x52, 0x12, 0x75, 0x6e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x65, + 0x64, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x6c, 0x6f, 0x63, 0x6b, + 0x65, 0x64, 0x5f, 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, + 0x52, 0x0d, 0x6c, 0x6f, 0x63, 0x6b, 0x65, 0x64, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x12, + 0x3f, 0x0a, 0x1c, 0x72, 0x65, 0x73, 0x65, 0x72, 0x76, 0x65, 0x64, 0x5f, 0x62, 0x61, 0x6c, 0x61, + 0x6e, 0x63, 0x65, 0x5f, 0x61, 0x6e, 0x63, 0x68, 0x6f, 0x72, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x18, + 0x06, 0x20, 0x01, 0x28, 0x03, 0x52, 0x19, 0x72, 0x65, 0x73, 0x65, 0x72, 0x76, 0x65, 0x64, 0x42, + 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x41, 0x6e, 0x63, 0x68, 0x6f, 0x72, 0x43, 0x68, 0x61, 0x6e, + 0x12, 0x59, 0x0a, 0x0f, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x62, 0x61, 0x6c, 0x61, + 0x6e, 0x63, 0x65, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x30, 0x2e, 0x6c, 0x6e, 0x72, 0x70, + 0x63, 0x2e, 0x57, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x42, + 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0e, 0x61, 0x63, 0x63, + 0x6f, 0x75, 0x6e, 0x74, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x1a, 0x5e, 0x0a, 0x13, 0x41, + 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x45, 0x6e, 0x74, + 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x03, 0x6b, 0x65, 0x79, 0x12, 0x31, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x61, 0x6c, 0x6c, + 0x65, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, + 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x2e, 0x0a, 0x06, 0x41, + 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x73, 0x61, 0x74, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x04, 0x52, 0x03, 0x73, 0x61, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6d, 0x73, 0x61, 0x74, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x04, 0x6d, 0x73, 0x61, 0x74, 0x22, 0x17, 0x0a, 0x15, 0x43, + 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x22, 0xb0, 0x04, 0x0a, 0x16, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, + 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x1c, 0x0a, 0x07, 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, + 0x42, 0x02, 0x18, 0x01, 0x52, 0x07, 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x12, 0x34, 0x0a, + 0x14, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x6f, 0x70, 0x65, 0x6e, 0x5f, 0x62, 0x61, + 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x42, 0x02, 0x18, 0x01, 0x52, + 0x12, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x4f, 0x70, 0x65, 0x6e, 0x42, 0x61, 0x6c, 0x61, + 0x6e, 0x63, 0x65, 0x12, 0x32, 0x0a, 0x0d, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x5f, 0x62, 0x61, 0x6c, + 0x61, 0x6e, 0x63, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x6c, 0x6e, 0x72, + 0x70, 0x63, 0x2e, 0x41, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x0c, 0x6c, 0x6f, 0x63, 0x61, 0x6c, + 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x12, 0x34, 0x0a, 0x0e, 0x72, 0x65, 0x6d, 0x6f, 0x74, + 0x65, 0x5f, 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x0d, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x0d, + 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x12, 0x45, 0x0a, + 0x17, 0x75, 0x6e, 0x73, 0x65, 0x74, 0x74, 0x6c, 0x65, 0x64, 0x5f, 0x6c, 0x6f, 0x63, 0x61, 0x6c, + 0x5f, 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0d, + 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x15, 0x75, + 0x6e, 0x73, 0x65, 0x74, 0x74, 0x6c, 0x65, 0x64, 0x4c, 0x6f, 0x63, 0x61, 0x6c, 0x42, 0x61, 0x6c, + 0x61, 0x6e, 0x63, 0x65, 0x12, 0x47, 0x0a, 0x18, 0x75, 0x6e, 0x73, 0x65, 0x74, 0x74, 0x6c, 0x65, + 0x64, 0x5f, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x5f, 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, + 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x41, + 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x16, 0x75, 0x6e, 0x73, 0x65, 0x74, 0x74, 0x6c, 0x65, 0x64, + 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x12, 0x4a, 0x0a, + 0x1a, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x6f, 0x70, 0x65, 0x6e, 0x5f, 0x6c, 0x6f, + 0x63, 0x61, 0x6c, 0x5f, 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x0d, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x6d, 0x6f, 0x75, 0x6e, 0x74, + 0x52, 0x17, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x4f, 0x70, 0x65, 0x6e, 0x4c, 0x6f, 0x63, + 0x61, 0x6c, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x12, 0x4c, 0x0a, 0x1b, 0x70, 0x65, 0x6e, + 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x6f, 0x70, 0x65, 0x6e, 0x5f, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, + 0x5f, 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0d, + 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x18, 0x70, + 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x4f, 0x70, 0x65, 0x6e, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, + 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x12, 0x2e, 0x0a, 0x13, 0x63, 0x75, 0x73, 0x74, 0x6f, + 0x6d, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x18, 0x09, + 0x20, 0x01, 0x28, 0x0c, 0x52, 0x11, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x43, 0x68, 0x61, 0x6e, + 0x6e, 0x65, 0x6c, 0x44, 0x61, 0x74, 0x61, 0x22, 0x9a, 0x07, 0x0a, 0x12, 0x51, 0x75, 0x65, 0x72, + 0x79, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x17, + 0x0a, 0x07, 0x70, 0x75, 0x62, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x06, 0x70, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x6d, 0x74, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x03, 0x52, 0x03, 0x61, 0x6d, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x61, 0x6d, 0x74, + 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x61, 0x6d, 0x74, + 0x4d, 0x73, 0x61, 0x74, 0x12, 0x28, 0x0a, 0x10, 0x66, 0x69, 0x6e, 0x61, 0x6c, 0x5f, 0x63, 0x6c, + 0x74, 0x76, 0x5f, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0e, + 0x66, 0x69, 0x6e, 0x61, 0x6c, 0x43, 0x6c, 0x74, 0x76, 0x44, 0x65, 0x6c, 0x74, 0x61, 0x12, 0x2c, + 0x0a, 0x09, 0x66, 0x65, 0x65, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x0f, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x65, 0x65, 0x4c, 0x69, 0x6d, + 0x69, 0x74, 0x52, 0x08, 0x66, 0x65, 0x65, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x23, 0x0a, 0x0d, + 0x69, 0x67, 0x6e, 0x6f, 0x72, 0x65, 0x64, 0x5f, 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x18, 0x06, 0x20, + 0x03, 0x28, 0x0c, 0x52, 0x0c, 0x69, 0x67, 0x6e, 0x6f, 0x72, 0x65, 0x64, 0x4e, 0x6f, 0x64, 0x65, + 0x73, 0x12, 0x3b, 0x0a, 0x0d, 0x69, 0x67, 0x6e, 0x6f, 0x72, 0x65, 0x64, 0x5f, 0x65, 0x64, 0x67, + 0x65, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, + 0x2e, 0x45, 0x64, 0x67, 0x65, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x6f, 0x72, 0x42, 0x02, 0x18, 0x01, + 0x52, 0x0c, 0x69, 0x67, 0x6e, 0x6f, 0x72, 0x65, 0x64, 0x45, 0x64, 0x67, 0x65, 0x73, 0x12, 0x24, + 0x0a, 0x0e, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x70, 0x75, 0x62, 0x5f, 0x6b, 0x65, 0x79, + 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x50, 0x75, + 0x62, 0x4b, 0x65, 0x79, 0x12, 0x2e, 0x0a, 0x13, 0x75, 0x73, 0x65, 0x5f, 0x6d, 0x69, 0x73, 0x73, + 0x69, 0x6f, 0x6e, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x18, 0x09, 0x20, 0x01, 0x28, + 0x08, 0x52, 0x11, 0x75, 0x73, 0x65, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x6e, + 0x74, 0x72, 0x6f, 0x6c, 0x12, 0x34, 0x0a, 0x0d, 0x69, 0x67, 0x6e, 0x6f, 0x72, 0x65, 0x64, 0x5f, + 0x70, 0x61, 0x69, 0x72, 0x73, 0x18, 0x0a, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x6c, 0x6e, + 0x72, 0x70, 0x63, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x50, 0x61, 0x69, 0x72, 0x52, 0x0c, 0x69, 0x67, + 0x6e, 0x6f, 0x72, 0x65, 0x64, 0x50, 0x61, 0x69, 0x72, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6c, + 0x74, 0x76, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, + 0x63, 0x6c, 0x74, 0x76, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x60, 0x0a, 0x13, 0x64, 0x65, 0x73, + 0x74, 0x5f, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5f, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, + 0x18, 0x0d, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x30, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x51, + 0x75, 0x65, 0x72, 0x79, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x2e, 0x44, 0x65, 0x73, 0x74, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x52, 0x65, 0x63, 0x6f, + 0x72, 0x64, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x11, 0x64, 0x65, 0x73, 0x74, 0x43, 0x75, + 0x73, 0x74, 0x6f, 0x6d, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x12, 0x2c, 0x0a, 0x10, 0x6f, + 0x75, 0x74, 0x67, 0x6f, 0x69, 0x6e, 0x67, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x5f, 0x69, 0x64, 0x18, + 0x0e, 0x20, 0x01, 0x28, 0x04, 0x42, 0x02, 0x30, 0x01, 0x52, 0x0e, 0x6f, 0x75, 0x74, 0x67, 0x6f, + 0x69, 0x6e, 0x67, 0x43, 0x68, 0x61, 0x6e, 0x49, 0x64, 0x12, 0x26, 0x0a, 0x0f, 0x6c, 0x61, 0x73, + 0x74, 0x5f, 0x68, 0x6f, 0x70, 0x5f, 0x70, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x18, 0x0f, 0x20, 0x01, + 0x28, 0x0c, 0x52, 0x0d, 0x6c, 0x61, 0x73, 0x74, 0x48, 0x6f, 0x70, 0x50, 0x75, 0x62, 0x6b, 0x65, + 0x79, 0x12, 0x31, 0x0a, 0x0b, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x5f, 0x68, 0x69, 0x6e, 0x74, 0x73, + 0x18, 0x10, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x52, + 0x6f, 0x75, 0x74, 0x65, 0x48, 0x69, 0x6e, 0x74, 0x52, 0x0a, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x48, + 0x69, 0x6e, 0x74, 0x73, 0x12, 0x4d, 0x0a, 0x15, 0x62, 0x6c, 0x69, 0x6e, 0x64, 0x65, 0x64, 0x5f, + 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x73, 0x18, 0x13, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x42, 0x6c, 0x69, 0x6e, + 0x64, 0x65, 0x64, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x50, 0x61, 0x74, 0x68, 0x52, 0x13, + 0x62, 0x6c, 0x69, 0x6e, 0x64, 0x65, 0x64, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x50, 0x61, + 0x74, 0x68, 0x73, 0x12, 0x36, 0x0a, 0x0d, 0x64, 0x65, 0x73, 0x74, 0x5f, 0x66, 0x65, 0x61, 0x74, + 0x75, 0x72, 0x65, 0x73, 0x18, 0x11, 0x20, 0x03, 0x28, 0x0e, 0x32, 0x11, 0x2e, 0x6c, 0x6e, 0x72, + 0x70, 0x63, 0x2e, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x42, 0x69, 0x74, 0x52, 0x0c, 0x64, + 0x65, 0x73, 0x74, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x74, + 0x69, 0x6d, 0x65, 0x5f, 0x70, 0x72, 0x65, 0x66, 0x18, 0x12, 0x20, 0x01, 0x28, 0x01, 0x52, 0x08, + 0x74, 0x69, 0x6d, 0x65, 0x50, 0x72, 0x65, 0x66, 0x1a, 0x44, 0x0a, 0x16, 0x44, 0x65, 0x73, 0x74, + 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x45, 0x6e, 0x74, + 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, + 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0c, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x4a, 0x04, + 0x08, 0x03, 0x10, 0x04, 0x22, 0x2e, 0x0a, 0x08, 0x4e, 0x6f, 0x64, 0x65, 0x50, 0x61, 0x69, 0x72, + 0x12, 0x12, 0x0a, 0x04, 0x66, 0x72, 0x6f, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, + 0x66, 0x72, 0x6f, 0x6d, 0x12, 0x0e, 0x0a, 0x02, 0x74, 0x6f, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, + 0x52, 0x02, 0x74, 0x6f, 0x22, 0x5d, 0x0a, 0x0b, 0x45, 0x64, 0x67, 0x65, 0x4c, 0x6f, 0x63, 0x61, + 0x74, 0x6f, 0x72, 0x12, 0x21, 0x0a, 0x0a, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x5f, 0x69, + 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x42, 0x02, 0x30, 0x01, 0x52, 0x09, 0x63, 0x68, 0x61, + 0x6e, 0x6e, 0x65, 0x6c, 0x49, 0x64, 0x12, 0x2b, 0x0a, 0x11, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x5f, 0x72, 0x65, 0x76, 0x65, 0x72, 0x73, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x08, 0x52, 0x10, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x76, 0x65, + 0x72, 0x73, 0x65, 0x22, 0x5e, 0x0a, 0x13, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x6f, 0x75, 0x74, + 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x24, 0x0a, 0x06, 0x72, 0x6f, + 0x75, 0x74, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x6c, 0x6e, 0x72, + 0x70, 0x63, 0x2e, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x52, 0x06, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x73, + 0x12, 0x21, 0x0a, 0x0c, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x70, 0x72, 0x6f, 0x62, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x01, 0x52, 0x0b, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x50, + 0x72, 0x6f, 0x62, 0x22, 0xa5, 0x05, 0x0a, 0x03, 0x48, 0x6f, 0x70, 0x12, 0x1b, 0x0a, 0x07, 0x63, + 0x68, 0x61, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x42, 0x02, 0x30, 0x01, + 0x52, 0x06, 0x63, 0x68, 0x61, 0x6e, 0x49, 0x64, 0x12, 0x27, 0x0a, 0x0d, 0x63, 0x68, 0x61, 0x6e, + 0x5f, 0x63, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x42, + 0x02, 0x18, 0x01, 0x52, 0x0c, 0x63, 0x68, 0x61, 0x6e, 0x43, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, + 0x79, 0x12, 0x28, 0x0a, 0x0e, 0x61, 0x6d, 0x74, 0x5f, 0x74, 0x6f, 0x5f, 0x66, 0x6f, 0x72, 0x77, + 0x61, 0x72, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x42, 0x02, 0x18, 0x01, 0x52, 0x0c, 0x61, + 0x6d, 0x74, 0x54, 0x6f, 0x46, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x12, 0x14, 0x0a, 0x03, 0x66, + 0x65, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x42, 0x02, 0x18, 0x01, 0x52, 0x03, 0x66, 0x65, + 0x65, 0x12, 0x16, 0x0a, 0x06, 0x65, 0x78, 0x70, 0x69, 0x72, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, + 0x0d, 0x52, 0x06, 0x65, 0x78, 0x70, 0x69, 0x72, 0x79, 0x12, 0x2d, 0x0a, 0x13, 0x61, 0x6d, 0x74, + 0x5f, 0x74, 0x6f, 0x5f, 0x66, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x5f, 0x6d, 0x73, 0x61, 0x74, + 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, 0x52, 0x10, 0x61, 0x6d, 0x74, 0x54, 0x6f, 0x46, 0x6f, 0x72, + 0x77, 0x61, 0x72, 0x64, 0x4d, 0x73, 0x61, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x66, 0x65, 0x65, 0x5f, + 0x6d, 0x73, 0x61, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x66, 0x65, 0x65, 0x4d, + 0x73, 0x61, 0x74, 0x12, 0x17, 0x0a, 0x07, 0x70, 0x75, 0x62, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x08, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x70, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x12, 0x23, 0x0a, 0x0b, + 0x74, 0x6c, 0x76, 0x5f, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x09, 0x20, 0x01, 0x28, + 0x08, 0x42, 0x02, 0x18, 0x01, 0x52, 0x0a, 0x74, 0x6c, 0x76, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, + 0x64, 0x12, 0x2f, 0x0a, 0x0a, 0x6d, 0x70, 0x70, 0x5f, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x18, + 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4d, 0x50, + 0x50, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x52, 0x09, 0x6d, 0x70, 0x70, 0x52, 0x65, 0x63, 0x6f, + 0x72, 0x64, 0x12, 0x2f, 0x0a, 0x0a, 0x61, 0x6d, 0x70, 0x5f, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, + 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x41, + 0x4d, 0x50, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x52, 0x09, 0x61, 0x6d, 0x70, 0x52, 0x65, 0x63, + 0x6f, 0x72, 0x64, 0x12, 0x44, 0x0a, 0x0e, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5f, 0x72, 0x65, + 0x63, 0x6f, 0x72, 0x64, 0x73, 0x18, 0x0b, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x6c, 0x6e, + 0x72, 0x70, 0x63, 0x2e, 0x48, 0x6f, 0x70, 0x2e, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x52, 0x65, + 0x63, 0x6f, 0x72, 0x64, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0d, 0x63, 0x75, 0x73, 0x74, + 0x6f, 0x6d, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x6d, 0x65, 0x74, + 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x6d, 0x65, 0x74, + 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x25, 0x0a, 0x0e, 0x62, 0x6c, 0x69, 0x6e, 0x64, 0x69, 0x6e, + 0x67, 0x5f, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0d, 0x62, + 0x6c, 0x69, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x25, 0x0a, 0x0e, + 0x65, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x18, 0x0f, + 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0d, 0x65, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x44, + 0x61, 0x74, 0x61, 0x12, 0x24, 0x0a, 0x0e, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x61, 0x6d, 0x74, + 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x10, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0c, 0x74, 0x6f, 0x74, + 0x61, 0x6c, 0x41, 0x6d, 0x74, 0x4d, 0x73, 0x61, 0x74, 0x1a, 0x40, 0x0a, 0x12, 0x43, 0x75, 0x73, + 0x74, 0x6f, 0x6d, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, + 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x03, 0x6b, 0x65, + 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, + 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x54, 0x0a, 0x09, 0x4d, + 0x50, 0x50, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x61, 0x79, 0x6d, + 0x65, 0x6e, 0x74, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, + 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x41, 0x64, 0x64, 0x72, 0x12, 0x24, 0x0a, 0x0e, 0x74, + 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x61, 0x6d, 0x74, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x0a, 0x20, + 0x01, 0x28, 0x03, 0x52, 0x0c, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x41, 0x6d, 0x74, 0x4d, 0x73, 0x61, + 0x74, 0x22, 0x62, 0x0a, 0x09, 0x41, 0x4d, 0x50, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x12, 0x1d, + 0x0a, 0x0a, 0x72, 0x6f, 0x6f, 0x74, 0x5f, 0x73, 0x68, 0x61, 0x72, 0x65, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0c, 0x52, 0x09, 0x72, 0x6f, 0x6f, 0x74, 0x53, 0x68, 0x61, 0x72, 0x65, 0x12, 0x15, 0x0a, + 0x06, 0x73, 0x65, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x73, + 0x65, 0x74, 0x49, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x63, 0x68, 0x69, 0x6c, 0x64, 0x5f, 0x69, 0x6e, + 0x64, 0x65, 0x78, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x63, 0x68, 0x69, 0x6c, 0x64, + 0x49, 0x6e, 0x64, 0x65, 0x78, 0x22, 0xc4, 0x02, 0x0a, 0x05, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x12, + 0x26, 0x0a, 0x0f, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x6c, 0x6f, + 0x63, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0d, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x54, + 0x69, 0x6d, 0x65, 0x4c, 0x6f, 0x63, 0x6b, 0x12, 0x21, 0x0a, 0x0a, 0x74, 0x6f, 0x74, 0x61, 0x6c, + 0x5f, 0x66, 0x65, 0x65, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x42, 0x02, 0x18, 0x01, 0x52, + 0x09, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x46, 0x65, 0x65, 0x73, 0x12, 0x1f, 0x0a, 0x09, 0x74, 0x6f, + 0x74, 0x61, 0x6c, 0x5f, 0x61, 0x6d, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x42, 0x02, 0x18, + 0x01, 0x52, 0x08, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x41, 0x6d, 0x74, 0x12, 0x1e, 0x0a, 0x04, 0x68, + 0x6f, 0x70, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, + 0x63, 0x2e, 0x48, 0x6f, 0x70, 0x52, 0x04, 0x68, 0x6f, 0x70, 0x73, 0x12, 0x26, 0x0a, 0x0f, 0x74, + 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x66, 0x65, 0x65, 0x73, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x05, + 0x20, 0x01, 0x28, 0x03, 0x52, 0x0d, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x46, 0x65, 0x65, 0x73, 0x4d, + 0x73, 0x61, 0x74, 0x12, 0x24, 0x0a, 0x0e, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x61, 0x6d, 0x74, + 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0c, 0x74, 0x6f, 0x74, + 0x61, 0x6c, 0x41, 0x6d, 0x74, 0x4d, 0x73, 0x61, 0x74, 0x12, 0x31, 0x0a, 0x15, 0x66, 0x69, 0x72, + 0x73, 0x74, 0x5f, 0x68, 0x6f, 0x70, 0x5f, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x6d, 0x73, + 0x61, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x03, 0x52, 0x12, 0x66, 0x69, 0x72, 0x73, 0x74, 0x48, + 0x6f, 0x70, 0x41, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x4d, 0x73, 0x61, 0x74, 0x12, 0x2e, 0x0a, 0x13, + 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x5f, 0x64, + 0x61, 0x74, 0x61, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x11, 0x63, 0x75, 0x73, 0x74, 0x6f, + 0x6d, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x44, 0x61, 0x74, 0x61, 0x22, 0x55, 0x0a, 0x0f, + 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, + 0x17, 0x0a, 0x07, 0x70, 0x75, 0x62, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x06, 0x70, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x12, 0x29, 0x0a, 0x10, 0x69, 0x6e, 0x63, 0x6c, + 0x75, 0x64, 0x65, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x0f, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x6e, + 0x65, 0x6c, 0x73, 0x22, 0xae, 0x01, 0x0a, 0x08, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, + 0x12, 0x28, 0x0a, 0x04, 0x6e, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, + 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x67, 0x68, 0x74, 0x6e, 0x69, 0x6e, 0x67, + 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x04, 0x6e, 0x6f, 0x64, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x6e, 0x75, + 0x6d, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, + 0x52, 0x0b, 0x6e, 0x75, 0x6d, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x12, 0x25, 0x0a, + 0x0e, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x63, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0d, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x43, 0x61, 0x70, 0x61, + 0x63, 0x69, 0x74, 0x79, 0x12, 0x2e, 0x0a, 0x08, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, + 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, + 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x45, 0x64, 0x67, 0x65, 0x52, 0x08, 0x63, 0x68, 0x61, 0x6e, + 0x6e, 0x65, 0x6c, 0x73, 0x22, 0xc6, 0x03, 0x0a, 0x0d, 0x4c, 0x69, 0x67, 0x68, 0x74, 0x6e, 0x69, + 0x6e, 0x67, 0x4e, 0x6f, 0x64, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x75, + 0x70, 0x64, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x6c, 0x61, 0x73, + 0x74, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x17, 0x0a, 0x07, 0x70, 0x75, 0x62, 0x5f, 0x6b, + 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x70, 0x75, 0x62, 0x4b, 0x65, 0x79, + 0x12, 0x14, 0x0a, 0x05, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x05, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x12, 0x30, 0x0a, 0x09, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, + 0x73, 0x65, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x6c, 0x6e, 0x72, 0x70, + 0x63, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x09, 0x61, + 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x6f, 0x6c, 0x6f, + 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x12, 0x3e, + 0x0a, 0x08, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x22, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x67, 0x68, 0x74, 0x6e, 0x69, + 0x6e, 0x67, 0x4e, 0x6f, 0x64, 0x65, 0x2e, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x45, + 0x6e, 0x74, 0x72, 0x79, 0x52, 0x08, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x12, 0x4e, + 0x0a, 0x0e, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5f, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, + 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4c, + 0x69, 0x67, 0x68, 0x74, 0x6e, 0x69, 0x6e, 0x67, 0x4e, 0x6f, 0x64, 0x65, 0x2e, 0x43, 0x75, 0x73, + 0x74, 0x6f, 0x6d, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, + 0x0d, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x1a, 0x4b, + 0x0a, 0x0d, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, + 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x6b, 0x65, + 0x79, 0x12, 0x24, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x0e, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, + 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x40, 0x0a, 0x12, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x0c, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x4a, 0x04, 0x08, - 0x03, 0x10, 0x04, 0x22, 0x2e, 0x0a, 0x08, 0x4e, 0x6f, 0x64, 0x65, 0x50, 0x61, 0x69, 0x72, 0x12, - 0x12, 0x0a, 0x04, 0x66, 0x72, 0x6f, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x66, - 0x72, 0x6f, 0x6d, 0x12, 0x0e, 0x0a, 0x02, 0x74, 0x6f, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, - 0x02, 0x74, 0x6f, 0x22, 0x5d, 0x0a, 0x0b, 0x45, 0x64, 0x67, 0x65, 0x4c, 0x6f, 0x63, 0x61, 0x74, - 0x6f, 0x72, 0x12, 0x21, 0x0a, 0x0a, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x5f, 0x69, 0x64, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x42, 0x02, 0x30, 0x01, 0x52, 0x09, 0x63, 0x68, 0x61, 0x6e, - 0x6e, 0x65, 0x6c, 0x49, 0x64, 0x12, 0x2b, 0x0a, 0x11, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, - 0x6f, 0x6e, 0x5f, 0x72, 0x65, 0x76, 0x65, 0x72, 0x73, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, - 0x52, 0x10, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x76, 0x65, 0x72, - 0x73, 0x65, 0x22, 0x5e, 0x0a, 0x13, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x6f, 0x75, 0x74, 0x65, - 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x24, 0x0a, 0x06, 0x72, 0x6f, 0x75, - 0x74, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x6c, 0x6e, 0x72, 0x70, - 0x63, 0x2e, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x52, 0x06, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x12, - 0x21, 0x0a, 0x0c, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x70, 0x72, 0x6f, 0x62, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x01, 0x52, 0x0b, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x50, 0x72, - 0x6f, 0x62, 0x22, 0xa5, 0x05, 0x0a, 0x03, 0x48, 0x6f, 0x70, 0x12, 0x1b, 0x0a, 0x07, 0x63, 0x68, - 0x61, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x42, 0x02, 0x30, 0x01, 0x52, - 0x06, 0x63, 0x68, 0x61, 0x6e, 0x49, 0x64, 0x12, 0x27, 0x0a, 0x0d, 0x63, 0x68, 0x61, 0x6e, 0x5f, - 0x63, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x42, 0x02, - 0x18, 0x01, 0x52, 0x0c, 0x63, 0x68, 0x61, 0x6e, 0x43, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, - 0x12, 0x28, 0x0a, 0x0e, 0x61, 0x6d, 0x74, 0x5f, 0x74, 0x6f, 0x5f, 0x66, 0x6f, 0x72, 0x77, 0x61, - 0x72, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x42, 0x02, 0x18, 0x01, 0x52, 0x0c, 0x61, 0x6d, - 0x74, 0x54, 0x6f, 0x46, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x12, 0x14, 0x0a, 0x03, 0x66, 0x65, - 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x42, 0x02, 0x18, 0x01, 0x52, 0x03, 0x66, 0x65, 0x65, - 0x12, 0x16, 0x0a, 0x06, 0x65, 0x78, 0x70, 0x69, 0x72, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, - 0x52, 0x06, 0x65, 0x78, 0x70, 0x69, 0x72, 0x79, 0x12, 0x2d, 0x0a, 0x13, 0x61, 0x6d, 0x74, 0x5f, - 0x74, 0x6f, 0x5f, 0x66, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, - 0x06, 0x20, 0x01, 0x28, 0x03, 0x52, 0x10, 0x61, 0x6d, 0x74, 0x54, 0x6f, 0x46, 0x6f, 0x72, 0x77, - 0x61, 0x72, 0x64, 0x4d, 0x73, 0x61, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x66, 0x65, 0x65, 0x5f, 0x6d, - 0x73, 0x61, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x66, 0x65, 0x65, 0x4d, 0x73, - 0x61, 0x74, 0x12, 0x17, 0x0a, 0x07, 0x70, 0x75, 0x62, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x08, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x06, 0x70, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x12, 0x23, 0x0a, 0x0b, 0x74, - 0x6c, 0x76, 0x5f, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x09, 0x20, 0x01, 0x28, 0x08, - 0x42, 0x02, 0x18, 0x01, 0x52, 0x0a, 0x74, 0x6c, 0x76, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, - 0x12, 0x2f, 0x0a, 0x0a, 0x6d, 0x70, 0x70, 0x5f, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x18, 0x0a, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4d, 0x50, 0x50, - 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x52, 0x09, 0x6d, 0x70, 0x70, 0x52, 0x65, 0x63, 0x6f, 0x72, - 0x64, 0x12, 0x2f, 0x0a, 0x0a, 0x61, 0x6d, 0x70, 0x5f, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x18, - 0x0c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x4d, - 0x50, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x52, 0x09, 0x61, 0x6d, 0x70, 0x52, 0x65, 0x63, 0x6f, - 0x72, 0x64, 0x12, 0x44, 0x0a, 0x0e, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5f, 0x72, 0x65, 0x63, - 0x6f, 0x72, 0x64, 0x73, 0x18, 0x0b, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x6c, 0x6e, 0x72, - 0x70, 0x63, 0x2e, 0x48, 0x6f, 0x70, 0x2e, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x52, 0x65, 0x63, - 0x6f, 0x72, 0x64, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0d, 0x63, 0x75, 0x73, 0x74, 0x6f, - 0x6d, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, - 0x64, 0x61, 0x74, 0x61, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, - 0x64, 0x61, 0x74, 0x61, 0x12, 0x25, 0x0a, 0x0e, 0x62, 0x6c, 0x69, 0x6e, 0x64, 0x69, 0x6e, 0x67, - 0x5f, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0d, 0x62, 0x6c, - 0x69, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x25, 0x0a, 0x0e, 0x65, - 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x18, 0x0f, 0x20, - 0x01, 0x28, 0x0c, 0x52, 0x0d, 0x65, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x44, 0x61, - 0x74, 0x61, 0x12, 0x24, 0x0a, 0x0e, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x61, 0x6d, 0x74, 0x5f, - 0x6d, 0x73, 0x61, 0x74, 0x18, 0x10, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0c, 0x74, 0x6f, 0x74, 0x61, - 0x6c, 0x41, 0x6d, 0x74, 0x4d, 0x73, 0x61, 0x74, 0x1a, 0x40, 0x0a, 0x12, 0x43, 0x75, 0x73, 0x74, - 0x6f, 0x6d, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, - 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x03, 0x6b, 0x65, 0x79, - 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, - 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x54, 0x0a, 0x09, 0x4d, 0x50, - 0x50, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x61, 0x79, 0x6d, 0x65, - 0x6e, 0x74, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x70, - 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x41, 0x64, 0x64, 0x72, 0x12, 0x24, 0x0a, 0x0e, 0x74, 0x6f, - 0x74, 0x61, 0x6c, 0x5f, 0x61, 0x6d, 0x74, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x0a, 0x20, 0x01, - 0x28, 0x03, 0x52, 0x0c, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x41, 0x6d, 0x74, 0x4d, 0x73, 0x61, 0x74, - 0x22, 0x62, 0x0a, 0x09, 0x41, 0x4d, 0x50, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x12, 0x1d, 0x0a, - 0x0a, 0x72, 0x6f, 0x6f, 0x74, 0x5f, 0x73, 0x68, 0x61, 0x72, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x0c, 0x52, 0x09, 0x72, 0x6f, 0x6f, 0x74, 0x53, 0x68, 0x61, 0x72, 0x65, 0x12, 0x15, 0x0a, 0x06, - 0x73, 0x65, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x73, 0x65, - 0x74, 0x49, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x63, 0x68, 0x69, 0x6c, 0x64, 0x5f, 0x69, 0x6e, 0x64, - 0x65, 0x78, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x63, 0x68, 0x69, 0x6c, 0x64, 0x49, - 0x6e, 0x64, 0x65, 0x78, 0x22, 0xc4, 0x02, 0x0a, 0x05, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x12, 0x26, - 0x0a, 0x0f, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x6c, 0x6f, 0x63, - 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0d, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x54, 0x69, - 0x6d, 0x65, 0x4c, 0x6f, 0x63, 0x6b, 0x12, 0x21, 0x0a, 0x0a, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, - 0x66, 0x65, 0x65, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x42, 0x02, 0x18, 0x01, 0x52, 0x09, - 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x46, 0x65, 0x65, 0x73, 0x12, 0x1f, 0x0a, 0x09, 0x74, 0x6f, 0x74, - 0x61, 0x6c, 0x5f, 0x61, 0x6d, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x42, 0x02, 0x18, 0x01, - 0x52, 0x08, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x41, 0x6d, 0x74, 0x12, 0x1e, 0x0a, 0x04, 0x68, 0x6f, - 0x70, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, - 0x2e, 0x48, 0x6f, 0x70, 0x52, 0x04, 0x68, 0x6f, 0x70, 0x73, 0x12, 0x26, 0x0a, 0x0f, 0x74, 0x6f, - 0x74, 0x61, 0x6c, 0x5f, 0x66, 0x65, 0x65, 0x73, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x05, 0x20, - 0x01, 0x28, 0x03, 0x52, 0x0d, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x46, 0x65, 0x65, 0x73, 0x4d, 0x73, - 0x61, 0x74, 0x12, 0x24, 0x0a, 0x0e, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x61, 0x6d, 0x74, 0x5f, - 0x6d, 0x73, 0x61, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0c, 0x74, 0x6f, 0x74, 0x61, - 0x6c, 0x41, 0x6d, 0x74, 0x4d, 0x73, 0x61, 0x74, 0x12, 0x31, 0x0a, 0x15, 0x66, 0x69, 0x72, 0x73, - 0x74, 0x5f, 0x68, 0x6f, 0x70, 0x5f, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x6d, 0x73, 0x61, - 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x03, 0x52, 0x12, 0x66, 0x69, 0x72, 0x73, 0x74, 0x48, 0x6f, - 0x70, 0x41, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x4d, 0x73, 0x61, 0x74, 0x12, 0x2e, 0x0a, 0x13, 0x63, - 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x5f, 0x64, 0x61, - 0x74, 0x61, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x11, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, - 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x44, 0x61, 0x74, 0x61, 0x22, 0x55, 0x0a, 0x0f, 0x4e, - 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x17, - 0x0a, 0x07, 0x70, 0x75, 0x62, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x06, 0x70, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x12, 0x29, 0x0a, 0x10, 0x69, 0x6e, 0x63, 0x6c, 0x75, - 0x64, 0x65, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x08, 0x52, 0x0f, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, - 0x6c, 0x73, 0x22, 0xae, 0x01, 0x0a, 0x08, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, - 0x28, 0x0a, 0x04, 0x6e, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, - 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x67, 0x68, 0x74, 0x6e, 0x69, 0x6e, 0x67, 0x4e, - 0x6f, 0x64, 0x65, 0x52, 0x04, 0x6e, 0x6f, 0x64, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x6e, 0x75, 0x6d, - 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, - 0x0b, 0x6e, 0x75, 0x6d, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x12, 0x25, 0x0a, 0x0e, - 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x63, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x03, 0x52, 0x0d, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x43, 0x61, 0x70, 0x61, 0x63, - 0x69, 0x74, 0x79, 0x12, 0x2e, 0x0a, 0x08, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x18, - 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, - 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x45, 0x64, 0x67, 0x65, 0x52, 0x08, 0x63, 0x68, 0x61, 0x6e, 0x6e, - 0x65, 0x6c, 0x73, 0x22, 0xc6, 0x03, 0x0a, 0x0d, 0x4c, 0x69, 0x67, 0x68, 0x74, 0x6e, 0x69, 0x6e, - 0x67, 0x4e, 0x6f, 0x64, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x75, 0x70, - 0x64, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x6c, 0x61, 0x73, 0x74, - 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x17, 0x0a, 0x07, 0x70, 0x75, 0x62, 0x5f, 0x6b, 0x65, - 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x70, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x12, - 0x14, 0x0a, 0x05, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, - 0x61, 0x6c, 0x69, 0x61, 0x73, 0x12, 0x30, 0x0a, 0x09, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, - 0x65, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, - 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x09, 0x61, 0x64, - 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x6f, 0x6c, 0x6f, 0x72, - 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x12, 0x3e, 0x0a, - 0x08, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x22, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x67, 0x68, 0x74, 0x6e, 0x69, 0x6e, - 0x67, 0x4e, 0x6f, 0x64, 0x65, 0x2e, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x45, 0x6e, - 0x74, 0x72, 0x79, 0x52, 0x08, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x12, 0x4e, 0x0a, - 0x0e, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5f, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x18, - 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, - 0x67, 0x68, 0x74, 0x6e, 0x69, 0x6e, 0x67, 0x4e, 0x6f, 0x64, 0x65, 0x2e, 0x43, 0x75, 0x73, 0x74, - 0x6f, 0x6d, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0d, - 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x1a, 0x4b, 0x0a, - 0x0d, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, - 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x6b, 0x65, 0x79, - 0x12, 0x24, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x0e, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x52, - 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x40, 0x0a, 0x12, 0x43, 0x75, - 0x73, 0x74, 0x6f, 0x6d, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, - 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x03, 0x6b, - 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x0c, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x3b, 0x0a, 0x0b, - 0x4e, 0x6f, 0x64, 0x65, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x6e, - 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6e, 0x65, - 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x12, 0x12, 0x0a, 0x04, 0x61, 0x64, 0x64, 0x72, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x04, 0x61, 0x64, 0x64, 0x72, 0x22, 0x89, 0x04, 0x0a, 0x0d, 0x52, 0x6f, - 0x75, 0x74, 0x69, 0x6e, 0x67, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x26, 0x0a, 0x0f, 0x74, - 0x69, 0x6d, 0x65, 0x5f, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0d, 0x74, 0x69, 0x6d, 0x65, 0x4c, 0x6f, 0x63, 0x6b, 0x44, 0x65, - 0x6c, 0x74, 0x61, 0x12, 0x19, 0x0a, 0x08, 0x6d, 0x69, 0x6e, 0x5f, 0x68, 0x74, 0x6c, 0x63, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x6d, 0x69, 0x6e, 0x48, 0x74, 0x6c, 0x63, 0x12, 0x22, - 0x0a, 0x0d, 0x66, 0x65, 0x65, 0x5f, 0x62, 0x61, 0x73, 0x65, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, - 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b, 0x66, 0x65, 0x65, 0x42, 0x61, 0x73, 0x65, 0x4d, 0x73, - 0x61, 0x74, 0x12, 0x2d, 0x0a, 0x13, 0x66, 0x65, 0x65, 0x5f, 0x72, 0x61, 0x74, 0x65, 0x5f, 0x6d, - 0x69, 0x6c, 0x6c, 0x69, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, - 0x10, 0x66, 0x65, 0x65, 0x52, 0x61, 0x74, 0x65, 0x4d, 0x69, 0x6c, 0x6c, 0x69, 0x4d, 0x73, 0x61, - 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x05, 0x20, - 0x01, 0x28, 0x08, 0x52, 0x08, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x22, 0x0a, - 0x0d, 0x6d, 0x61, 0x78, 0x5f, 0x68, 0x74, 0x6c, 0x63, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x06, - 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x6d, 0x61, 0x78, 0x48, 0x74, 0x6c, 0x63, 0x4d, 0x73, 0x61, - 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, - 0x18, 0x07, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x6c, 0x61, 0x73, 0x74, 0x55, 0x70, 0x64, 0x61, - 0x74, 0x65, 0x12, 0x4e, 0x0a, 0x0e, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5f, 0x72, 0x65, 0x63, - 0x6f, 0x72, 0x64, 0x73, 0x18, 0x08, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x6c, 0x6e, 0x72, - 0x70, 0x63, 0x2e, 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, - 0x2e, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x45, 0x6e, - 0x74, 0x72, 0x79, 0x52, 0x0d, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x52, 0x65, 0x63, 0x6f, 0x72, - 0x64, 0x73, 0x12, 0x31, 0x0a, 0x15, 0x69, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x5f, 0x66, 0x65, - 0x65, 0x5f, 0x62, 0x61, 0x73, 0x65, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x09, 0x20, 0x01, 0x28, - 0x05, 0x52, 0x12, 0x69, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x46, 0x65, 0x65, 0x42, 0x61, 0x73, - 0x65, 0x4d, 0x73, 0x61, 0x74, 0x12, 0x3c, 0x0a, 0x1b, 0x69, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, - 0x5f, 0x66, 0x65, 0x65, 0x5f, 0x72, 0x61, 0x74, 0x65, 0x5f, 0x6d, 0x69, 0x6c, 0x6c, 0x69, 0x5f, - 0x6d, 0x73, 0x61, 0x74, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x05, 0x52, 0x17, 0x69, 0x6e, 0x62, 0x6f, - 0x75, 0x6e, 0x64, 0x46, 0x65, 0x65, 0x52, 0x61, 0x74, 0x65, 0x4d, 0x69, 0x6c, 0x6c, 0x69, 0x4d, - 0x73, 0x61, 0x74, 0x1a, 0x40, 0x0a, 0x12, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x52, 0x65, 0x63, + 0x28, 0x0c, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x3b, 0x0a, + 0x0b, 0x4e, 0x6f, 0x64, 0x65, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x18, 0x0a, 0x07, + 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6e, + 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x12, 0x12, 0x0a, 0x04, 0x61, 0x64, 0x64, 0x72, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x61, 0x64, 0x64, 0x72, 0x22, 0x89, 0x04, 0x0a, 0x0d, 0x52, + 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x26, 0x0a, 0x0f, + 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0d, 0x74, 0x69, 0x6d, 0x65, 0x4c, 0x6f, 0x63, 0x6b, 0x44, + 0x65, 0x6c, 0x74, 0x61, 0x12, 0x19, 0x0a, 0x08, 0x6d, 0x69, 0x6e, 0x5f, 0x68, 0x74, 0x6c, 0x63, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x6d, 0x69, 0x6e, 0x48, 0x74, 0x6c, 0x63, 0x12, + 0x22, 0x0a, 0x0d, 0x66, 0x65, 0x65, 0x5f, 0x62, 0x61, 0x73, 0x65, 0x5f, 0x6d, 0x73, 0x61, 0x74, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b, 0x66, 0x65, 0x65, 0x42, 0x61, 0x73, 0x65, 0x4d, + 0x73, 0x61, 0x74, 0x12, 0x2d, 0x0a, 0x13, 0x66, 0x65, 0x65, 0x5f, 0x72, 0x61, 0x74, 0x65, 0x5f, + 0x6d, 0x69, 0x6c, 0x6c, 0x69, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, + 0x52, 0x10, 0x66, 0x65, 0x65, 0x52, 0x61, 0x74, 0x65, 0x4d, 0x69, 0x6c, 0x6c, 0x69, 0x4d, 0x73, + 0x61, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x05, + 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x22, + 0x0a, 0x0d, 0x6d, 0x61, 0x78, 0x5f, 0x68, 0x74, 0x6c, 0x63, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, + 0x06, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x6d, 0x61, 0x78, 0x48, 0x74, 0x6c, 0x63, 0x4d, 0x73, + 0x61, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, + 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x6c, 0x61, 0x73, 0x74, 0x55, 0x70, 0x64, + 0x61, 0x74, 0x65, 0x12, 0x4e, 0x0a, 0x0e, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5f, 0x72, 0x65, + 0x63, 0x6f, 0x72, 0x64, 0x73, 0x18, 0x08, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x6c, 0x6e, + 0x72, 0x70, 0x63, 0x2e, 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x50, 0x6f, 0x6c, 0x69, 0x63, + 0x79, 0x2e, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x45, + 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0d, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x52, 0x65, 0x63, 0x6f, + 0x72, 0x64, 0x73, 0x12, 0x31, 0x0a, 0x15, 0x69, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x5f, 0x66, + 0x65, 0x65, 0x5f, 0x62, 0x61, 0x73, 0x65, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x09, 0x20, 0x01, + 0x28, 0x05, 0x52, 0x12, 0x69, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x46, 0x65, 0x65, 0x42, 0x61, + 0x73, 0x65, 0x4d, 0x73, 0x61, 0x74, 0x12, 0x3c, 0x0a, 0x1b, 0x69, 0x6e, 0x62, 0x6f, 0x75, 0x6e, + 0x64, 0x5f, 0x66, 0x65, 0x65, 0x5f, 0x72, 0x61, 0x74, 0x65, 0x5f, 0x6d, 0x69, 0x6c, 0x6c, 0x69, + 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x05, 0x52, 0x17, 0x69, 0x6e, 0x62, + 0x6f, 0x75, 0x6e, 0x64, 0x46, 0x65, 0x65, 0x52, 0x61, 0x74, 0x65, 0x4d, 0x69, 0x6c, 0x6c, 0x69, + 0x4d, 0x73, 0x61, 0x74, 0x1a, 0x40, 0x0a, 0x12, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x52, 0x65, + 0x63, 0x6f, 0x72, 0x64, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, + 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, + 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x76, 0x61, 0x6c, + 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xcc, 0x03, 0x0a, 0x0b, 0x43, 0x68, 0x61, 0x6e, 0x6e, + 0x65, 0x6c, 0x45, 0x64, 0x67, 0x65, 0x12, 0x21, 0x0a, 0x0a, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, + 0x6c, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x42, 0x02, 0x30, 0x01, 0x52, 0x09, + 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x68, 0x61, + 0x6e, 0x5f, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, + 0x68, 0x61, 0x6e, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x23, 0x0a, 0x0b, 0x6c, 0x61, 0x73, 0x74, + 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x42, 0x02, 0x18, + 0x01, 0x52, 0x0a, 0x6c, 0x61, 0x73, 0x74, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x1b, 0x0a, + 0x09, 0x6e, 0x6f, 0x64, 0x65, 0x31, 0x5f, 0x70, 0x75, 0x62, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x08, 0x6e, 0x6f, 0x64, 0x65, 0x31, 0x50, 0x75, 0x62, 0x12, 0x1b, 0x0a, 0x09, 0x6e, 0x6f, + 0x64, 0x65, 0x32, 0x5f, 0x70, 0x75, 0x62, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6e, + 0x6f, 0x64, 0x65, 0x32, 0x50, 0x75, 0x62, 0x12, 0x1a, 0x0a, 0x08, 0x63, 0x61, 0x70, 0x61, 0x63, + 0x69, 0x74, 0x79, 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x63, 0x61, 0x70, 0x61, 0x63, + 0x69, 0x74, 0x79, 0x12, 0x37, 0x0a, 0x0c, 0x6e, 0x6f, 0x64, 0x65, 0x31, 0x5f, 0x70, 0x6f, 0x6c, + 0x69, 0x63, 0x79, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x6c, 0x6e, 0x72, 0x70, + 0x63, 0x2e, 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, + 0x0b, 0x6e, 0x6f, 0x64, 0x65, 0x31, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x37, 0x0a, 0x0c, + 0x6e, 0x6f, 0x64, 0x65, 0x32, 0x5f, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x18, 0x08, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x6f, 0x75, 0x74, 0x69, + 0x6e, 0x67, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x0b, 0x6e, 0x6f, 0x64, 0x65, 0x32, 0x50, + 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x4c, 0x0a, 0x0e, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5f, + 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x18, 0x09, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x25, 0x2e, + 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x45, 0x64, 0x67, + 0x65, 0x2e, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x45, + 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0d, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x52, 0x65, 0x63, 0x6f, + 0x72, 0x64, 0x73, 0x1a, 0x40, 0x0a, 0x12, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, - 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xcc, 0x03, 0x0a, 0x0b, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, - 0x6c, 0x45, 0x64, 0x67, 0x65, 0x12, 0x21, 0x0a, 0x0a, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, - 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x42, 0x02, 0x30, 0x01, 0x52, 0x09, 0x63, - 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x68, 0x61, 0x6e, - 0x5f, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x68, - 0x61, 0x6e, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x23, 0x0a, 0x0b, 0x6c, 0x61, 0x73, 0x74, 0x5f, - 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x42, 0x02, 0x18, 0x01, - 0x52, 0x0a, 0x6c, 0x61, 0x73, 0x74, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x1b, 0x0a, 0x09, - 0x6e, 0x6f, 0x64, 0x65, 0x31, 0x5f, 0x70, 0x75, 0x62, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x08, 0x6e, 0x6f, 0x64, 0x65, 0x31, 0x50, 0x75, 0x62, 0x12, 0x1b, 0x0a, 0x09, 0x6e, 0x6f, 0x64, - 0x65, 0x32, 0x5f, 0x70, 0x75, 0x62, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6e, 0x6f, - 0x64, 0x65, 0x32, 0x50, 0x75, 0x62, 0x12, 0x1a, 0x0a, 0x08, 0x63, 0x61, 0x70, 0x61, 0x63, 0x69, - 0x74, 0x79, 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x63, 0x61, 0x70, 0x61, 0x63, 0x69, - 0x74, 0x79, 0x12, 0x37, 0x0a, 0x0c, 0x6e, 0x6f, 0x64, 0x65, 0x31, 0x5f, 0x70, 0x6f, 0x6c, 0x69, - 0x63, 0x79, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, - 0x2e, 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x0b, - 0x6e, 0x6f, 0x64, 0x65, 0x31, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x37, 0x0a, 0x0c, 0x6e, - 0x6f, 0x64, 0x65, 0x32, 0x5f, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x18, 0x08, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x14, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, - 0x67, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x0b, 0x6e, 0x6f, 0x64, 0x65, 0x32, 0x50, 0x6f, - 0x6c, 0x69, 0x63, 0x79, 0x12, 0x4c, 0x0a, 0x0e, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5f, 0x72, - 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x18, 0x09, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x6c, - 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x45, 0x64, 0x67, 0x65, - 0x2e, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x45, 0x6e, - 0x74, 0x72, 0x79, 0x52, 0x0d, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x52, 0x65, 0x63, 0x6f, 0x72, - 0x64, 0x73, 0x1a, 0x40, 0x0a, 0x12, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x52, 0x65, 0x63, 0x6f, - 0x72, 0x64, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, - 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, - 0x3a, 0x02, 0x38, 0x01, 0x22, 0x46, 0x0a, 0x13, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x47, - 0x72, 0x61, 0x70, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2f, 0x0a, 0x13, 0x69, - 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x5f, 0x75, 0x6e, 0x61, 0x6e, 0x6e, 0x6f, 0x75, 0x6e, 0x63, - 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x12, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, - 0x65, 0x55, 0x6e, 0x61, 0x6e, 0x6e, 0x6f, 0x75, 0x6e, 0x63, 0x65, 0x64, 0x22, 0x64, 0x0a, 0x0c, - 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x47, 0x72, 0x61, 0x70, 0x68, 0x12, 0x2a, 0x0a, 0x05, - 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x6c, 0x6e, - 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x67, 0x68, 0x74, 0x6e, 0x69, 0x6e, 0x67, 0x4e, 0x6f, 0x64, - 0x65, 0x52, 0x05, 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x12, 0x28, 0x0a, 0x05, 0x65, 0x64, 0x67, 0x65, - 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, - 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x45, 0x64, 0x67, 0x65, 0x52, 0x05, 0x65, 0x64, 0x67, - 0x65, 0x73, 0x22, 0x41, 0x0a, 0x12, 0x4e, 0x6f, 0x64, 0x65, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, - 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2b, 0x0a, 0x05, 0x74, 0x79, 0x70, 0x65, - 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0e, 0x32, 0x15, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, - 0x4e, 0x6f, 0x64, 0x65, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x54, 0x79, 0x70, 0x65, 0x52, 0x05, - 0x74, 0x79, 0x70, 0x65, 0x73, 0x22, 0xe1, 0x01, 0x0a, 0x13, 0x4e, 0x6f, 0x64, 0x65, 0x4d, 0x65, - 0x74, 0x72, 0x69, 0x63, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x6c, 0x0a, - 0x16, 0x62, 0x65, 0x74, 0x77, 0x65, 0x65, 0x6e, 0x6e, 0x65, 0x73, 0x73, 0x5f, 0x63, 0x65, 0x6e, - 0x74, 0x72, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x35, 0x2e, - 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, - 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x42, 0x65, 0x74, 0x77, 0x65, 0x65, - 0x6e, 0x6e, 0x65, 0x73, 0x73, 0x43, 0x65, 0x6e, 0x74, 0x72, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x45, - 0x6e, 0x74, 0x72, 0x79, 0x52, 0x15, 0x62, 0x65, 0x74, 0x77, 0x65, 0x65, 0x6e, 0x6e, 0x65, 0x73, - 0x73, 0x43, 0x65, 0x6e, 0x74, 0x72, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x1a, 0x5c, 0x0a, 0x1a, 0x42, - 0x65, 0x74, 0x77, 0x65, 0x65, 0x6e, 0x6e, 0x65, 0x73, 0x73, 0x43, 0x65, 0x6e, 0x74, 0x72, 0x61, - 0x6c, 0x69, 0x74, 0x79, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x28, 0x0a, 0x05, 0x76, - 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x6c, 0x6e, 0x72, - 0x70, 0x63, 0x2e, 0x46, 0x6c, 0x6f, 0x61, 0x74, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x52, 0x05, - 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x4e, 0x0a, 0x0b, 0x46, 0x6c, 0x6f, - 0x61, 0x74, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, - 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x01, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x29, - 0x0a, 0x10, 0x6e, 0x6f, 0x72, 0x6d, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x76, 0x61, 0x6c, - 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x01, 0x52, 0x0f, 0x6e, 0x6f, 0x72, 0x6d, 0x61, 0x6c, - 0x69, 0x7a, 0x65, 0x64, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x4d, 0x0a, 0x0f, 0x43, 0x68, 0x61, - 0x6e, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x07, - 0x63, 0x68, 0x61, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x42, 0x02, 0x30, - 0x01, 0x52, 0x06, 0x63, 0x68, 0x61, 0x6e, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x68, 0x61, - 0x6e, 0x5f, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, - 0x68, 0x61, 0x6e, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x22, 0x14, 0x0a, 0x12, 0x4e, 0x65, 0x74, 0x77, - 0x6f, 0x72, 0x6b, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0xd5, - 0x03, 0x0a, 0x0b, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x25, - 0x0a, 0x0e, 0x67, 0x72, 0x61, 0x70, 0x68, 0x5f, 0x64, 0x69, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0d, 0x67, 0x72, 0x61, 0x70, 0x68, 0x44, 0x69, 0x61, - 0x6d, 0x65, 0x74, 0x65, 0x72, 0x12, 0x24, 0x0a, 0x0e, 0x61, 0x76, 0x67, 0x5f, 0x6f, 0x75, 0x74, - 0x5f, 0x64, 0x65, 0x67, 0x72, 0x65, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x01, 0x52, 0x0c, 0x61, - 0x76, 0x67, 0x4f, 0x75, 0x74, 0x44, 0x65, 0x67, 0x72, 0x65, 0x65, 0x12, 0x24, 0x0a, 0x0e, 0x6d, - 0x61, 0x78, 0x5f, 0x6f, 0x75, 0x74, 0x5f, 0x64, 0x65, 0x67, 0x72, 0x65, 0x65, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x0d, 0x52, 0x0c, 0x6d, 0x61, 0x78, 0x4f, 0x75, 0x74, 0x44, 0x65, 0x67, 0x72, 0x65, - 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x6e, 0x75, 0x6d, 0x5f, 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x18, 0x04, - 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x6e, 0x75, 0x6d, 0x4e, 0x6f, 0x64, 0x65, 0x73, 0x12, 0x21, - 0x0a, 0x0c, 0x6e, 0x75, 0x6d, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x18, 0x05, - 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x6e, 0x75, 0x6d, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, - 0x73, 0x12, 0x34, 0x0a, 0x16, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x6e, 0x65, 0x74, 0x77, 0x6f, - 0x72, 0x6b, 0x5f, 0x63, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x18, 0x06, 0x20, 0x01, 0x28, - 0x03, 0x52, 0x14, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x43, - 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x12, 0x28, 0x0a, 0x10, 0x61, 0x76, 0x67, 0x5f, 0x63, - 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, - 0x01, 0x52, 0x0e, 0x61, 0x76, 0x67, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x53, 0x69, 0x7a, - 0x65, 0x12, 0x28, 0x0a, 0x10, 0x6d, 0x69, 0x6e, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, - 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0e, 0x6d, 0x69, 0x6e, - 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x28, 0x0a, 0x10, 0x6d, - 0x61, 0x78, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, - 0x09, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0e, 0x6d, 0x61, 0x78, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, - 0x6c, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x35, 0x0a, 0x17, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x6e, 0x5f, - 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x5f, 0x73, 0x61, 0x74, - 0x18, 0x0a, 0x20, 0x01, 0x28, 0x03, 0x52, 0x14, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x6e, 0x43, 0x68, - 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x53, 0x69, 0x7a, 0x65, 0x53, 0x61, 0x74, 0x12, 0x28, 0x0a, 0x10, - 0x6e, 0x75, 0x6d, 0x5f, 0x7a, 0x6f, 0x6d, 0x62, 0x69, 0x65, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x73, - 0x18, 0x0b, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0e, 0x6e, 0x75, 0x6d, 0x5a, 0x6f, 0x6d, 0x62, 0x69, - 0x65, 0x43, 0x68, 0x61, 0x6e, 0x73, 0x22, 0x0d, 0x0a, 0x0b, 0x53, 0x74, 0x6f, 0x70, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x0e, 0x0a, 0x0c, 0x53, 0x74, 0x6f, 0x70, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x0a, 0x19, 0x47, 0x72, 0x61, 0x70, 0x68, 0x54, 0x6f, - 0x70, 0x6f, 0x6c, 0x6f, 0x67, 0x79, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, - 0x6f, 0x6e, 0x22, 0xcd, 0x01, 0x0a, 0x13, 0x47, 0x72, 0x61, 0x70, 0x68, 0x54, 0x6f, 0x70, 0x6f, - 0x6c, 0x6f, 0x67, 0x79, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x34, 0x0a, 0x0c, 0x6e, 0x6f, - 0x64, 0x65, 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x11, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x55, 0x70, 0x64, - 0x61, 0x74, 0x65, 0x52, 0x0b, 0x6e, 0x6f, 0x64, 0x65, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, - 0x12, 0x41, 0x0a, 0x0f, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x5f, 0x75, 0x70, 0x64, 0x61, - 0x74, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x6c, 0x6e, 0x72, 0x70, - 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x45, 0x64, 0x67, 0x65, 0x55, 0x70, 0x64, - 0x61, 0x74, 0x65, 0x52, 0x0e, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x55, 0x70, 0x64, 0x61, - 0x74, 0x65, 0x73, 0x12, 0x3d, 0x0a, 0x0c, 0x63, 0x6c, 0x6f, 0x73, 0x65, 0x64, 0x5f, 0x63, 0x68, - 0x61, 0x6e, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, - 0x63, 0x2e, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x64, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x55, - 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x0b, 0x63, 0x6c, 0x6f, 0x73, 0x65, 0x64, 0x43, 0x68, 0x61, - 0x6e, 0x73, 0x22, 0xef, 0x02, 0x0a, 0x0a, 0x4e, 0x6f, 0x64, 0x65, 0x55, 0x70, 0x64, 0x61, 0x74, - 0x65, 0x12, 0x20, 0x0a, 0x09, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x18, 0x01, - 0x20, 0x03, 0x28, 0x09, 0x42, 0x02, 0x18, 0x01, 0x52, 0x09, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, - 0x73, 0x65, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x5f, - 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x69, 0x64, 0x65, 0x6e, 0x74, - 0x69, 0x74, 0x79, 0x4b, 0x65, 0x79, 0x12, 0x2b, 0x0a, 0x0f, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, - 0x5f, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x42, - 0x02, 0x18, 0x01, 0x52, 0x0e, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x46, 0x65, 0x61, 0x74, 0x75, - 0x72, 0x65, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x18, 0x04, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x05, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x6f, 0x6c, - 0x6f, 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x12, - 0x39, 0x0a, 0x0e, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, - 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, - 0x4e, 0x6f, 0x64, 0x65, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x0d, 0x6e, 0x6f, 0x64, - 0x65, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x12, 0x3b, 0x0a, 0x08, 0x66, 0x65, - 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x6c, - 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x2e, - 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x08, 0x66, - 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x1a, 0x4b, 0x0a, 0x0d, 0x46, 0x65, 0x61, 0x74, 0x75, - 0x72, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x24, 0x0a, 0x05, 0x76, 0x61, - 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x6c, 0x6e, 0x72, 0x70, - 0x63, 0x2e, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, - 0x3a, 0x02, 0x38, 0x01, 0x22, 0x91, 0x02, 0x0a, 0x11, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, - 0x45, 0x64, 0x67, 0x65, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x1b, 0x0a, 0x07, 0x63, 0x68, - 0x61, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x42, 0x02, 0x30, 0x01, 0x52, - 0x06, 0x63, 0x68, 0x61, 0x6e, 0x49, 0x64, 0x12, 0x32, 0x0a, 0x0a, 0x63, 0x68, 0x61, 0x6e, 0x5f, - 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x6c, 0x6e, - 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x50, 0x6f, 0x69, 0x6e, 0x74, - 0x52, 0x09, 0x63, 0x68, 0x61, 0x6e, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x63, - 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x63, - 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x12, 0x3b, 0x0a, 0x0e, 0x72, 0x6f, 0x75, 0x74, 0x69, - 0x6e, 0x67, 0x5f, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x14, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x50, - 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x0d, 0x72, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x50, 0x6f, - 0x6c, 0x69, 0x63, 0x79, 0x12, 0x29, 0x0a, 0x10, 0x61, 0x64, 0x76, 0x65, 0x72, 0x74, 0x69, 0x73, - 0x69, 0x6e, 0x67, 0x5f, 0x6e, 0x6f, 0x64, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, - 0x61, 0x64, 0x76, 0x65, 0x72, 0x74, 0x69, 0x73, 0x69, 0x6e, 0x67, 0x4e, 0x6f, 0x64, 0x65, 0x12, - 0x27, 0x0a, 0x0f, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6e, 0x67, 0x5f, 0x6e, 0x6f, - 0x64, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, - 0x74, 0x69, 0x6e, 0x67, 0x4e, 0x6f, 0x64, 0x65, 0x22, 0xa7, 0x01, 0x0a, 0x13, 0x43, 0x6c, 0x6f, - 0x73, 0x65, 0x64, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, - 0x12, 0x1b, 0x0a, 0x07, 0x63, 0x68, 0x61, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x04, 0x42, 0x02, 0x30, 0x01, 0x52, 0x06, 0x63, 0x68, 0x61, 0x6e, 0x49, 0x64, 0x12, 0x1a, 0x0a, - 0x08, 0x63, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, - 0x08, 0x63, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x6c, 0x6f, - 0x73, 0x65, 0x64, 0x5f, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, - 0x52, 0x0c, 0x63, 0x6c, 0x6f, 0x73, 0x65, 0x64, 0x48, 0x65, 0x69, 0x67, 0x68, 0x74, 0x12, 0x32, - 0x0a, 0x0a, 0x63, 0x68, 0x61, 0x6e, 0x5f, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x04, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, - 0x65, 0x6c, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x09, 0x63, 0x68, 0x61, 0x6e, 0x50, 0x6f, 0x69, - 0x6e, 0x74, 0x22, 0xcf, 0x01, 0x0a, 0x07, 0x48, 0x6f, 0x70, 0x48, 0x69, 0x6e, 0x74, 0x12, 0x17, - 0x0a, 0x07, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x06, 0x6e, 0x6f, 0x64, 0x65, 0x49, 0x64, 0x12, 0x1b, 0x0a, 0x07, 0x63, 0x68, 0x61, 0x6e, 0x5f, - 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x42, 0x02, 0x30, 0x01, 0x52, 0x06, 0x63, 0x68, - 0x61, 0x6e, 0x49, 0x64, 0x12, 0x22, 0x0a, 0x0d, 0x66, 0x65, 0x65, 0x5f, 0x62, 0x61, 0x73, 0x65, - 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x66, 0x65, 0x65, - 0x42, 0x61, 0x73, 0x65, 0x4d, 0x73, 0x61, 0x74, 0x12, 0x3e, 0x0a, 0x1b, 0x66, 0x65, 0x65, 0x5f, - 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x72, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x5f, 0x6d, 0x69, 0x6c, - 0x6c, 0x69, 0x6f, 0x6e, 0x74, 0x68, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x19, 0x66, - 0x65, 0x65, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x72, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x4d, 0x69, - 0x6c, 0x6c, 0x69, 0x6f, 0x6e, 0x74, 0x68, 0x73, 0x12, 0x2a, 0x0a, 0x11, 0x63, 0x6c, 0x74, 0x76, - 0x5f, 0x65, 0x78, 0x70, 0x69, 0x72, 0x79, 0x5f, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x18, 0x05, 0x20, - 0x01, 0x28, 0x0d, 0x52, 0x0f, 0x63, 0x6c, 0x74, 0x76, 0x45, 0x78, 0x70, 0x69, 0x72, 0x79, 0x44, - 0x65, 0x6c, 0x74, 0x61, 0x22, 0x1e, 0x0a, 0x05, 0x53, 0x65, 0x74, 0x49, 0x44, 0x12, 0x15, 0x0a, - 0x06, 0x73, 0x65, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x73, - 0x65, 0x74, 0x49, 0x64, 0x22, 0x38, 0x0a, 0x09, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x48, 0x69, 0x6e, - 0x74, 0x12, 0x2b, 0x0a, 0x09, 0x68, 0x6f, 0x70, 0x5f, 0x68, 0x69, 0x6e, 0x74, 0x73, 0x18, 0x01, - 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x48, 0x6f, 0x70, - 0x48, 0x69, 0x6e, 0x74, 0x52, 0x08, 0x68, 0x6f, 0x70, 0x48, 0x69, 0x6e, 0x74, 0x73, 0x22, 0xc4, - 0x02, 0x0a, 0x12, 0x42, 0x6c, 0x69, 0x6e, 0x64, 0x65, 0x64, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, - 0x74, 0x50, 0x61, 0x74, 0x68, 0x12, 0x35, 0x0a, 0x0c, 0x62, 0x6c, 0x69, 0x6e, 0x64, 0x65, 0x64, - 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x6c, 0x6e, - 0x72, 0x70, 0x63, 0x2e, 0x42, 0x6c, 0x69, 0x6e, 0x64, 0x65, 0x64, 0x50, 0x61, 0x74, 0x68, 0x52, - 0x0b, 0x62, 0x6c, 0x69, 0x6e, 0x64, 0x65, 0x64, 0x50, 0x61, 0x74, 0x68, 0x12, 0x22, 0x0a, 0x0d, - 0x62, 0x61, 0x73, 0x65, 0x5f, 0x66, 0x65, 0x65, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x04, 0x52, 0x0b, 0x62, 0x61, 0x73, 0x65, 0x46, 0x65, 0x65, 0x4d, 0x73, 0x61, 0x74, - 0x12, 0x32, 0x0a, 0x15, 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x72, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, - 0x5f, 0x66, 0x65, 0x65, 0x5f, 0x72, 0x61, 0x74, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, - 0x13, 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x72, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x46, 0x65, 0x65, - 0x52, 0x61, 0x74, 0x65, 0x12, 0x28, 0x0a, 0x10, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x63, 0x6c, - 0x74, 0x76, 0x5f, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0e, - 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x43, 0x6c, 0x74, 0x76, 0x44, 0x65, 0x6c, 0x74, 0x61, 0x12, 0x22, - 0x0a, 0x0d, 0x68, 0x74, 0x6c, 0x63, 0x5f, 0x6d, 0x69, 0x6e, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, - 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x68, 0x74, 0x6c, 0x63, 0x4d, 0x69, 0x6e, 0x4d, 0x73, - 0x61, 0x74, 0x12, 0x22, 0x0a, 0x0d, 0x68, 0x74, 0x6c, 0x63, 0x5f, 0x6d, 0x61, 0x78, 0x5f, 0x6d, - 0x73, 0x61, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x68, 0x74, 0x6c, 0x63, 0x4d, - 0x61, 0x78, 0x4d, 0x73, 0x61, 0x74, 0x12, 0x2d, 0x0a, 0x08, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, - 0x65, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0e, 0x32, 0x11, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, - 0x2e, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x42, 0x69, 0x74, 0x52, 0x08, 0x66, 0x65, 0x61, - 0x74, 0x75, 0x72, 0x65, 0x73, 0x22, 0x97, 0x01, 0x0a, 0x0b, 0x42, 0x6c, 0x69, 0x6e, 0x64, 0x65, - 0x64, 0x50, 0x61, 0x74, 0x68, 0x12, 0x2b, 0x0a, 0x11, 0x69, 0x6e, 0x74, 0x72, 0x6f, 0x64, 0x75, - 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6e, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, - 0x52, 0x10, 0x69, 0x6e, 0x74, 0x72, 0x6f, 0x64, 0x75, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x4e, 0x6f, - 0x64, 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x62, 0x6c, 0x69, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x70, - 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0d, 0x62, 0x6c, 0x69, 0x6e, - 0x64, 0x69, 0x6e, 0x67, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x34, 0x0a, 0x0c, 0x62, 0x6c, 0x69, - 0x6e, 0x64, 0x65, 0x64, 0x5f, 0x68, 0x6f, 0x70, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x11, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x42, 0x6c, 0x69, 0x6e, 0x64, 0x65, 0x64, 0x48, - 0x6f, 0x70, 0x52, 0x0b, 0x62, 0x6c, 0x69, 0x6e, 0x64, 0x65, 0x64, 0x48, 0x6f, 0x70, 0x73, 0x22, - 0x56, 0x0a, 0x0a, 0x42, 0x6c, 0x69, 0x6e, 0x64, 0x65, 0x64, 0x48, 0x6f, 0x70, 0x12, 0x21, 0x0a, - 0x0c, 0x62, 0x6c, 0x69, 0x6e, 0x64, 0x65, 0x64, 0x5f, 0x6e, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x62, 0x6c, 0x69, 0x6e, 0x64, 0x65, 0x64, 0x4e, 0x6f, 0x64, 0x65, - 0x12, 0x25, 0x0a, 0x0e, 0x65, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x5f, 0x64, 0x61, - 0x74, 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0d, 0x65, 0x6e, 0x63, 0x72, 0x79, 0x70, - 0x74, 0x65, 0x64, 0x44, 0x61, 0x74, 0x61, 0x22, 0xa8, 0x01, 0x0a, 0x0f, 0x41, 0x4d, 0x50, 0x49, - 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x2d, 0x0a, 0x05, 0x73, - 0x74, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x17, 0x2e, 0x6c, 0x6e, 0x72, - 0x70, 0x63, 0x2e, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x48, 0x54, 0x4c, 0x43, 0x53, 0x74, - 0x61, 0x74, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x73, 0x65, - 0x74, 0x74, 0x6c, 0x65, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, - 0x52, 0x0b, 0x73, 0x65, 0x74, 0x74, 0x6c, 0x65, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x1f, 0x0a, - 0x0b, 0x73, 0x65, 0x74, 0x74, 0x6c, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x03, 0x52, 0x0a, 0x73, 0x65, 0x74, 0x74, 0x6c, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x22, - 0x0a, 0x0d, 0x61, 0x6d, 0x74, 0x5f, 0x70, 0x61, 0x69, 0x64, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, - 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b, 0x61, 0x6d, 0x74, 0x50, 0x61, 0x69, 0x64, 0x4d, 0x73, - 0x61, 0x74, 0x22, 0xac, 0x0a, 0x0a, 0x07, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x12, 0x12, - 0x0a, 0x04, 0x6d, 0x65, 0x6d, 0x6f, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6d, 0x65, - 0x6d, 0x6f, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x5f, 0x70, 0x72, 0x65, 0x69, 0x6d, 0x61, 0x67, 0x65, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x72, 0x50, 0x72, 0x65, 0x69, 0x6d, 0x61, 0x67, - 0x65, 0x12, 0x15, 0x0a, 0x06, 0x72, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x04, 0x20, 0x01, 0x28, - 0x0c, 0x52, 0x05, 0x72, 0x48, 0x61, 0x73, 0x68, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, - 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x1d, - 0x0a, 0x0a, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x17, 0x20, 0x01, - 0x28, 0x03, 0x52, 0x09, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x4d, 0x73, 0x61, 0x74, 0x12, 0x1c, 0x0a, - 0x07, 0x73, 0x65, 0x74, 0x74, 0x6c, 0x65, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x42, 0x02, - 0x18, 0x01, 0x52, 0x07, 0x73, 0x65, 0x74, 0x74, 0x6c, 0x65, 0x64, 0x12, 0x23, 0x0a, 0x0d, 0x63, - 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x64, 0x61, 0x74, 0x65, 0x18, 0x07, 0x20, 0x01, - 0x28, 0x03, 0x52, 0x0c, 0x63, 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x61, 0x74, 0x65, - 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x65, 0x74, 0x74, 0x6c, 0x65, 0x5f, 0x64, 0x61, 0x74, 0x65, 0x18, - 0x08, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x73, 0x65, 0x74, 0x74, 0x6c, 0x65, 0x44, 0x61, 0x74, - 0x65, 0x12, 0x27, 0x0a, 0x0f, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x72, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x70, 0x61, 0x79, 0x6d, - 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x29, 0x0a, 0x10, 0x64, 0x65, - 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x0a, - 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0f, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, - 0x6e, 0x48, 0x61, 0x73, 0x68, 0x12, 0x16, 0x0a, 0x06, 0x65, 0x78, 0x70, 0x69, 0x72, 0x79, 0x18, - 0x0b, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x65, 0x78, 0x70, 0x69, 0x72, 0x79, 0x12, 0x23, 0x0a, - 0x0d, 0x66, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x18, 0x0c, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x66, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x41, 0x64, - 0x64, 0x72, 0x12, 0x1f, 0x0a, 0x0b, 0x63, 0x6c, 0x74, 0x76, 0x5f, 0x65, 0x78, 0x70, 0x69, 0x72, - 0x79, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x63, 0x6c, 0x74, 0x76, 0x45, 0x78, 0x70, - 0x69, 0x72, 0x79, 0x12, 0x31, 0x0a, 0x0b, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x5f, 0x68, 0x69, 0x6e, - 0x74, 0x73, 0x18, 0x0e, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, - 0x2e, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x48, 0x69, 0x6e, 0x74, 0x52, 0x0a, 0x72, 0x6f, 0x75, 0x74, - 0x65, 0x48, 0x69, 0x6e, 0x74, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, - 0x65, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, - 0x12, 0x1b, 0x0a, 0x09, 0x61, 0x64, 0x64, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x10, 0x20, - 0x01, 0x28, 0x04, 0x52, 0x08, 0x61, 0x64, 0x64, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x21, 0x0a, - 0x0c, 0x73, 0x65, 0x74, 0x74, 0x6c, 0x65, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x11, 0x20, - 0x01, 0x28, 0x04, 0x52, 0x0b, 0x73, 0x65, 0x74, 0x74, 0x6c, 0x65, 0x49, 0x6e, 0x64, 0x65, 0x78, - 0x12, 0x1d, 0x0a, 0x08, 0x61, 0x6d, 0x74, 0x5f, 0x70, 0x61, 0x69, 0x64, 0x18, 0x12, 0x20, 0x01, - 0x28, 0x03, 0x42, 0x02, 0x18, 0x01, 0x52, 0x07, 0x61, 0x6d, 0x74, 0x50, 0x61, 0x69, 0x64, 0x12, - 0x20, 0x0a, 0x0c, 0x61, 0x6d, 0x74, 0x5f, 0x70, 0x61, 0x69, 0x64, 0x5f, 0x73, 0x61, 0x74, 0x18, - 0x13, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x61, 0x6d, 0x74, 0x50, 0x61, 0x69, 0x64, 0x53, 0x61, - 0x74, 0x12, 0x22, 0x0a, 0x0d, 0x61, 0x6d, 0x74, 0x5f, 0x70, 0x61, 0x69, 0x64, 0x5f, 0x6d, 0x73, - 0x61, 0x74, 0x18, 0x14, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b, 0x61, 0x6d, 0x74, 0x50, 0x61, 0x69, - 0x64, 0x4d, 0x73, 0x61, 0x74, 0x12, 0x31, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x15, - 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1b, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x49, 0x6e, 0x76, - 0x6f, 0x69, 0x63, 0x65, 0x2e, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x53, 0x74, 0x61, 0x74, - 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x28, 0x0a, 0x05, 0x68, 0x74, 0x6c, 0x63, - 0x73, 0x18, 0x16, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, - 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x48, 0x54, 0x4c, 0x43, 0x52, 0x05, 0x68, 0x74, 0x6c, - 0x63, 0x73, 0x12, 0x38, 0x0a, 0x08, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x18, 0x18, - 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x49, 0x6e, 0x76, - 0x6f, 0x69, 0x63, 0x65, 0x2e, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x45, 0x6e, 0x74, - 0x72, 0x79, 0x52, 0x08, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x12, 0x1d, 0x0a, 0x0a, - 0x69, 0x73, 0x5f, 0x6b, 0x65, 0x79, 0x73, 0x65, 0x6e, 0x64, 0x18, 0x19, 0x20, 0x01, 0x28, 0x08, - 0x52, 0x09, 0x69, 0x73, 0x4b, 0x65, 0x79, 0x73, 0x65, 0x6e, 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x70, - 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x18, 0x1a, 0x20, 0x01, 0x28, - 0x0c, 0x52, 0x0b, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x41, 0x64, 0x64, 0x72, 0x12, 0x15, - 0x0a, 0x06, 0x69, 0x73, 0x5f, 0x61, 0x6d, 0x70, 0x18, 0x1b, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, - 0x69, 0x73, 0x41, 0x6d, 0x70, 0x12, 0x4f, 0x0a, 0x11, 0x61, 0x6d, 0x70, 0x5f, 0x69, 0x6e, 0x76, - 0x6f, 0x69, 0x63, 0x65, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x1c, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x23, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, - 0x2e, 0x41, 0x6d, 0x70, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, - 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0f, 0x61, 0x6d, 0x70, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, - 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x69, 0x73, 0x5f, 0x62, 0x6c, 0x69, - 0x6e, 0x64, 0x65, 0x64, 0x18, 0x1d, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x69, 0x73, 0x42, 0x6c, - 0x69, 0x6e, 0x64, 0x65, 0x64, 0x12, 0x48, 0x0a, 0x13, 0x62, 0x6c, 0x69, 0x6e, 0x64, 0x65, 0x64, - 0x5f, 0x70, 0x61, 0x74, 0x68, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x1e, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x42, 0x6c, 0x69, 0x6e, 0x64, - 0x65, 0x64, 0x50, 0x61, 0x74, 0x68, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x11, 0x62, 0x6c, - 0x69, 0x6e, 0x64, 0x65, 0x64, 0x50, 0x61, 0x74, 0x68, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x1a, - 0x4b, 0x0a, 0x0d, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, - 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x6b, - 0x65, 0x79, 0x12, 0x24, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x0e, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, - 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x5a, 0x0a, 0x14, - 0x41, 0x6d, 0x70, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x45, - 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x2c, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x4d, - 0x50, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x05, 0x76, - 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x41, 0x0a, 0x0c, 0x49, 0x6e, 0x76, 0x6f, - 0x69, 0x63, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x08, 0x0a, 0x04, 0x4f, 0x50, 0x45, 0x4e, - 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x53, 0x45, 0x54, 0x54, 0x4c, 0x45, 0x44, 0x10, 0x01, 0x12, - 0x0c, 0x0a, 0x08, 0x43, 0x41, 0x4e, 0x43, 0x45, 0x4c, 0x45, 0x44, 0x10, 0x02, 0x12, 0x0c, 0x0a, - 0x08, 0x41, 0x43, 0x43, 0x45, 0x50, 0x54, 0x45, 0x44, 0x10, 0x03, 0x4a, 0x04, 0x08, 0x02, 0x10, - 0x03, 0x22, 0xef, 0x01, 0x0a, 0x11, 0x42, 0x6c, 0x69, 0x6e, 0x64, 0x65, 0x64, 0x50, 0x61, 0x74, - 0x68, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x2e, 0x0a, 0x11, 0x6d, 0x69, 0x6e, 0x5f, 0x6e, - 0x75, 0x6d, 0x5f, 0x72, 0x65, 0x61, 0x6c, 0x5f, 0x68, 0x6f, 0x70, 0x73, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x0d, 0x48, 0x00, 0x52, 0x0e, 0x6d, 0x69, 0x6e, 0x4e, 0x75, 0x6d, 0x52, 0x65, 0x61, 0x6c, - 0x48, 0x6f, 0x70, 0x73, 0x88, 0x01, 0x01, 0x12, 0x1e, 0x0a, 0x08, 0x6e, 0x75, 0x6d, 0x5f, 0x68, - 0x6f, 0x70, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x48, 0x01, 0x52, 0x07, 0x6e, 0x75, 0x6d, - 0x48, 0x6f, 0x70, 0x73, 0x88, 0x01, 0x01, 0x12, 0x27, 0x0a, 0x0d, 0x6d, 0x61, 0x78, 0x5f, 0x6e, - 0x75, 0x6d, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x48, 0x02, - 0x52, 0x0b, 0x6d, 0x61, 0x78, 0x4e, 0x75, 0x6d, 0x50, 0x61, 0x74, 0x68, 0x73, 0x88, 0x01, 0x01, - 0x12, 0x2c, 0x0a, 0x12, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x6f, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, - 0x6e, 0x5f, 0x6c, 0x69, 0x73, 0x74, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x10, 0x6e, 0x6f, - 0x64, 0x65, 0x4f, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x4c, 0x69, 0x73, 0x74, 0x42, 0x14, - 0x0a, 0x12, 0x5f, 0x6d, 0x69, 0x6e, 0x5f, 0x6e, 0x75, 0x6d, 0x5f, 0x72, 0x65, 0x61, 0x6c, 0x5f, - 0x68, 0x6f, 0x70, 0x73, 0x42, 0x0b, 0x0a, 0x09, 0x5f, 0x6e, 0x75, 0x6d, 0x5f, 0x68, 0x6f, 0x70, - 0x73, 0x42, 0x10, 0x0a, 0x0e, 0x5f, 0x6d, 0x61, 0x78, 0x5f, 0x6e, 0x75, 0x6d, 0x5f, 0x70, 0x61, - 0x74, 0x68, 0x73, 0x22, 0xac, 0x04, 0x0a, 0x0b, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x48, - 0x54, 0x4c, 0x43, 0x12, 0x1b, 0x0a, 0x07, 0x63, 0x68, 0x61, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x04, 0x42, 0x02, 0x30, 0x01, 0x52, 0x06, 0x63, 0x68, 0x61, 0x6e, 0x49, 0x64, - 0x12, 0x1d, 0x0a, 0x0a, 0x68, 0x74, 0x6c, 0x63, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x68, 0x74, 0x6c, 0x63, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, - 0x19, 0x0a, 0x08, 0x61, 0x6d, 0x74, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x04, 0x52, 0x07, 0x61, 0x6d, 0x74, 0x4d, 0x73, 0x61, 0x74, 0x12, 0x23, 0x0a, 0x0d, 0x61, 0x63, - 0x63, 0x65, 0x70, 0x74, 0x5f, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, - 0x05, 0x52, 0x0c, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x48, 0x65, 0x69, 0x67, 0x68, 0x74, 0x12, - 0x1f, 0x0a, 0x0b, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x05, - 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x54, 0x69, 0x6d, 0x65, - 0x12, 0x21, 0x0a, 0x0c, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, - 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x54, - 0x69, 0x6d, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x65, 0x78, 0x70, 0x69, 0x72, 0x79, 0x5f, 0x68, 0x65, - 0x69, 0x67, 0x68, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0c, 0x65, 0x78, 0x70, 0x69, - 0x72, 0x79, 0x48, 0x65, 0x69, 0x67, 0x68, 0x74, 0x12, 0x2d, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, - 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x17, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, - 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x48, 0x54, 0x4c, 0x43, 0x53, 0x74, 0x61, 0x74, 0x65, - 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x4c, 0x0a, 0x0e, 0x63, 0x75, 0x73, 0x74, 0x6f, - 0x6d, 0x5f, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x18, 0x09, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x25, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x48, - 0x54, 0x4c, 0x43, 0x2e, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, - 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0d, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x52, 0x65, - 0x63, 0x6f, 0x72, 0x64, 0x73, 0x12, 0x2b, 0x0a, 0x12, 0x6d, 0x70, 0x70, 0x5f, 0x74, 0x6f, 0x74, - 0x61, 0x6c, 0x5f, 0x61, 0x6d, 0x74, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x0a, 0x20, 0x01, 0x28, - 0x04, 0x52, 0x0f, 0x6d, 0x70, 0x70, 0x54, 0x6f, 0x74, 0x61, 0x6c, 0x41, 0x6d, 0x74, 0x4d, 0x73, - 0x61, 0x74, 0x12, 0x1c, 0x0a, 0x03, 0x61, 0x6d, 0x70, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x0a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x4d, 0x50, 0x52, 0x03, 0x61, 0x6d, 0x70, - 0x12, 0x2e, 0x0a, 0x13, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x6e, - 0x65, 0x6c, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x11, 0x63, - 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x44, 0x61, 0x74, 0x61, - 0x1a, 0x40, 0x0a, 0x12, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, + 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x46, 0x0a, 0x13, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, + 0x47, 0x72, 0x61, 0x70, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2f, 0x0a, 0x13, + 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x5f, 0x75, 0x6e, 0x61, 0x6e, 0x6e, 0x6f, 0x75, 0x6e, + 0x63, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x12, 0x69, 0x6e, 0x63, 0x6c, 0x75, + 0x64, 0x65, 0x55, 0x6e, 0x61, 0x6e, 0x6e, 0x6f, 0x75, 0x6e, 0x63, 0x65, 0x64, 0x22, 0x64, 0x0a, + 0x0c, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x47, 0x72, 0x61, 0x70, 0x68, 0x12, 0x2a, 0x0a, + 0x05, 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x6c, + 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x67, 0x68, 0x74, 0x6e, 0x69, 0x6e, 0x67, 0x4e, 0x6f, + 0x64, 0x65, 0x52, 0x05, 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x12, 0x28, 0x0a, 0x05, 0x65, 0x64, 0x67, + 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, + 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x45, 0x64, 0x67, 0x65, 0x52, 0x05, 0x65, 0x64, + 0x67, 0x65, 0x73, 0x22, 0x41, 0x0a, 0x12, 0x4e, 0x6f, 0x64, 0x65, 0x4d, 0x65, 0x74, 0x72, 0x69, + 0x63, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2b, 0x0a, 0x05, 0x74, 0x79, 0x70, + 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0e, 0x32, 0x15, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, + 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x54, 0x79, 0x70, 0x65, 0x52, + 0x05, 0x74, 0x79, 0x70, 0x65, 0x73, 0x22, 0xe1, 0x01, 0x0a, 0x13, 0x4e, 0x6f, 0x64, 0x65, 0x4d, + 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x6c, + 0x0a, 0x16, 0x62, 0x65, 0x74, 0x77, 0x65, 0x65, 0x6e, 0x6e, 0x65, 0x73, 0x73, 0x5f, 0x63, 0x65, + 0x6e, 0x74, 0x72, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x35, + 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x4d, 0x65, 0x74, 0x72, 0x69, + 0x63, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x42, 0x65, 0x74, 0x77, 0x65, + 0x65, 0x6e, 0x6e, 0x65, 0x73, 0x73, 0x43, 0x65, 0x6e, 0x74, 0x72, 0x61, 0x6c, 0x69, 0x74, 0x79, + 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x15, 0x62, 0x65, 0x74, 0x77, 0x65, 0x65, 0x6e, 0x6e, 0x65, + 0x73, 0x73, 0x43, 0x65, 0x6e, 0x74, 0x72, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x1a, 0x5c, 0x0a, 0x1a, + 0x42, 0x65, 0x74, 0x77, 0x65, 0x65, 0x6e, 0x6e, 0x65, 0x73, 0x73, 0x43, 0x65, 0x6e, 0x74, 0x72, + 0x61, 0x6c, 0x69, 0x74, 0x79, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, + 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x28, 0x0a, 0x05, + 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x6c, 0x6e, + 0x72, 0x70, 0x63, 0x2e, 0x46, 0x6c, 0x6f, 0x61, 0x74, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x52, + 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x4e, 0x0a, 0x0b, 0x46, 0x6c, + 0x6f, 0x61, 0x74, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, + 0x75, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x01, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, + 0x29, 0x0a, 0x10, 0x6e, 0x6f, 0x72, 0x6d, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x76, 0x61, + 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x01, 0x52, 0x0f, 0x6e, 0x6f, 0x72, 0x6d, 0x61, + 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x4d, 0x0a, 0x0f, 0x43, 0x68, + 0x61, 0x6e, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, + 0x07, 0x63, 0x68, 0x61, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x42, 0x02, + 0x30, 0x01, 0x52, 0x06, 0x63, 0x68, 0x61, 0x6e, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x68, + 0x61, 0x6e, 0x5f, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, + 0x63, 0x68, 0x61, 0x6e, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x22, 0x14, 0x0a, 0x12, 0x4e, 0x65, 0x74, + 0x77, 0x6f, 0x72, 0x6b, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, + 0xd5, 0x03, 0x0a, 0x0b, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x49, 0x6e, 0x66, 0x6f, 0x12, + 0x25, 0x0a, 0x0e, 0x67, 0x72, 0x61, 0x70, 0x68, 0x5f, 0x64, 0x69, 0x61, 0x6d, 0x65, 0x74, 0x65, + 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0d, 0x67, 0x72, 0x61, 0x70, 0x68, 0x44, 0x69, + 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x12, 0x24, 0x0a, 0x0e, 0x61, 0x76, 0x67, 0x5f, 0x6f, 0x75, + 0x74, 0x5f, 0x64, 0x65, 0x67, 0x72, 0x65, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x01, 0x52, 0x0c, + 0x61, 0x76, 0x67, 0x4f, 0x75, 0x74, 0x44, 0x65, 0x67, 0x72, 0x65, 0x65, 0x12, 0x24, 0x0a, 0x0e, + 0x6d, 0x61, 0x78, 0x5f, 0x6f, 0x75, 0x74, 0x5f, 0x64, 0x65, 0x67, 0x72, 0x65, 0x65, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0c, 0x6d, 0x61, 0x78, 0x4f, 0x75, 0x74, 0x44, 0x65, 0x67, 0x72, + 0x65, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x6e, 0x75, 0x6d, 0x5f, 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x6e, 0x75, 0x6d, 0x4e, 0x6f, 0x64, 0x65, 0x73, 0x12, + 0x21, 0x0a, 0x0c, 0x6e, 0x75, 0x6d, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x18, + 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x6e, 0x75, 0x6d, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, + 0x6c, 0x73, 0x12, 0x34, 0x0a, 0x16, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x6e, 0x65, 0x74, 0x77, + 0x6f, 0x72, 0x6b, 0x5f, 0x63, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x18, 0x06, 0x20, 0x01, + 0x28, 0x03, 0x52, 0x14, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, + 0x43, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x12, 0x28, 0x0a, 0x10, 0x61, 0x76, 0x67, 0x5f, + 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x07, 0x20, 0x01, + 0x28, 0x01, 0x52, 0x0e, 0x61, 0x76, 0x67, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x53, 0x69, + 0x7a, 0x65, 0x12, 0x28, 0x0a, 0x10, 0x6d, 0x69, 0x6e, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, + 0x6c, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0e, 0x6d, 0x69, + 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x28, 0x0a, 0x10, + 0x6d, 0x61, 0x78, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x5f, 0x73, 0x69, 0x7a, 0x65, + 0x18, 0x09, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0e, 0x6d, 0x61, 0x78, 0x43, 0x68, 0x61, 0x6e, 0x6e, + 0x65, 0x6c, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x35, 0x0a, 0x17, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x6e, + 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x5f, 0x73, 0x61, + 0x74, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x03, 0x52, 0x14, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x6e, 0x43, + 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x53, 0x69, 0x7a, 0x65, 0x53, 0x61, 0x74, 0x12, 0x28, 0x0a, + 0x10, 0x6e, 0x75, 0x6d, 0x5f, 0x7a, 0x6f, 0x6d, 0x62, 0x69, 0x65, 0x5f, 0x63, 0x68, 0x61, 0x6e, + 0x73, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0e, 0x6e, 0x75, 0x6d, 0x5a, 0x6f, 0x6d, 0x62, + 0x69, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x73, 0x22, 0x0d, 0x0a, 0x0b, 0x53, 0x74, 0x6f, 0x70, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x0e, 0x0a, 0x0c, 0x53, 0x74, 0x6f, 0x70, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x0a, 0x19, 0x47, 0x72, 0x61, 0x70, 0x68, 0x54, + 0x6f, 0x70, 0x6f, 0x6c, 0x6f, 0x67, 0x79, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, + 0x69, 0x6f, 0x6e, 0x22, 0xcd, 0x01, 0x0a, 0x13, 0x47, 0x72, 0x61, 0x70, 0x68, 0x54, 0x6f, 0x70, + 0x6f, 0x6c, 0x6f, 0x67, 0x79, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x34, 0x0a, 0x0c, 0x6e, + 0x6f, 0x64, 0x65, 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x11, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x55, 0x70, + 0x64, 0x61, 0x74, 0x65, 0x52, 0x0b, 0x6e, 0x6f, 0x64, 0x65, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, + 0x73, 0x12, 0x41, 0x0a, 0x0f, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x5f, 0x75, 0x70, 0x64, + 0x61, 0x74, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x6c, 0x6e, 0x72, + 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x45, 0x64, 0x67, 0x65, 0x55, 0x70, + 0x64, 0x61, 0x74, 0x65, 0x52, 0x0e, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x55, 0x70, 0x64, + 0x61, 0x74, 0x65, 0x73, 0x12, 0x3d, 0x0a, 0x0c, 0x63, 0x6c, 0x6f, 0x73, 0x65, 0x64, 0x5f, 0x63, + 0x68, 0x61, 0x6e, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, + 0x70, 0x63, 0x2e, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x64, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, + 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x0b, 0x63, 0x6c, 0x6f, 0x73, 0x65, 0x64, 0x43, 0x68, + 0x61, 0x6e, 0x73, 0x22, 0xef, 0x02, 0x0a, 0x0a, 0x4e, 0x6f, 0x64, 0x65, 0x55, 0x70, 0x64, 0x61, + 0x74, 0x65, 0x12, 0x20, 0x0a, 0x09, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x18, + 0x01, 0x20, 0x03, 0x28, 0x09, 0x42, 0x02, 0x18, 0x01, 0x52, 0x09, 0x61, 0x64, 0x64, 0x72, 0x65, + 0x73, 0x73, 0x65, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, + 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x69, 0x64, 0x65, 0x6e, + 0x74, 0x69, 0x74, 0x79, 0x4b, 0x65, 0x79, 0x12, 0x2b, 0x0a, 0x0f, 0x67, 0x6c, 0x6f, 0x62, 0x61, + 0x6c, 0x5f, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, + 0x42, 0x02, 0x18, 0x01, 0x52, 0x0e, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x46, 0x65, 0x61, 0x74, + 0x75, 0x72, 0x65, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x18, 0x04, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x05, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x6f, + 0x6c, 0x6f, 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x63, 0x6f, 0x6c, 0x6f, 0x72, + 0x12, 0x39, 0x0a, 0x0e, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, + 0x65, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, + 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x0d, 0x6e, 0x6f, + 0x64, 0x65, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x12, 0x3b, 0x0a, 0x08, 0x66, + 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, + 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, + 0x2e, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x08, + 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x1a, 0x4b, 0x0a, 0x0d, 0x46, 0x65, 0x61, 0x74, + 0x75, 0x72, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x24, 0x0a, 0x05, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x6c, 0x6e, 0x72, + 0x70, 0x63, 0x2e, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x91, 0x02, 0x0a, 0x11, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, + 0x6c, 0x45, 0x64, 0x67, 0x65, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x1b, 0x0a, 0x07, 0x63, + 0x68, 0x61, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x42, 0x02, 0x30, 0x01, + 0x52, 0x06, 0x63, 0x68, 0x61, 0x6e, 0x49, 0x64, 0x12, 0x32, 0x0a, 0x0a, 0x63, 0x68, 0x61, 0x6e, + 0x5f, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x6c, + 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x50, 0x6f, 0x69, 0x6e, + 0x74, 0x52, 0x09, 0x63, 0x68, 0x61, 0x6e, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x1a, 0x0a, 0x08, + 0x63, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, + 0x63, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x12, 0x3b, 0x0a, 0x0e, 0x72, 0x6f, 0x75, 0x74, + 0x69, 0x6e, 0x67, 0x5f, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x14, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, + 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x0d, 0x72, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x50, + 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x29, 0x0a, 0x10, 0x61, 0x64, 0x76, 0x65, 0x72, 0x74, 0x69, + 0x73, 0x69, 0x6e, 0x67, 0x5f, 0x6e, 0x6f, 0x64, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0f, 0x61, 0x64, 0x76, 0x65, 0x72, 0x74, 0x69, 0x73, 0x69, 0x6e, 0x67, 0x4e, 0x6f, 0x64, 0x65, + 0x12, 0x27, 0x0a, 0x0f, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6e, 0x67, 0x5f, 0x6e, + 0x6f, 0x64, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x63, 0x6f, 0x6e, 0x6e, 0x65, + 0x63, 0x74, 0x69, 0x6e, 0x67, 0x4e, 0x6f, 0x64, 0x65, 0x22, 0xa7, 0x01, 0x0a, 0x13, 0x43, 0x6c, + 0x6f, 0x73, 0x65, 0x64, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x55, 0x70, 0x64, 0x61, 0x74, + 0x65, 0x12, 0x1b, 0x0a, 0x07, 0x63, 0x68, 0x61, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x04, 0x42, 0x02, 0x30, 0x01, 0x52, 0x06, 0x63, 0x68, 0x61, 0x6e, 0x49, 0x64, 0x12, 0x1a, + 0x0a, 0x08, 0x63, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, + 0x52, 0x08, 0x63, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x6c, + 0x6f, 0x73, 0x65, 0x64, 0x5f, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x0d, 0x52, 0x0c, 0x63, 0x6c, 0x6f, 0x73, 0x65, 0x64, 0x48, 0x65, 0x69, 0x67, 0x68, 0x74, 0x12, + 0x32, 0x0a, 0x0a, 0x63, 0x68, 0x61, 0x6e, 0x5f, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x04, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, + 0x6e, 0x65, 0x6c, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x09, 0x63, 0x68, 0x61, 0x6e, 0x50, 0x6f, + 0x69, 0x6e, 0x74, 0x22, 0xcf, 0x01, 0x0a, 0x07, 0x48, 0x6f, 0x70, 0x48, 0x69, 0x6e, 0x74, 0x12, + 0x17, 0x0a, 0x07, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x06, 0x6e, 0x6f, 0x64, 0x65, 0x49, 0x64, 0x12, 0x1b, 0x0a, 0x07, 0x63, 0x68, 0x61, 0x6e, + 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x42, 0x02, 0x30, 0x01, 0x52, 0x06, 0x63, + 0x68, 0x61, 0x6e, 0x49, 0x64, 0x12, 0x22, 0x0a, 0x0d, 0x66, 0x65, 0x65, 0x5f, 0x62, 0x61, 0x73, + 0x65, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x66, 0x65, + 0x65, 0x42, 0x61, 0x73, 0x65, 0x4d, 0x73, 0x61, 0x74, 0x12, 0x3e, 0x0a, 0x1b, 0x66, 0x65, 0x65, + 0x5f, 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x72, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x5f, 0x6d, 0x69, + 0x6c, 0x6c, 0x69, 0x6f, 0x6e, 0x74, 0x68, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x19, + 0x66, 0x65, 0x65, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x72, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x4d, + 0x69, 0x6c, 0x6c, 0x69, 0x6f, 0x6e, 0x74, 0x68, 0x73, 0x12, 0x2a, 0x0a, 0x11, 0x63, 0x6c, 0x74, + 0x76, 0x5f, 0x65, 0x78, 0x70, 0x69, 0x72, 0x79, 0x5f, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x18, 0x05, + 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0f, 0x63, 0x6c, 0x74, 0x76, 0x45, 0x78, 0x70, 0x69, 0x72, 0x79, + 0x44, 0x65, 0x6c, 0x74, 0x61, 0x22, 0x1e, 0x0a, 0x05, 0x53, 0x65, 0x74, 0x49, 0x44, 0x12, 0x15, + 0x0a, 0x06, 0x73, 0x65, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, + 0x73, 0x65, 0x74, 0x49, 0x64, 0x22, 0x38, 0x0a, 0x09, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x48, 0x69, + 0x6e, 0x74, 0x12, 0x2b, 0x0a, 0x09, 0x68, 0x6f, 0x70, 0x5f, 0x68, 0x69, 0x6e, 0x74, 0x73, 0x18, + 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x48, 0x6f, + 0x70, 0x48, 0x69, 0x6e, 0x74, 0x52, 0x08, 0x68, 0x6f, 0x70, 0x48, 0x69, 0x6e, 0x74, 0x73, 0x22, + 0xc4, 0x02, 0x0a, 0x12, 0x42, 0x6c, 0x69, 0x6e, 0x64, 0x65, 0x64, 0x50, 0x61, 0x79, 0x6d, 0x65, + 0x6e, 0x74, 0x50, 0x61, 0x74, 0x68, 0x12, 0x35, 0x0a, 0x0c, 0x62, 0x6c, 0x69, 0x6e, 0x64, 0x65, + 0x64, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x6c, + 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x42, 0x6c, 0x69, 0x6e, 0x64, 0x65, 0x64, 0x50, 0x61, 0x74, 0x68, + 0x52, 0x0b, 0x62, 0x6c, 0x69, 0x6e, 0x64, 0x65, 0x64, 0x50, 0x61, 0x74, 0x68, 0x12, 0x22, 0x0a, + 0x0d, 0x62, 0x61, 0x73, 0x65, 0x5f, 0x66, 0x65, 0x65, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x62, 0x61, 0x73, 0x65, 0x46, 0x65, 0x65, 0x4d, 0x73, 0x61, + 0x74, 0x12, 0x32, 0x0a, 0x15, 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x72, 0x74, 0x69, 0x6f, 0x6e, 0x61, + 0x6c, 0x5f, 0x66, 0x65, 0x65, 0x5f, 0x72, 0x61, 0x74, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, + 0x52, 0x13, 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x72, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x46, 0x65, + 0x65, 0x52, 0x61, 0x74, 0x65, 0x12, 0x28, 0x0a, 0x10, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x63, + 0x6c, 0x74, 0x76, 0x5f, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, + 0x0e, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x43, 0x6c, 0x74, 0x76, 0x44, 0x65, 0x6c, 0x74, 0x61, 0x12, + 0x22, 0x0a, 0x0d, 0x68, 0x74, 0x6c, 0x63, 0x5f, 0x6d, 0x69, 0x6e, 0x5f, 0x6d, 0x73, 0x61, 0x74, + 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x68, 0x74, 0x6c, 0x63, 0x4d, 0x69, 0x6e, 0x4d, + 0x73, 0x61, 0x74, 0x12, 0x22, 0x0a, 0x0d, 0x68, 0x74, 0x6c, 0x63, 0x5f, 0x6d, 0x61, 0x78, 0x5f, + 0x6d, 0x73, 0x61, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x68, 0x74, 0x6c, 0x63, + 0x4d, 0x61, 0x78, 0x4d, 0x73, 0x61, 0x74, 0x12, 0x2d, 0x0a, 0x08, 0x66, 0x65, 0x61, 0x74, 0x75, + 0x72, 0x65, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0e, 0x32, 0x11, 0x2e, 0x6c, 0x6e, 0x72, 0x70, + 0x63, 0x2e, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x42, 0x69, 0x74, 0x52, 0x08, 0x66, 0x65, + 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x22, 0x97, 0x01, 0x0a, 0x0b, 0x42, 0x6c, 0x69, 0x6e, 0x64, + 0x65, 0x64, 0x50, 0x61, 0x74, 0x68, 0x12, 0x2b, 0x0a, 0x11, 0x69, 0x6e, 0x74, 0x72, 0x6f, 0x64, + 0x75, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6e, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0c, 0x52, 0x10, 0x69, 0x6e, 0x74, 0x72, 0x6f, 0x64, 0x75, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x4e, + 0x6f, 0x64, 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x62, 0x6c, 0x69, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x5f, + 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0d, 0x62, 0x6c, 0x69, + 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x34, 0x0a, 0x0c, 0x62, 0x6c, + 0x69, 0x6e, 0x64, 0x65, 0x64, 0x5f, 0x68, 0x6f, 0x70, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x11, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x42, 0x6c, 0x69, 0x6e, 0x64, 0x65, 0x64, + 0x48, 0x6f, 0x70, 0x52, 0x0b, 0x62, 0x6c, 0x69, 0x6e, 0x64, 0x65, 0x64, 0x48, 0x6f, 0x70, 0x73, + 0x22, 0x56, 0x0a, 0x0a, 0x42, 0x6c, 0x69, 0x6e, 0x64, 0x65, 0x64, 0x48, 0x6f, 0x70, 0x12, 0x21, + 0x0a, 0x0c, 0x62, 0x6c, 0x69, 0x6e, 0x64, 0x65, 0x64, 0x5f, 0x6e, 0x6f, 0x64, 0x65, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x62, 0x6c, 0x69, 0x6e, 0x64, 0x65, 0x64, 0x4e, 0x6f, 0x64, + 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x65, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x5f, 0x64, + 0x61, 0x74, 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0d, 0x65, 0x6e, 0x63, 0x72, 0x79, + 0x70, 0x74, 0x65, 0x64, 0x44, 0x61, 0x74, 0x61, 0x22, 0xa8, 0x01, 0x0a, 0x0f, 0x41, 0x4d, 0x50, + 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x2d, 0x0a, 0x05, + 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x17, 0x2e, 0x6c, 0x6e, + 0x72, 0x70, 0x63, 0x2e, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x48, 0x54, 0x4c, 0x43, 0x53, + 0x74, 0x61, 0x74, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x73, + 0x65, 0x74, 0x74, 0x6c, 0x65, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x04, 0x52, 0x0b, 0x73, 0x65, 0x74, 0x74, 0x6c, 0x65, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x1f, + 0x0a, 0x0b, 0x73, 0x65, 0x74, 0x74, 0x6c, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x03, 0x52, 0x0a, 0x73, 0x65, 0x74, 0x74, 0x6c, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, + 0x22, 0x0a, 0x0d, 0x61, 0x6d, 0x74, 0x5f, 0x70, 0x61, 0x69, 0x64, 0x5f, 0x6d, 0x73, 0x61, 0x74, + 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b, 0x61, 0x6d, 0x74, 0x50, 0x61, 0x69, 0x64, 0x4d, + 0x73, 0x61, 0x74, 0x22, 0xac, 0x0a, 0x0a, 0x07, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x12, + 0x12, 0x0a, 0x04, 0x6d, 0x65, 0x6d, 0x6f, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6d, + 0x65, 0x6d, 0x6f, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x5f, 0x70, 0x72, 0x65, 0x69, 0x6d, 0x61, 0x67, + 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x72, 0x50, 0x72, 0x65, 0x69, 0x6d, 0x61, + 0x67, 0x65, 0x12, 0x15, 0x0a, 0x06, 0x72, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x0c, 0x52, 0x05, 0x72, 0x48, 0x61, 0x73, 0x68, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, + 0x75, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, + 0x1d, 0x0a, 0x0a, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x17, 0x20, + 0x01, 0x28, 0x03, 0x52, 0x09, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x4d, 0x73, 0x61, 0x74, 0x12, 0x1c, + 0x0a, 0x07, 0x73, 0x65, 0x74, 0x74, 0x6c, 0x65, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x42, + 0x02, 0x18, 0x01, 0x52, 0x07, 0x73, 0x65, 0x74, 0x74, 0x6c, 0x65, 0x64, 0x12, 0x23, 0x0a, 0x0d, + 0x63, 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x64, 0x61, 0x74, 0x65, 0x18, 0x07, 0x20, + 0x01, 0x28, 0x03, 0x52, 0x0c, 0x63, 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x61, 0x74, + 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x65, 0x74, 0x74, 0x6c, 0x65, 0x5f, 0x64, 0x61, 0x74, 0x65, + 0x18, 0x08, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x73, 0x65, 0x74, 0x74, 0x6c, 0x65, 0x44, 0x61, + 0x74, 0x65, 0x12, 0x27, 0x0a, 0x0f, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x72, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x70, 0x61, 0x79, + 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x29, 0x0a, 0x10, 0x64, + 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, + 0x0a, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0f, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, + 0x6f, 0x6e, 0x48, 0x61, 0x73, 0x68, 0x12, 0x16, 0x0a, 0x06, 0x65, 0x78, 0x70, 0x69, 0x72, 0x79, + 0x18, 0x0b, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x65, 0x78, 0x70, 0x69, 0x72, 0x79, 0x12, 0x23, + 0x0a, 0x0d, 0x66, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x18, + 0x0c, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x66, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x41, + 0x64, 0x64, 0x72, 0x12, 0x1f, 0x0a, 0x0b, 0x63, 0x6c, 0x74, 0x76, 0x5f, 0x65, 0x78, 0x70, 0x69, + 0x72, 0x79, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x63, 0x6c, 0x74, 0x76, 0x45, 0x78, + 0x70, 0x69, 0x72, 0x79, 0x12, 0x31, 0x0a, 0x0b, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x5f, 0x68, 0x69, + 0x6e, 0x74, 0x73, 0x18, 0x0e, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x6c, 0x6e, 0x72, 0x70, + 0x63, 0x2e, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x48, 0x69, 0x6e, 0x74, 0x52, 0x0a, 0x72, 0x6f, 0x75, + 0x74, 0x65, 0x48, 0x69, 0x6e, 0x74, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x72, 0x69, 0x76, 0x61, + 0x74, 0x65, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, + 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x61, 0x64, 0x64, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x10, + 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x61, 0x64, 0x64, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x21, + 0x0a, 0x0c, 0x73, 0x65, 0x74, 0x74, 0x6c, 0x65, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x11, + 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x73, 0x65, 0x74, 0x74, 0x6c, 0x65, 0x49, 0x6e, 0x64, 0x65, + 0x78, 0x12, 0x1d, 0x0a, 0x08, 0x61, 0x6d, 0x74, 0x5f, 0x70, 0x61, 0x69, 0x64, 0x18, 0x12, 0x20, + 0x01, 0x28, 0x03, 0x42, 0x02, 0x18, 0x01, 0x52, 0x07, 0x61, 0x6d, 0x74, 0x50, 0x61, 0x69, 0x64, + 0x12, 0x20, 0x0a, 0x0c, 0x61, 0x6d, 0x74, 0x5f, 0x70, 0x61, 0x69, 0x64, 0x5f, 0x73, 0x61, 0x74, + 0x18, 0x13, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x61, 0x6d, 0x74, 0x50, 0x61, 0x69, 0x64, 0x53, + 0x61, 0x74, 0x12, 0x22, 0x0a, 0x0d, 0x61, 0x6d, 0x74, 0x5f, 0x70, 0x61, 0x69, 0x64, 0x5f, 0x6d, + 0x73, 0x61, 0x74, 0x18, 0x14, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b, 0x61, 0x6d, 0x74, 0x50, 0x61, + 0x69, 0x64, 0x4d, 0x73, 0x61, 0x74, 0x12, 0x31, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, + 0x15, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1b, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x49, 0x6e, + 0x76, 0x6f, 0x69, 0x63, 0x65, 0x2e, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x53, 0x74, 0x61, + 0x74, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x28, 0x0a, 0x05, 0x68, 0x74, 0x6c, + 0x63, 0x73, 0x18, 0x16, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, + 0x2e, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x48, 0x54, 0x4c, 0x43, 0x52, 0x05, 0x68, 0x74, + 0x6c, 0x63, 0x73, 0x12, 0x38, 0x0a, 0x08, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x18, + 0x18, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x49, 0x6e, + 0x76, 0x6f, 0x69, 0x63, 0x65, 0x2e, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x45, 0x6e, + 0x74, 0x72, 0x79, 0x52, 0x08, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x12, 0x1d, 0x0a, + 0x0a, 0x69, 0x73, 0x5f, 0x6b, 0x65, 0x79, 0x73, 0x65, 0x6e, 0x64, 0x18, 0x19, 0x20, 0x01, 0x28, + 0x08, 0x52, 0x09, 0x69, 0x73, 0x4b, 0x65, 0x79, 0x73, 0x65, 0x6e, 0x64, 0x12, 0x21, 0x0a, 0x0c, + 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x18, 0x1a, 0x20, 0x01, + 0x28, 0x0c, 0x52, 0x0b, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x41, 0x64, 0x64, 0x72, 0x12, + 0x15, 0x0a, 0x06, 0x69, 0x73, 0x5f, 0x61, 0x6d, 0x70, 0x18, 0x1b, 0x20, 0x01, 0x28, 0x08, 0x52, + 0x05, 0x69, 0x73, 0x41, 0x6d, 0x70, 0x12, 0x4f, 0x0a, 0x11, 0x61, 0x6d, 0x70, 0x5f, 0x69, 0x6e, + 0x76, 0x6f, 0x69, 0x63, 0x65, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x1c, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x23, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, + 0x65, 0x2e, 0x41, 0x6d, 0x70, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x53, 0x74, 0x61, 0x74, + 0x65, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0f, 0x61, 0x6d, 0x70, 0x49, 0x6e, 0x76, 0x6f, 0x69, + 0x63, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x69, 0x73, 0x5f, 0x62, 0x6c, + 0x69, 0x6e, 0x64, 0x65, 0x64, 0x18, 0x1d, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x69, 0x73, 0x42, + 0x6c, 0x69, 0x6e, 0x64, 0x65, 0x64, 0x12, 0x48, 0x0a, 0x13, 0x62, 0x6c, 0x69, 0x6e, 0x64, 0x65, + 0x64, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x1e, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x42, 0x6c, 0x69, 0x6e, + 0x64, 0x65, 0x64, 0x50, 0x61, 0x74, 0x68, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x11, 0x62, + 0x6c, 0x69, 0x6e, 0x64, 0x65, 0x64, 0x50, 0x61, 0x74, 0x68, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, + 0x1a, 0x4b, 0x0a, 0x0d, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, + 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, + 0x6b, 0x65, 0x79, 0x12, 0x24, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x65, 0x61, 0x74, 0x75, + 0x72, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x5a, 0x0a, + 0x14, 0x41, 0x6d, 0x70, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, + 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x2c, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x41, + 0x4d, 0x50, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x05, + 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x41, 0x0a, 0x0c, 0x49, 0x6e, 0x76, + 0x6f, 0x69, 0x63, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x08, 0x0a, 0x04, 0x4f, 0x50, 0x45, + 0x4e, 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x53, 0x45, 0x54, 0x54, 0x4c, 0x45, 0x44, 0x10, 0x01, + 0x12, 0x0c, 0x0a, 0x08, 0x43, 0x41, 0x4e, 0x43, 0x45, 0x4c, 0x45, 0x44, 0x10, 0x02, 0x12, 0x0c, + 0x0a, 0x08, 0x41, 0x43, 0x43, 0x45, 0x50, 0x54, 0x45, 0x44, 0x10, 0x03, 0x4a, 0x04, 0x08, 0x02, + 0x10, 0x03, 0x22, 0xef, 0x01, 0x0a, 0x11, 0x42, 0x6c, 0x69, 0x6e, 0x64, 0x65, 0x64, 0x50, 0x61, + 0x74, 0x68, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x2e, 0x0a, 0x11, 0x6d, 0x69, 0x6e, 0x5f, + 0x6e, 0x75, 0x6d, 0x5f, 0x72, 0x65, 0x61, 0x6c, 0x5f, 0x68, 0x6f, 0x70, 0x73, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0d, 0x48, 0x00, 0x52, 0x0e, 0x6d, 0x69, 0x6e, 0x4e, 0x75, 0x6d, 0x52, 0x65, 0x61, + 0x6c, 0x48, 0x6f, 0x70, 0x73, 0x88, 0x01, 0x01, 0x12, 0x1e, 0x0a, 0x08, 0x6e, 0x75, 0x6d, 0x5f, + 0x68, 0x6f, 0x70, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x48, 0x01, 0x52, 0x07, 0x6e, 0x75, + 0x6d, 0x48, 0x6f, 0x70, 0x73, 0x88, 0x01, 0x01, 0x12, 0x27, 0x0a, 0x0d, 0x6d, 0x61, 0x78, 0x5f, + 0x6e, 0x75, 0x6d, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x48, + 0x02, 0x52, 0x0b, 0x6d, 0x61, 0x78, 0x4e, 0x75, 0x6d, 0x50, 0x61, 0x74, 0x68, 0x73, 0x88, 0x01, + 0x01, 0x12, 0x2c, 0x0a, 0x12, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x6f, 0x6d, 0x69, 0x73, 0x73, 0x69, + 0x6f, 0x6e, 0x5f, 0x6c, 0x69, 0x73, 0x74, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x10, 0x6e, + 0x6f, 0x64, 0x65, 0x4f, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x4c, 0x69, 0x73, 0x74, 0x42, + 0x14, 0x0a, 0x12, 0x5f, 0x6d, 0x69, 0x6e, 0x5f, 0x6e, 0x75, 0x6d, 0x5f, 0x72, 0x65, 0x61, 0x6c, + 0x5f, 0x68, 0x6f, 0x70, 0x73, 0x42, 0x0b, 0x0a, 0x09, 0x5f, 0x6e, 0x75, 0x6d, 0x5f, 0x68, 0x6f, + 0x70, 0x73, 0x42, 0x10, 0x0a, 0x0e, 0x5f, 0x6d, 0x61, 0x78, 0x5f, 0x6e, 0x75, 0x6d, 0x5f, 0x70, + 0x61, 0x74, 0x68, 0x73, 0x22, 0xac, 0x04, 0x0a, 0x0b, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, + 0x48, 0x54, 0x4c, 0x43, 0x12, 0x1b, 0x0a, 0x07, 0x63, 0x68, 0x61, 0x6e, 0x5f, 0x69, 0x64, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x04, 0x42, 0x02, 0x30, 0x01, 0x52, 0x06, 0x63, 0x68, 0x61, 0x6e, 0x49, + 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x68, 0x74, 0x6c, 0x63, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x68, 0x74, 0x6c, 0x63, 0x49, 0x6e, 0x64, 0x65, 0x78, + 0x12, 0x19, 0x0a, 0x08, 0x61, 0x6d, 0x74, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x04, 0x52, 0x07, 0x61, 0x6d, 0x74, 0x4d, 0x73, 0x61, 0x74, 0x12, 0x23, 0x0a, 0x0d, 0x61, + 0x63, 0x63, 0x65, 0x70, 0x74, 0x5f, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x05, 0x52, 0x0c, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x48, 0x65, 0x69, 0x67, 0x68, 0x74, + 0x12, 0x1f, 0x0a, 0x0b, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, + 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x54, 0x69, 0x6d, + 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x5f, 0x74, 0x69, 0x6d, + 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, + 0x54, 0x69, 0x6d, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x65, 0x78, 0x70, 0x69, 0x72, 0x79, 0x5f, 0x68, + 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0c, 0x65, 0x78, 0x70, + 0x69, 0x72, 0x79, 0x48, 0x65, 0x69, 0x67, 0x68, 0x74, 0x12, 0x2d, 0x0a, 0x05, 0x73, 0x74, 0x61, + 0x74, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x17, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, + 0x2e, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x48, 0x54, 0x4c, 0x43, 0x53, 0x74, 0x61, 0x74, + 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x4c, 0x0a, 0x0e, 0x63, 0x75, 0x73, 0x74, + 0x6f, 0x6d, 0x5f, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x18, 0x09, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x25, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, + 0x48, 0x54, 0x4c, 0x43, 0x2e, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x52, 0x65, 0x63, 0x6f, 0x72, + 0x64, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0d, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x52, + 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x12, 0x2b, 0x0a, 0x12, 0x6d, 0x70, 0x70, 0x5f, 0x74, 0x6f, + 0x74, 0x61, 0x6c, 0x5f, 0x61, 0x6d, 0x74, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x0a, 0x20, 0x01, + 0x28, 0x04, 0x52, 0x0f, 0x6d, 0x70, 0x70, 0x54, 0x6f, 0x74, 0x61, 0x6c, 0x41, 0x6d, 0x74, 0x4d, + 0x73, 0x61, 0x74, 0x12, 0x1c, 0x0a, 0x03, 0x61, 0x6d, 0x70, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x0a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x4d, 0x50, 0x52, 0x03, 0x61, 0x6d, + 0x70, 0x12, 0x2e, 0x0a, 0x13, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5f, 0x63, 0x68, 0x61, 0x6e, + 0x6e, 0x65, 0x6c, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x11, + 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x44, 0x61, 0x74, + 0x61, 0x1a, 0x40, 0x0a, 0x12, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x52, 0x65, 0x63, 0x6f, 0x72, + 0x64, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x04, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, + 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, + 0x02, 0x38, 0x01, 0x22, 0x8c, 0x01, 0x0a, 0x03, 0x41, 0x4d, 0x50, 0x12, 0x1d, 0x0a, 0x0a, 0x72, + 0x6f, 0x6f, 0x74, 0x5f, 0x73, 0x68, 0x61, 0x72, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, + 0x09, 0x72, 0x6f, 0x6f, 0x74, 0x53, 0x68, 0x61, 0x72, 0x65, 0x12, 0x15, 0x0a, 0x06, 0x73, 0x65, + 0x74, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x73, 0x65, 0x74, 0x49, + 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x63, 0x68, 0x69, 0x6c, 0x64, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x63, 0x68, 0x69, 0x6c, 0x64, 0x49, 0x6e, 0x64, + 0x65, 0x78, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x61, 0x73, 0x68, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, + 0x52, 0x04, 0x68, 0x61, 0x73, 0x68, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x72, 0x65, 0x69, 0x6d, 0x61, + 0x67, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x70, 0x72, 0x65, 0x69, 0x6d, 0x61, + 0x67, 0x65, 0x22, 0x94, 0x01, 0x0a, 0x12, 0x41, 0x64, 0x64, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, + 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x15, 0x0a, 0x06, 0x72, 0x5f, 0x68, + 0x61, 0x73, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x72, 0x48, 0x61, 0x73, 0x68, + 0x12, 0x27, 0x0a, 0x0f, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x72, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x70, 0x61, 0x79, 0x6d, 0x65, + 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x61, 0x64, 0x64, + 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x10, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x61, 0x64, + 0x64, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, + 0x74, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x18, 0x11, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x70, 0x61, + 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x41, 0x64, 0x64, 0x72, 0x22, 0x46, 0x0a, 0x0b, 0x50, 0x61, 0x79, + 0x6d, 0x65, 0x6e, 0x74, 0x48, 0x61, 0x73, 0x68, 0x12, 0x20, 0x0a, 0x0a, 0x72, 0x5f, 0x68, 0x61, + 0x73, 0x68, 0x5f, 0x73, 0x74, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x02, 0x18, 0x01, + 0x52, 0x08, 0x72, 0x48, 0x61, 0x73, 0x68, 0x53, 0x74, 0x72, 0x12, 0x15, 0x0a, 0x06, 0x72, 0x5f, + 0x68, 0x61, 0x73, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x72, 0x48, 0x61, 0x73, + 0x68, 0x22, 0xfc, 0x01, 0x0a, 0x12, 0x4c, 0x69, 0x73, 0x74, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, + 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x65, 0x6e, 0x64, + 0x69, 0x6e, 0x67, 0x5f, 0x6f, 0x6e, 0x6c, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, + 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x4f, 0x6e, 0x6c, 0x79, 0x12, 0x21, 0x0a, 0x0c, 0x69, + 0x6e, 0x64, 0x65, 0x78, 0x5f, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x04, 0x52, 0x0b, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x12, 0x28, + 0x0a, 0x10, 0x6e, 0x75, 0x6d, 0x5f, 0x6d, 0x61, 0x78, 0x5f, 0x69, 0x6e, 0x76, 0x6f, 0x69, 0x63, + 0x65, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0e, 0x6e, 0x75, 0x6d, 0x4d, 0x61, 0x78, + 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x76, 0x65, + 0x72, 0x73, 0x65, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x72, 0x65, 0x76, 0x65, + 0x72, 0x73, 0x65, 0x64, 0x12, 0x2e, 0x0a, 0x13, 0x63, 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x5f, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x73, 0x74, 0x61, 0x72, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, + 0x04, 0x52, 0x11, 0x63, 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x61, 0x74, 0x65, 0x53, + 0x74, 0x61, 0x72, 0x74, 0x12, 0x2a, 0x0a, 0x11, 0x63, 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x5f, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x65, 0x6e, 0x64, 0x18, 0x08, 0x20, 0x01, 0x28, 0x04, 0x52, + 0x0f, 0x63, 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x61, 0x74, 0x65, 0x45, 0x6e, 0x64, + 0x22, 0x9b, 0x01, 0x0a, 0x13, 0x4c, 0x69, 0x73, 0x74, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2a, 0x0a, 0x08, 0x69, 0x6e, 0x76, 0x6f, + 0x69, 0x63, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x6c, 0x6e, 0x72, + 0x70, 0x63, 0x2e, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x52, 0x08, 0x69, 0x6e, 0x76, 0x6f, + 0x69, 0x63, 0x65, 0x73, 0x12, 0x2a, 0x0a, 0x11, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x69, 0x6e, 0x64, + 0x65, 0x78, 0x5f, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, + 0x0f, 0x6c, 0x61, 0x73, 0x74, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, + 0x12, 0x2c, 0x0a, 0x12, 0x66, 0x69, 0x72, 0x73, 0x74, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x5f, + 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x10, 0x66, 0x69, + 0x72, 0x73, 0x74, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x55, + 0x0a, 0x13, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, + 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1b, 0x0a, 0x09, 0x61, 0x64, 0x64, 0x5f, 0x69, 0x6e, 0x64, + 0x65, 0x78, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x61, 0x64, 0x64, 0x49, 0x6e, 0x64, + 0x65, 0x78, 0x12, 0x21, 0x0a, 0x0c, 0x73, 0x65, 0x74, 0x74, 0x6c, 0x65, 0x5f, 0x69, 0x6e, 0x64, + 0x65, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x73, 0x65, 0x74, 0x74, 0x6c, 0x65, + 0x49, 0x6e, 0x64, 0x65, 0x78, 0x22, 0xcb, 0x06, 0x0a, 0x07, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, + 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x68, 0x61, 0x73, + 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, + 0x48, 0x61, 0x73, 0x68, 0x12, 0x18, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x03, 0x42, 0x02, 0x18, 0x01, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x27, + 0x0a, 0x0d, 0x63, 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x64, 0x61, 0x74, 0x65, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x03, 0x42, 0x02, 0x18, 0x01, 0x52, 0x0c, 0x63, 0x72, 0x65, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x44, 0x61, 0x74, 0x65, 0x12, 0x14, 0x0a, 0x03, 0x66, 0x65, 0x65, 0x18, 0x05, + 0x20, 0x01, 0x28, 0x03, 0x42, 0x02, 0x18, 0x01, 0x52, 0x03, 0x66, 0x65, 0x65, 0x12, 0x29, 0x0a, + 0x10, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x70, 0x72, 0x65, 0x69, 0x6d, 0x61, 0x67, + 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, + 0x50, 0x72, 0x65, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x5f, 0x73, 0x61, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x76, 0x61, 0x6c, + 0x75, 0x65, 0x53, 0x61, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x5f, 0x6d, + 0x73, 0x61, 0x74, 0x18, 0x08, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x4d, 0x73, 0x61, 0x74, 0x12, 0x27, 0x0a, 0x0f, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x5f, + 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x70, + 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x34, 0x0a, + 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1c, 0x2e, + 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x50, 0x61, + 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, + 0x74, 0x75, 0x73, 0x12, 0x17, 0x0a, 0x07, 0x66, 0x65, 0x65, 0x5f, 0x73, 0x61, 0x74, 0x18, 0x0b, + 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x66, 0x65, 0x65, 0x53, 0x61, 0x74, 0x12, 0x19, 0x0a, 0x08, + 0x66, 0x65, 0x65, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, + 0x66, 0x65, 0x65, 0x4d, 0x73, 0x61, 0x74, 0x12, 0x28, 0x0a, 0x10, 0x63, 0x72, 0x65, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x6e, 0x73, 0x18, 0x0d, 0x20, 0x01, 0x28, + 0x03, 0x52, 0x0e, 0x63, 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x69, 0x6d, 0x65, 0x4e, + 0x73, 0x12, 0x28, 0x0a, 0x05, 0x68, 0x74, 0x6c, 0x63, 0x73, 0x18, 0x0e, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x12, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x48, 0x54, 0x4c, 0x43, 0x41, 0x74, 0x74, + 0x65, 0x6d, 0x70, 0x74, 0x52, 0x05, 0x68, 0x74, 0x6c, 0x63, 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x70, + 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x0f, 0x20, 0x01, + 0x28, 0x04, 0x52, 0x0c, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x49, 0x6e, 0x64, 0x65, 0x78, + 0x12, 0x42, 0x0a, 0x0e, 0x66, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x5f, 0x72, 0x65, 0x61, 0x73, + 0x6f, 0x6e, 0x18, 0x10, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1b, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, + 0x2e, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x46, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x52, + 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x52, 0x0d, 0x66, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x52, 0x65, + 0x61, 0x73, 0x6f, 0x6e, 0x12, 0x62, 0x0a, 0x18, 0x66, 0x69, 0x72, 0x73, 0x74, 0x5f, 0x68, 0x6f, + 0x70, 0x5f, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5f, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, + 0x18, 0x11, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x50, + 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x46, 0x69, 0x72, 0x73, 0x74, 0x48, 0x6f, 0x70, 0x43, + 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x45, 0x6e, 0x74, 0x72, + 0x79, 0x52, 0x15, 0x66, 0x69, 0x72, 0x73, 0x74, 0x48, 0x6f, 0x70, 0x43, 0x75, 0x73, 0x74, 0x6f, + 0x6d, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x1a, 0x48, 0x0a, 0x1a, 0x46, 0x69, 0x72, 0x73, + 0x74, 0x48, 0x6f, 0x70, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, - 0x38, 0x01, 0x22, 0x8c, 0x01, 0x0a, 0x03, 0x41, 0x4d, 0x50, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x6f, - 0x6f, 0x74, 0x5f, 0x73, 0x68, 0x61, 0x72, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, - 0x72, 0x6f, 0x6f, 0x74, 0x53, 0x68, 0x61, 0x72, 0x65, 0x12, 0x15, 0x0a, 0x06, 0x73, 0x65, 0x74, - 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x73, 0x65, 0x74, 0x49, 0x64, - 0x12, 0x1f, 0x0a, 0x0b, 0x63, 0x68, 0x69, 0x6c, 0x64, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, - 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x63, 0x68, 0x69, 0x6c, 0x64, 0x49, 0x6e, 0x64, 0x65, - 0x78, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x61, 0x73, 0x68, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, - 0x04, 0x68, 0x61, 0x73, 0x68, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x72, 0x65, 0x69, 0x6d, 0x61, 0x67, - 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x70, 0x72, 0x65, 0x69, 0x6d, 0x61, 0x67, - 0x65, 0x22, 0x94, 0x01, 0x0a, 0x12, 0x41, 0x64, 0x64, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x15, 0x0a, 0x06, 0x72, 0x5f, 0x68, 0x61, - 0x73, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x72, 0x48, 0x61, 0x73, 0x68, 0x12, - 0x27, 0x0a, 0x0f, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, - 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x61, 0x64, 0x64, 0x5f, - 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x10, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x61, 0x64, 0x64, - 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, - 0x5f, 0x61, 0x64, 0x64, 0x72, 0x18, 0x11, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x70, 0x61, 0x79, - 0x6d, 0x65, 0x6e, 0x74, 0x41, 0x64, 0x64, 0x72, 0x22, 0x46, 0x0a, 0x0b, 0x50, 0x61, 0x79, 0x6d, - 0x65, 0x6e, 0x74, 0x48, 0x61, 0x73, 0x68, 0x12, 0x20, 0x0a, 0x0a, 0x72, 0x5f, 0x68, 0x61, 0x73, - 0x68, 0x5f, 0x73, 0x74, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x02, 0x18, 0x01, 0x52, - 0x08, 0x72, 0x48, 0x61, 0x73, 0x68, 0x53, 0x74, 0x72, 0x12, 0x15, 0x0a, 0x06, 0x72, 0x5f, 0x68, - 0x61, 0x73, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x72, 0x48, 0x61, 0x73, 0x68, - 0x22, 0xfc, 0x01, 0x0a, 0x12, 0x4c, 0x69, 0x73, 0x74, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x65, 0x6e, 0x64, 0x69, - 0x6e, 0x67, 0x5f, 0x6f, 0x6e, 0x6c, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x70, - 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x4f, 0x6e, 0x6c, 0x79, 0x12, 0x21, 0x0a, 0x0c, 0x69, 0x6e, - 0x64, 0x65, 0x78, 0x5f, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, - 0x52, 0x0b, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x12, 0x28, 0x0a, - 0x10, 0x6e, 0x75, 0x6d, 0x5f, 0x6d, 0x61, 0x78, 0x5f, 0x69, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, - 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0e, 0x6e, 0x75, 0x6d, 0x4d, 0x61, 0x78, 0x49, - 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x76, 0x65, 0x72, - 0x73, 0x65, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x72, 0x65, 0x76, 0x65, 0x72, - 0x73, 0x65, 0x64, 0x12, 0x2e, 0x0a, 0x13, 0x63, 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, - 0x64, 0x61, 0x74, 0x65, 0x5f, 0x73, 0x74, 0x61, 0x72, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x04, - 0x52, 0x11, 0x63, 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x61, 0x74, 0x65, 0x53, 0x74, - 0x61, 0x72, 0x74, 0x12, 0x2a, 0x0a, 0x11, 0x63, 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, - 0x64, 0x61, 0x74, 0x65, 0x5f, 0x65, 0x6e, 0x64, 0x18, 0x08, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0f, - 0x63, 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x61, 0x74, 0x65, 0x45, 0x6e, 0x64, 0x22, - 0x9b, 0x01, 0x0a, 0x13, 0x4c, 0x69, 0x73, 0x74, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2a, 0x0a, 0x08, 0x69, 0x6e, 0x76, 0x6f, 0x69, - 0x63, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x6c, 0x6e, 0x72, 0x70, - 0x63, 0x2e, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x52, 0x08, 0x69, 0x6e, 0x76, 0x6f, 0x69, - 0x63, 0x65, 0x73, 0x12, 0x2a, 0x0a, 0x11, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x69, 0x6e, 0x64, 0x65, - 0x78, 0x5f, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0f, - 0x6c, 0x61, 0x73, 0x74, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x12, - 0x2c, 0x0a, 0x12, 0x66, 0x69, 0x72, 0x73, 0x74, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x5f, 0x6f, - 0x66, 0x66, 0x73, 0x65, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x10, 0x66, 0x69, 0x72, - 0x73, 0x74, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x55, 0x0a, - 0x13, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, - 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1b, 0x0a, 0x09, 0x61, 0x64, 0x64, 0x5f, 0x69, 0x6e, 0x64, 0x65, - 0x78, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x61, 0x64, 0x64, 0x49, 0x6e, 0x64, 0x65, - 0x78, 0x12, 0x21, 0x0a, 0x0c, 0x73, 0x65, 0x74, 0x74, 0x6c, 0x65, 0x5f, 0x69, 0x6e, 0x64, 0x65, - 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x73, 0x65, 0x74, 0x74, 0x6c, 0x65, 0x49, - 0x6e, 0x64, 0x65, 0x78, 0x22, 0xcb, 0x06, 0x0a, 0x07, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, - 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x68, 0x61, 0x73, 0x68, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x48, - 0x61, 0x73, 0x68, 0x12, 0x18, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x03, 0x42, 0x02, 0x18, 0x01, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x27, 0x0a, - 0x0d, 0x63, 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x64, 0x61, 0x74, 0x65, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x03, 0x42, 0x02, 0x18, 0x01, 0x52, 0x0c, 0x63, 0x72, 0x65, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x44, 0x61, 0x74, 0x65, 0x12, 0x14, 0x0a, 0x03, 0x66, 0x65, 0x65, 0x18, 0x05, 0x20, - 0x01, 0x28, 0x03, 0x42, 0x02, 0x18, 0x01, 0x52, 0x03, 0x66, 0x65, 0x65, 0x12, 0x29, 0x0a, 0x10, - 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x70, 0x72, 0x65, 0x69, 0x6d, 0x61, 0x67, 0x65, - 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x50, - 0x72, 0x65, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x76, 0x61, 0x6c, 0x75, 0x65, - 0x5f, 0x73, 0x61, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x76, 0x61, 0x6c, 0x75, - 0x65, 0x53, 0x61, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x5f, 0x6d, 0x73, - 0x61, 0x74, 0x18, 0x08, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x4d, - 0x73, 0x61, 0x74, 0x12, 0x27, 0x0a, 0x0f, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x72, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x70, 0x61, - 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x34, 0x0a, 0x06, - 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1c, 0x2e, 0x6c, - 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x50, 0x61, 0x79, - 0x6d, 0x65, 0x6e, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, - 0x75, 0x73, 0x12, 0x17, 0x0a, 0x07, 0x66, 0x65, 0x65, 0x5f, 0x73, 0x61, 0x74, 0x18, 0x0b, 0x20, - 0x01, 0x28, 0x03, 0x52, 0x06, 0x66, 0x65, 0x65, 0x53, 0x61, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x66, - 0x65, 0x65, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x66, - 0x65, 0x65, 0x4d, 0x73, 0x61, 0x74, 0x12, 0x28, 0x0a, 0x10, 0x63, 0x72, 0x65, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x6e, 0x73, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x03, - 0x52, 0x0e, 0x63, 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x69, 0x6d, 0x65, 0x4e, 0x73, - 0x12, 0x28, 0x0a, 0x05, 0x68, 0x74, 0x6c, 0x63, 0x73, 0x18, 0x0e, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x12, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x48, 0x54, 0x4c, 0x43, 0x41, 0x74, 0x74, 0x65, - 0x6d, 0x70, 0x74, 0x52, 0x05, 0x68, 0x74, 0x6c, 0x63, 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x70, 0x61, - 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x0f, 0x20, 0x01, 0x28, - 0x04, 0x52, 0x0c, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, - 0x42, 0x0a, 0x0e, 0x66, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x5f, 0x72, 0x65, 0x61, 0x73, 0x6f, - 0x6e, 0x18, 0x10, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1b, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, - 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x46, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x52, 0x65, - 0x61, 0x73, 0x6f, 0x6e, 0x52, 0x0d, 0x66, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x52, 0x65, 0x61, - 0x73, 0x6f, 0x6e, 0x12, 0x62, 0x0a, 0x18, 0x66, 0x69, 0x72, 0x73, 0x74, 0x5f, 0x68, 0x6f, 0x70, - 0x5f, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5f, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x18, - 0x11, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x61, - 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x46, 0x69, 0x72, 0x73, 0x74, 0x48, 0x6f, 0x70, 0x43, 0x75, - 0x73, 0x74, 0x6f, 0x6d, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, - 0x52, 0x15, 0x66, 0x69, 0x72, 0x73, 0x74, 0x48, 0x6f, 0x70, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, - 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x1a, 0x48, 0x0a, 0x1a, 0x46, 0x69, 0x72, 0x73, 0x74, - 0x48, 0x6f, 0x70, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, + 0x38, 0x01, 0x22, 0x59, 0x0a, 0x0d, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x53, 0x74, 0x61, + 0x74, 0x75, 0x73, 0x12, 0x0f, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, + 0x1a, 0x02, 0x08, 0x01, 0x12, 0x0d, 0x0a, 0x09, 0x49, 0x4e, 0x5f, 0x46, 0x4c, 0x49, 0x47, 0x48, + 0x54, 0x10, 0x01, 0x12, 0x0d, 0x0a, 0x09, 0x53, 0x55, 0x43, 0x43, 0x45, 0x45, 0x44, 0x45, 0x44, + 0x10, 0x02, 0x12, 0x0a, 0x0a, 0x06, 0x46, 0x41, 0x49, 0x4c, 0x45, 0x44, 0x10, 0x03, 0x12, 0x0d, + 0x0a, 0x09, 0x49, 0x4e, 0x49, 0x54, 0x49, 0x41, 0x54, 0x45, 0x44, 0x10, 0x04, 0x4a, 0x04, 0x08, + 0x04, 0x10, 0x05, 0x22, 0xd5, 0x02, 0x0a, 0x0b, 0x48, 0x54, 0x4c, 0x43, 0x41, 0x74, 0x74, 0x65, + 0x6d, 0x70, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x61, 0x74, 0x74, 0x65, 0x6d, 0x70, 0x74, 0x5f, 0x69, + 0x64, 0x18, 0x07, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x61, 0x74, 0x74, 0x65, 0x6d, 0x70, 0x74, + 0x49, 0x64, 0x12, 0x35, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0e, 0x32, 0x1d, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x48, 0x54, 0x4c, 0x43, 0x41, + 0x74, 0x74, 0x65, 0x6d, 0x70, 0x74, 0x2e, 0x48, 0x54, 0x4c, 0x43, 0x53, 0x74, 0x61, 0x74, 0x75, + 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x22, 0x0a, 0x05, 0x72, 0x6f, 0x75, + 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, + 0x2e, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x52, 0x05, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x12, 0x26, 0x0a, + 0x0f, 0x61, 0x74, 0x74, 0x65, 0x6d, 0x70, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x6e, 0x73, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0d, 0x61, 0x74, 0x74, 0x65, 0x6d, 0x70, 0x74, 0x54, + 0x69, 0x6d, 0x65, 0x4e, 0x73, 0x12, 0x26, 0x0a, 0x0f, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, + 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x6e, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0d, + 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x4e, 0x73, 0x12, 0x28, 0x0a, + 0x07, 0x66, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, + 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x52, 0x07, + 0x66, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x72, 0x65, 0x69, 0x6d, + 0x61, 0x67, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x70, 0x72, 0x65, 0x69, 0x6d, + 0x61, 0x67, 0x65, 0x22, 0x36, 0x0a, 0x0a, 0x48, 0x54, 0x4c, 0x43, 0x53, 0x74, 0x61, 0x74, 0x75, + 0x73, 0x12, 0x0d, 0x0a, 0x09, 0x49, 0x4e, 0x5f, 0x46, 0x4c, 0x49, 0x47, 0x48, 0x54, 0x10, 0x00, + 0x12, 0x0d, 0x0a, 0x09, 0x53, 0x55, 0x43, 0x43, 0x45, 0x45, 0x44, 0x45, 0x44, 0x10, 0x01, 0x12, + 0x0a, 0x0a, 0x06, 0x46, 0x41, 0x49, 0x4c, 0x45, 0x44, 0x10, 0x02, 0x22, 0xb4, 0x02, 0x0a, 0x13, + 0x4c, 0x69, 0x73, 0x74, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x12, 0x2d, 0x0a, 0x12, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x5f, 0x69, + 0x6e, 0x63, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, + 0x11, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x49, 0x6e, 0x63, 0x6f, 0x6d, 0x70, 0x6c, 0x65, + 0x74, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x5f, 0x6f, 0x66, 0x66, 0x73, + 0x65, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x4f, + 0x66, 0x66, 0x73, 0x65, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x6d, 0x61, 0x78, 0x5f, 0x70, 0x61, 0x79, + 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x6d, 0x61, 0x78, + 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x76, 0x65, + 0x72, 0x73, 0x65, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x72, 0x65, 0x76, 0x65, + 0x72, 0x73, 0x65, 0x64, 0x12, 0x30, 0x0a, 0x14, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x74, 0x6f, + 0x74, 0x61, 0x6c, 0x5f, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x05, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x12, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x54, 0x6f, 0x74, 0x61, 0x6c, 0x50, 0x61, + 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x2e, 0x0a, 0x13, 0x63, 0x72, 0x65, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x5f, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x73, 0x74, 0x61, 0x72, 0x74, 0x18, 0x06, 0x20, + 0x01, 0x28, 0x04, 0x52, 0x11, 0x63, 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x61, 0x74, + 0x65, 0x53, 0x74, 0x61, 0x72, 0x74, 0x12, 0x2a, 0x0a, 0x11, 0x63, 0x72, 0x65, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x5f, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x65, 0x6e, 0x64, 0x18, 0x07, 0x20, 0x01, 0x28, + 0x04, 0x52, 0x0f, 0x63, 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x61, 0x74, 0x65, 0x45, + 0x6e, 0x64, 0x22, 0xca, 0x01, 0x0a, 0x14, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x61, 0x79, 0x6d, 0x65, + 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2a, 0x0a, 0x08, 0x70, + 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0e, 0x2e, + 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x08, 0x70, + 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x2c, 0x0a, 0x12, 0x66, 0x69, 0x72, 0x73, 0x74, + 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x5f, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x04, 0x52, 0x10, 0x66, 0x69, 0x72, 0x73, 0x74, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x4f, + 0x66, 0x66, 0x73, 0x65, 0x74, 0x12, 0x2a, 0x0a, 0x11, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x69, 0x6e, + 0x64, 0x65, 0x78, 0x5f, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, + 0x52, 0x0f, 0x6c, 0x61, 0x73, 0x74, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x4f, 0x66, 0x66, 0x73, 0x65, + 0x74, 0x12, 0x2c, 0x0a, 0x12, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x6e, 0x75, 0x6d, 0x5f, 0x70, + 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x10, 0x74, + 0x6f, 0x74, 0x61, 0x6c, 0x4e, 0x75, 0x6d, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x22, + 0x65, 0x0a, 0x14, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x61, 0x79, 0x6d, 0x65, + 0x6e, 0x74, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x70, + 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x48, 0x61, 0x73, 0x68, 0x12, 0x2a, 0x0a, 0x11, 0x66, 0x61, + 0x69, 0x6c, 0x65, 0x64, 0x5f, 0x68, 0x74, 0x6c, 0x63, 0x73, 0x5f, 0x6f, 0x6e, 0x6c, 0x79, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, 0x66, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x48, 0x74, 0x6c, + 0x63, 0x73, 0x4f, 0x6e, 0x6c, 0x79, 0x22, 0x9b, 0x01, 0x0a, 0x18, 0x44, 0x65, 0x6c, 0x65, 0x74, + 0x65, 0x41, 0x6c, 0x6c, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x12, 0x30, 0x0a, 0x14, 0x66, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x5f, 0x70, 0x61, + 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x5f, 0x6f, 0x6e, 0x6c, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x08, 0x52, 0x12, 0x66, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, + 0x73, 0x4f, 0x6e, 0x6c, 0x79, 0x12, 0x2a, 0x0a, 0x11, 0x66, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x5f, + 0x68, 0x74, 0x6c, 0x63, 0x73, 0x5f, 0x6f, 0x6e, 0x6c, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, + 0x52, 0x0f, 0x66, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x48, 0x74, 0x6c, 0x63, 0x73, 0x4f, 0x6e, 0x6c, + 0x79, 0x12, 0x21, 0x0a, 0x0c, 0x61, 0x6c, 0x6c, 0x5f, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, + 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x61, 0x6c, 0x6c, 0x50, 0x61, 0x79, 0x6d, + 0x65, 0x6e, 0x74, 0x73, 0x22, 0x17, 0x0a, 0x15, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x50, 0x61, + 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x0a, + 0x19, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x41, 0x6c, 0x6c, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, + 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xbf, 0x01, 0x0a, 0x15, 0x41, + 0x62, 0x61, 0x6e, 0x64, 0x6f, 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x12, 0x38, 0x0a, 0x0d, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x5f, + 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x6c, 0x6e, + 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x50, 0x6f, 0x69, 0x6e, 0x74, + 0x52, 0x0c, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x39, + 0x0a, 0x19, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x66, 0x75, 0x6e, 0x64, 0x69, 0x6e, + 0x67, 0x5f, 0x73, 0x68, 0x69, 0x6d, 0x5f, 0x6f, 0x6e, 0x6c, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x08, 0x52, 0x16, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x46, 0x75, 0x6e, 0x64, 0x69, 0x6e, + 0x67, 0x53, 0x68, 0x69, 0x6d, 0x4f, 0x6e, 0x6c, 0x79, 0x12, 0x31, 0x0a, 0x16, 0x69, 0x5f, 0x6b, + 0x6e, 0x6f, 0x77, 0x5f, 0x77, 0x68, 0x61, 0x74, 0x5f, 0x69, 0x5f, 0x61, 0x6d, 0x5f, 0x64, 0x6f, + 0x69, 0x6e, 0x67, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x11, 0x69, 0x4b, 0x6e, 0x6f, 0x77, + 0x57, 0x68, 0x61, 0x74, 0x49, 0x41, 0x6d, 0x44, 0x6f, 0x69, 0x6e, 0x67, 0x22, 0x18, 0x0a, 0x16, + 0x41, 0x62, 0x61, 0x6e, 0x64, 0x6f, 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x46, 0x0a, 0x11, 0x44, 0x65, 0x62, 0x75, 0x67, 0x4c, + 0x65, 0x76, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x73, + 0x68, 0x6f, 0x77, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x04, 0x73, 0x68, 0x6f, 0x77, 0x12, + 0x1d, 0x0a, 0x0a, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x5f, 0x73, 0x70, 0x65, 0x63, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x09, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x53, 0x70, 0x65, 0x63, 0x22, 0x35, + 0x0a, 0x12, 0x44, 0x65, 0x62, 0x75, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x75, 0x62, 0x5f, 0x73, 0x79, 0x73, 0x74, + 0x65, 0x6d, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x75, 0x62, 0x53, 0x79, + 0x73, 0x74, 0x65, 0x6d, 0x73, 0x22, 0x27, 0x0a, 0x0c, 0x50, 0x61, 0x79, 0x52, 0x65, 0x71, 0x53, + 0x74, 0x72, 0x69, 0x6e, 0x67, 0x12, 0x17, 0x0a, 0x07, 0x70, 0x61, 0x79, 0x5f, 0x72, 0x65, 0x71, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x70, 0x61, 0x79, 0x52, 0x65, 0x71, 0x22, 0xf0, + 0x04, 0x0a, 0x06, 0x50, 0x61, 0x79, 0x52, 0x65, 0x71, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, + 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, + 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x21, 0x0a, 0x0c, 0x70, + 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0b, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x48, 0x61, 0x73, 0x68, 0x12, 0x21, + 0x0a, 0x0c, 0x6e, 0x75, 0x6d, 0x5f, 0x73, 0x61, 0x74, 0x6f, 0x73, 0x68, 0x69, 0x73, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b, 0x6e, 0x75, 0x6d, 0x53, 0x61, 0x74, 0x6f, 0x73, 0x68, 0x69, + 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, + 0x16, 0x0a, 0x06, 0x65, 0x78, 0x70, 0x69, 0x72, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, + 0x06, 0x65, 0x78, 0x70, 0x69, 0x72, 0x79, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, + 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, + 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x29, 0x0a, 0x10, 0x64, 0x65, 0x73, + 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x07, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0f, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, + 0x48, 0x61, 0x73, 0x68, 0x12, 0x23, 0x0a, 0x0d, 0x66, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, + 0x5f, 0x61, 0x64, 0x64, 0x72, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x66, 0x61, 0x6c, + 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x41, 0x64, 0x64, 0x72, 0x12, 0x1f, 0x0a, 0x0b, 0x63, 0x6c, 0x74, + 0x76, 0x5f, 0x65, 0x78, 0x70, 0x69, 0x72, 0x79, 0x18, 0x09, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, + 0x63, 0x6c, 0x74, 0x76, 0x45, 0x78, 0x70, 0x69, 0x72, 0x79, 0x12, 0x31, 0x0a, 0x0b, 0x72, 0x6f, + 0x75, 0x74, 0x65, 0x5f, 0x68, 0x69, 0x6e, 0x74, 0x73, 0x18, 0x0a, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x10, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x48, 0x69, 0x6e, + 0x74, 0x52, 0x0a, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x48, 0x69, 0x6e, 0x74, 0x73, 0x12, 0x21, 0x0a, + 0x0c, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x18, 0x0b, 0x20, + 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x41, 0x64, 0x64, 0x72, + 0x12, 0x19, 0x0a, 0x08, 0x6e, 0x75, 0x6d, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x0c, 0x20, 0x01, + 0x28, 0x03, 0x52, 0x07, 0x6e, 0x75, 0x6d, 0x4d, 0x73, 0x61, 0x74, 0x12, 0x37, 0x0a, 0x08, 0x66, + 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x18, 0x0d, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, + 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x61, 0x79, 0x52, 0x65, 0x71, 0x2e, 0x46, 0x65, 0x61, + 0x74, 0x75, 0x72, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x08, 0x66, 0x65, 0x61, 0x74, + 0x75, 0x72, 0x65, 0x73, 0x12, 0x3e, 0x0a, 0x0d, 0x62, 0x6c, 0x69, 0x6e, 0x64, 0x65, 0x64, 0x5f, + 0x70, 0x61, 0x74, 0x68, 0x73, 0x18, 0x0e, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x6c, 0x6e, + 0x72, 0x70, 0x63, 0x2e, 0x42, 0x6c, 0x69, 0x6e, 0x64, 0x65, 0x64, 0x50, 0x61, 0x79, 0x6d, 0x65, + 0x6e, 0x74, 0x50, 0x61, 0x74, 0x68, 0x52, 0x0c, 0x62, 0x6c, 0x69, 0x6e, 0x64, 0x65, 0x64, 0x50, + 0x61, 0x74, 0x68, 0x73, 0x1a, 0x4b, 0x0a, 0x0d, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x04, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, - 0x01, 0x22, 0x59, 0x0a, 0x0d, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x53, 0x74, 0x61, 0x74, - 0x75, 0x73, 0x12, 0x0f, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x1a, - 0x02, 0x08, 0x01, 0x12, 0x0d, 0x0a, 0x09, 0x49, 0x4e, 0x5f, 0x46, 0x4c, 0x49, 0x47, 0x48, 0x54, - 0x10, 0x01, 0x12, 0x0d, 0x0a, 0x09, 0x53, 0x55, 0x43, 0x43, 0x45, 0x45, 0x44, 0x45, 0x44, 0x10, - 0x02, 0x12, 0x0a, 0x0a, 0x06, 0x46, 0x41, 0x49, 0x4c, 0x45, 0x44, 0x10, 0x03, 0x12, 0x0d, 0x0a, - 0x09, 0x49, 0x4e, 0x49, 0x54, 0x49, 0x41, 0x54, 0x45, 0x44, 0x10, 0x04, 0x4a, 0x04, 0x08, 0x04, - 0x10, 0x05, 0x22, 0xd5, 0x02, 0x0a, 0x0b, 0x48, 0x54, 0x4c, 0x43, 0x41, 0x74, 0x74, 0x65, 0x6d, - 0x70, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x61, 0x74, 0x74, 0x65, 0x6d, 0x70, 0x74, 0x5f, 0x69, 0x64, - 0x18, 0x07, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x61, 0x74, 0x74, 0x65, 0x6d, 0x70, 0x74, 0x49, - 0x64, 0x12, 0x35, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x0e, 0x32, 0x1d, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x48, 0x54, 0x4c, 0x43, 0x41, 0x74, - 0x74, 0x65, 0x6d, 0x70, 0x74, 0x2e, 0x48, 0x54, 0x4c, 0x43, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, - 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x22, 0x0a, 0x05, 0x72, 0x6f, 0x75, 0x74, - 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, - 0x52, 0x6f, 0x75, 0x74, 0x65, 0x52, 0x05, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x12, 0x26, 0x0a, 0x0f, - 0x61, 0x74, 0x74, 0x65, 0x6d, 0x70, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x6e, 0x73, 0x18, - 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0d, 0x61, 0x74, 0x74, 0x65, 0x6d, 0x70, 0x74, 0x54, 0x69, - 0x6d, 0x65, 0x4e, 0x73, 0x12, 0x26, 0x0a, 0x0f, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x5f, - 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x6e, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0d, 0x72, - 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x4e, 0x73, 0x12, 0x28, 0x0a, 0x07, - 0x66, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, - 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x52, 0x07, 0x66, - 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x72, 0x65, 0x69, 0x6d, 0x61, - 0x67, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x70, 0x72, 0x65, 0x69, 0x6d, 0x61, - 0x67, 0x65, 0x22, 0x36, 0x0a, 0x0a, 0x48, 0x54, 0x4c, 0x43, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, - 0x12, 0x0d, 0x0a, 0x09, 0x49, 0x4e, 0x5f, 0x46, 0x4c, 0x49, 0x47, 0x48, 0x54, 0x10, 0x00, 0x12, - 0x0d, 0x0a, 0x09, 0x53, 0x55, 0x43, 0x43, 0x45, 0x45, 0x44, 0x45, 0x44, 0x10, 0x01, 0x12, 0x0a, - 0x0a, 0x06, 0x46, 0x41, 0x49, 0x4c, 0x45, 0x44, 0x10, 0x02, 0x22, 0xb4, 0x02, 0x0a, 0x13, 0x4c, - 0x69, 0x73, 0x74, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x12, 0x2d, 0x0a, 0x12, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x5f, 0x69, 0x6e, - 0x63, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x11, - 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x49, 0x6e, 0x63, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, - 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x5f, 0x6f, 0x66, 0x66, 0x73, 0x65, - 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x4f, 0x66, - 0x66, 0x73, 0x65, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x6d, 0x61, 0x78, 0x5f, 0x70, 0x61, 0x79, 0x6d, - 0x65, 0x6e, 0x74, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x6d, 0x61, 0x78, 0x50, - 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x76, 0x65, 0x72, - 0x73, 0x65, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x72, 0x65, 0x76, 0x65, 0x72, - 0x73, 0x65, 0x64, 0x12, 0x30, 0x0a, 0x14, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x74, 0x6f, 0x74, - 0x61, 0x6c, 0x5f, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, - 0x08, 0x52, 0x12, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x54, 0x6f, 0x74, 0x61, 0x6c, 0x50, 0x61, 0x79, - 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x2e, 0x0a, 0x13, 0x63, 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x5f, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x73, 0x74, 0x61, 0x72, 0x74, 0x18, 0x06, 0x20, 0x01, - 0x28, 0x04, 0x52, 0x11, 0x63, 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x61, 0x74, 0x65, - 0x53, 0x74, 0x61, 0x72, 0x74, 0x12, 0x2a, 0x0a, 0x11, 0x63, 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x5f, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x65, 0x6e, 0x64, 0x18, 0x07, 0x20, 0x01, 0x28, 0x04, - 0x52, 0x0f, 0x63, 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x61, 0x74, 0x65, 0x45, 0x6e, - 0x64, 0x22, 0xca, 0x01, 0x0a, 0x14, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, - 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2a, 0x0a, 0x08, 0x70, 0x61, - 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x6c, - 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x08, 0x70, 0x61, - 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x2c, 0x0a, 0x12, 0x66, 0x69, 0x72, 0x73, 0x74, 0x5f, - 0x69, 0x6e, 0x64, 0x65, 0x78, 0x5f, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x04, 0x52, 0x10, 0x66, 0x69, 0x72, 0x73, 0x74, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x4f, 0x66, - 0x66, 0x73, 0x65, 0x74, 0x12, 0x2a, 0x0a, 0x11, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x69, 0x6e, 0x64, - 0x65, 0x78, 0x5f, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, - 0x0f, 0x6c, 0x61, 0x73, 0x74, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, - 0x12, 0x2c, 0x0a, 0x12, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x6e, 0x75, 0x6d, 0x5f, 0x70, 0x61, - 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x10, 0x74, 0x6f, - 0x74, 0x61, 0x6c, 0x4e, 0x75, 0x6d, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x22, 0x65, - 0x0a, 0x14, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, - 0x74, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x70, 0x61, - 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x48, 0x61, 0x73, 0x68, 0x12, 0x2a, 0x0a, 0x11, 0x66, 0x61, 0x69, - 0x6c, 0x65, 0x64, 0x5f, 0x68, 0x74, 0x6c, 0x63, 0x73, 0x5f, 0x6f, 0x6e, 0x6c, 0x79, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, 0x66, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x48, 0x74, 0x6c, 0x63, - 0x73, 0x4f, 0x6e, 0x6c, 0x79, 0x22, 0x9b, 0x01, 0x0a, 0x18, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, - 0x41, 0x6c, 0x6c, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x12, 0x30, 0x0a, 0x14, 0x66, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x5f, 0x70, 0x61, 0x79, - 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x5f, 0x6f, 0x6e, 0x6c, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, - 0x52, 0x12, 0x66, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x73, - 0x4f, 0x6e, 0x6c, 0x79, 0x12, 0x2a, 0x0a, 0x11, 0x66, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x5f, 0x68, - 0x74, 0x6c, 0x63, 0x73, 0x5f, 0x6f, 0x6e, 0x6c, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, - 0x0f, 0x66, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x48, 0x74, 0x6c, 0x63, 0x73, 0x4f, 0x6e, 0x6c, 0x79, - 0x12, 0x21, 0x0a, 0x0c, 0x61, 0x6c, 0x6c, 0x5f, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x73, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x61, 0x6c, 0x6c, 0x50, 0x61, 0x79, 0x6d, 0x65, - 0x6e, 0x74, 0x73, 0x22, 0x17, 0x0a, 0x15, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x50, 0x61, 0x79, - 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x0a, 0x19, - 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x41, 0x6c, 0x6c, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, - 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xbf, 0x01, 0x0a, 0x15, 0x41, 0x62, - 0x61, 0x6e, 0x64, 0x6f, 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x12, 0x38, 0x0a, 0x0d, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x5f, 0x70, - 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x6c, 0x6e, 0x72, - 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x52, - 0x0c, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x39, 0x0a, - 0x19, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x66, 0x75, 0x6e, 0x64, 0x69, 0x6e, 0x67, - 0x5f, 0x73, 0x68, 0x69, 0x6d, 0x5f, 0x6f, 0x6e, 0x6c, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, - 0x52, 0x16, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x46, 0x75, 0x6e, 0x64, 0x69, 0x6e, 0x67, - 0x53, 0x68, 0x69, 0x6d, 0x4f, 0x6e, 0x6c, 0x79, 0x12, 0x31, 0x0a, 0x16, 0x69, 0x5f, 0x6b, 0x6e, - 0x6f, 0x77, 0x5f, 0x77, 0x68, 0x61, 0x74, 0x5f, 0x69, 0x5f, 0x61, 0x6d, 0x5f, 0x64, 0x6f, 0x69, - 0x6e, 0x67, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x11, 0x69, 0x4b, 0x6e, 0x6f, 0x77, 0x57, - 0x68, 0x61, 0x74, 0x49, 0x41, 0x6d, 0x44, 0x6f, 0x69, 0x6e, 0x67, 0x22, 0x18, 0x0a, 0x16, 0x41, - 0x62, 0x61, 0x6e, 0x64, 0x6f, 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x46, 0x0a, 0x11, 0x44, 0x65, 0x62, 0x75, 0x67, 0x4c, 0x65, - 0x76, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x68, - 0x6f, 0x77, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x04, 0x73, 0x68, 0x6f, 0x77, 0x12, 0x1d, - 0x0a, 0x0a, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x5f, 0x73, 0x70, 0x65, 0x63, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x09, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x53, 0x70, 0x65, 0x63, 0x22, 0x35, 0x0a, - 0x12, 0x44, 0x65, 0x62, 0x75, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x75, 0x62, 0x5f, 0x73, 0x79, 0x73, 0x74, 0x65, - 0x6d, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x75, 0x62, 0x53, 0x79, 0x73, - 0x74, 0x65, 0x6d, 0x73, 0x22, 0x27, 0x0a, 0x0c, 0x50, 0x61, 0x79, 0x52, 0x65, 0x71, 0x53, 0x74, - 0x72, 0x69, 0x6e, 0x67, 0x12, 0x17, 0x0a, 0x07, 0x70, 0x61, 0x79, 0x5f, 0x72, 0x65, 0x71, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x70, 0x61, 0x79, 0x52, 0x65, 0x71, 0x22, 0xf0, 0x04, - 0x0a, 0x06, 0x50, 0x61, 0x79, 0x52, 0x65, 0x71, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x74, - 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, - 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x61, - 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x0b, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x48, 0x61, 0x73, 0x68, 0x12, 0x21, 0x0a, - 0x0c, 0x6e, 0x75, 0x6d, 0x5f, 0x73, 0x61, 0x74, 0x6f, 0x73, 0x68, 0x69, 0x73, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x03, 0x52, 0x0b, 0x6e, 0x75, 0x6d, 0x53, 0x61, 0x74, 0x6f, 0x73, 0x68, 0x69, 0x73, - 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x04, 0x20, - 0x01, 0x28, 0x03, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x16, - 0x0a, 0x06, 0x65, 0x78, 0x70, 0x69, 0x72, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, - 0x65, 0x78, 0x70, 0x69, 0x72, 0x79, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, - 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, - 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x29, 0x0a, 0x10, 0x64, 0x65, 0x73, 0x63, - 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x07, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x0f, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x48, - 0x61, 0x73, 0x68, 0x12, 0x23, 0x0a, 0x0d, 0x66, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x5f, - 0x61, 0x64, 0x64, 0x72, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x66, 0x61, 0x6c, 0x6c, - 0x62, 0x61, 0x63, 0x6b, 0x41, 0x64, 0x64, 0x72, 0x12, 0x1f, 0x0a, 0x0b, 0x63, 0x6c, 0x74, 0x76, - 0x5f, 0x65, 0x78, 0x70, 0x69, 0x72, 0x79, 0x18, 0x09, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x63, - 0x6c, 0x74, 0x76, 0x45, 0x78, 0x70, 0x69, 0x72, 0x79, 0x12, 0x31, 0x0a, 0x0b, 0x72, 0x6f, 0x75, - 0x74, 0x65, 0x5f, 0x68, 0x69, 0x6e, 0x74, 0x73, 0x18, 0x0a, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x10, - 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x48, 0x69, 0x6e, 0x74, - 0x52, 0x0a, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x48, 0x69, 0x6e, 0x74, 0x73, 0x12, 0x21, 0x0a, 0x0c, - 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x18, 0x0b, 0x20, 0x01, - 0x28, 0x0c, 0x52, 0x0b, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x41, 0x64, 0x64, 0x72, 0x12, - 0x19, 0x0a, 0x08, 0x6e, 0x75, 0x6d, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x0c, 0x20, 0x01, 0x28, - 0x03, 0x52, 0x07, 0x6e, 0x75, 0x6d, 0x4d, 0x73, 0x61, 0x74, 0x12, 0x37, 0x0a, 0x08, 0x66, 0x65, - 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x18, 0x0d, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x6c, - 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x61, 0x79, 0x52, 0x65, 0x71, 0x2e, 0x46, 0x65, 0x61, 0x74, - 0x75, 0x72, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x08, 0x66, 0x65, 0x61, 0x74, 0x75, - 0x72, 0x65, 0x73, 0x12, 0x3e, 0x0a, 0x0d, 0x62, 0x6c, 0x69, 0x6e, 0x64, 0x65, 0x64, 0x5f, 0x70, - 0x61, 0x74, 0x68, 0x73, 0x18, 0x0e, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x6c, 0x6e, 0x72, - 0x70, 0x63, 0x2e, 0x42, 0x6c, 0x69, 0x6e, 0x64, 0x65, 0x64, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, - 0x74, 0x50, 0x61, 0x74, 0x68, 0x52, 0x0c, 0x62, 0x6c, 0x69, 0x6e, 0x64, 0x65, 0x64, 0x50, 0x61, - 0x74, 0x68, 0x73, 0x1a, 0x4b, 0x0a, 0x0d, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x45, - 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x0d, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x24, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x65, - 0x61, 0x74, 0x75, 0x72, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, - 0x22, 0x59, 0x0a, 0x07, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, - 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, - 0x1f, 0x0a, 0x0b, 0x69, 0x73, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x69, 0x73, 0x52, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, - 0x12, 0x19, 0x0a, 0x08, 0x69, 0x73, 0x5f, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x18, 0x04, 0x20, 0x01, - 0x28, 0x08, 0x52, 0x07, 0x69, 0x73, 0x4b, 0x6e, 0x6f, 0x77, 0x6e, 0x22, 0x12, 0x0a, 0x10, 0x46, - 0x65, 0x65, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, - 0x95, 0x02, 0x0a, 0x10, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x46, 0x65, 0x65, 0x52, 0x65, - 0x70, 0x6f, 0x72, 0x74, 0x12, 0x1b, 0x0a, 0x07, 0x63, 0x68, 0x61, 0x6e, 0x5f, 0x69, 0x64, 0x18, - 0x05, 0x20, 0x01, 0x28, 0x04, 0x42, 0x02, 0x30, 0x01, 0x52, 0x06, 0x63, 0x68, 0x61, 0x6e, 0x49, - 0x64, 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x5f, 0x70, 0x6f, 0x69, - 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, - 0x6c, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x22, 0x0a, 0x0d, 0x62, 0x61, 0x73, 0x65, 0x5f, 0x66, - 0x65, 0x65, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b, 0x62, - 0x61, 0x73, 0x65, 0x46, 0x65, 0x65, 0x4d, 0x73, 0x61, 0x74, 0x12, 0x1e, 0x0a, 0x0b, 0x66, 0x65, - 0x65, 0x5f, 0x70, 0x65, 0x72, 0x5f, 0x6d, 0x69, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, - 0x09, 0x66, 0x65, 0x65, 0x50, 0x65, 0x72, 0x4d, 0x69, 0x6c, 0x12, 0x19, 0x0a, 0x08, 0x66, 0x65, - 0x65, 0x5f, 0x72, 0x61, 0x74, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x01, 0x52, 0x07, 0x66, 0x65, - 0x65, 0x52, 0x61, 0x74, 0x65, 0x12, 0x31, 0x0a, 0x15, 0x69, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, - 0x5f, 0x62, 0x61, 0x73, 0x65, 0x5f, 0x66, 0x65, 0x65, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x06, - 0x20, 0x01, 0x28, 0x05, 0x52, 0x12, 0x69, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x42, 0x61, 0x73, - 0x65, 0x46, 0x65, 0x65, 0x4d, 0x73, 0x61, 0x74, 0x12, 0x2d, 0x0a, 0x13, 0x69, 0x6e, 0x62, 0x6f, - 0x75, 0x6e, 0x64, 0x5f, 0x66, 0x65, 0x65, 0x5f, 0x70, 0x65, 0x72, 0x5f, 0x6d, 0x69, 0x6c, 0x18, - 0x07, 0x20, 0x01, 0x28, 0x05, 0x52, 0x10, 0x69, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x46, 0x65, - 0x65, 0x50, 0x65, 0x72, 0x4d, 0x69, 0x6c, 0x22, 0xb5, 0x01, 0x0a, 0x11, 0x46, 0x65, 0x65, 0x52, - 0x65, 0x70, 0x6f, 0x72, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3a, 0x0a, - 0x0c, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x5f, 0x66, 0x65, 0x65, 0x73, 0x18, 0x01, 0x20, - 0x03, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, - 0x6e, 0x65, 0x6c, 0x46, 0x65, 0x65, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x52, 0x0b, 0x63, 0x68, - 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x46, 0x65, 0x65, 0x73, 0x12, 0x1e, 0x0a, 0x0b, 0x64, 0x61, 0x79, - 0x5f, 0x66, 0x65, 0x65, 0x5f, 0x73, 0x75, 0x6d, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, - 0x64, 0x61, 0x79, 0x46, 0x65, 0x65, 0x53, 0x75, 0x6d, 0x12, 0x20, 0x0a, 0x0c, 0x77, 0x65, 0x65, - 0x6b, 0x5f, 0x66, 0x65, 0x65, 0x5f, 0x73, 0x75, 0x6d, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, - 0x0a, 0x77, 0x65, 0x65, 0x6b, 0x46, 0x65, 0x65, 0x53, 0x75, 0x6d, 0x12, 0x22, 0x0a, 0x0d, 0x6d, - 0x6f, 0x6e, 0x74, 0x68, 0x5f, 0x66, 0x65, 0x65, 0x5f, 0x73, 0x75, 0x6d, 0x18, 0x04, 0x20, 0x01, - 0x28, 0x04, 0x52, 0x0b, 0x6d, 0x6f, 0x6e, 0x74, 0x68, 0x46, 0x65, 0x65, 0x53, 0x75, 0x6d, 0x22, - 0x52, 0x0a, 0x0a, 0x49, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x46, 0x65, 0x65, 0x12, 0x22, 0x0a, - 0x0d, 0x62, 0x61, 0x73, 0x65, 0x5f, 0x66, 0x65, 0x65, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x05, 0x52, 0x0b, 0x62, 0x61, 0x73, 0x65, 0x46, 0x65, 0x65, 0x4d, 0x73, 0x61, - 0x74, 0x12, 0x20, 0x0a, 0x0c, 0x66, 0x65, 0x65, 0x5f, 0x72, 0x61, 0x74, 0x65, 0x5f, 0x70, 0x70, - 0x6d, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x66, 0x65, 0x65, 0x52, 0x61, 0x74, 0x65, - 0x50, 0x70, 0x6d, 0x22, 0xaa, 0x03, 0x0a, 0x13, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x55, 0x70, - 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x06, 0x67, - 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x48, 0x00, 0x52, 0x06, 0x67, - 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x12, 0x34, 0x0a, 0x0a, 0x63, 0x68, 0x61, 0x6e, 0x5f, 0x70, 0x6f, - 0x69, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x6c, 0x6e, 0x72, 0x70, - 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x48, 0x00, - 0x52, 0x09, 0x63, 0x68, 0x61, 0x6e, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x22, 0x0a, 0x0d, 0x62, - 0x61, 0x73, 0x65, 0x5f, 0x66, 0x65, 0x65, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x03, 0x52, 0x0b, 0x62, 0x61, 0x73, 0x65, 0x46, 0x65, 0x65, 0x4d, 0x73, 0x61, 0x74, 0x12, - 0x19, 0x0a, 0x08, 0x66, 0x65, 0x65, 0x5f, 0x72, 0x61, 0x74, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, - 0x01, 0x52, 0x07, 0x66, 0x65, 0x65, 0x52, 0x61, 0x74, 0x65, 0x12, 0x20, 0x0a, 0x0c, 0x66, 0x65, - 0x65, 0x5f, 0x72, 0x61, 0x74, 0x65, 0x5f, 0x70, 0x70, 0x6d, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0d, - 0x52, 0x0a, 0x66, 0x65, 0x65, 0x52, 0x61, 0x74, 0x65, 0x50, 0x70, 0x6d, 0x12, 0x26, 0x0a, 0x0f, - 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x18, - 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0d, 0x74, 0x69, 0x6d, 0x65, 0x4c, 0x6f, 0x63, 0x6b, 0x44, - 0x65, 0x6c, 0x74, 0x61, 0x12, 0x22, 0x0a, 0x0d, 0x6d, 0x61, 0x78, 0x5f, 0x68, 0x74, 0x6c, 0x63, - 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x6d, 0x61, 0x78, - 0x48, 0x74, 0x6c, 0x63, 0x4d, 0x73, 0x61, 0x74, 0x12, 0x22, 0x0a, 0x0d, 0x6d, 0x69, 0x6e, 0x5f, - 0x68, 0x74, 0x6c, 0x63, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x04, 0x52, - 0x0b, 0x6d, 0x69, 0x6e, 0x48, 0x74, 0x6c, 0x63, 0x4d, 0x73, 0x61, 0x74, 0x12, 0x35, 0x0a, 0x17, - 0x6d, 0x69, 0x6e, 0x5f, 0x68, 0x74, 0x6c, 0x63, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x5f, 0x73, 0x70, - 0x65, 0x63, 0x69, 0x66, 0x69, 0x65, 0x64, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, 0x14, 0x6d, - 0x69, 0x6e, 0x48, 0x74, 0x6c, 0x63, 0x4d, 0x73, 0x61, 0x74, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, - 0x69, 0x65, 0x64, 0x12, 0x32, 0x0a, 0x0b, 0x69, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x5f, 0x66, - 0x65, 0x65, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, - 0x2e, 0x49, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x46, 0x65, 0x65, 0x52, 0x0a, 0x69, 0x6e, 0x62, - 0x6f, 0x75, 0x6e, 0x64, 0x46, 0x65, 0x65, 0x42, 0x07, 0x0a, 0x05, 0x73, 0x63, 0x6f, 0x70, 0x65, - 0x22, 0x8c, 0x01, 0x0a, 0x0c, 0x46, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x55, 0x70, 0x64, 0x61, 0x74, - 0x65, 0x12, 0x2b, 0x0a, 0x08, 0x6f, 0x75, 0x74, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4f, 0x75, 0x74, 0x50, - 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x08, 0x6f, 0x75, 0x74, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x2c, - 0x0a, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x14, - 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x46, 0x61, 0x69, - 0x6c, 0x75, 0x72, 0x65, 0x52, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x12, 0x21, 0x0a, 0x0c, - 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x0b, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x22, - 0x52, 0x0a, 0x14, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3a, 0x0a, 0x0e, 0x66, 0x61, 0x69, 0x6c, 0x65, - 0x64, 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x13, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x55, 0x70, - 0x64, 0x61, 0x74, 0x65, 0x52, 0x0d, 0x66, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x55, 0x70, 0x64, 0x61, - 0x74, 0x65, 0x73, 0x22, 0xc9, 0x01, 0x0a, 0x18, 0x46, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x69, - 0x6e, 0x67, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x73, 0x74, 0x61, 0x72, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x12, - 0x19, 0x0a, 0x08, 0x65, 0x6e, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x04, 0x52, 0x07, 0x65, 0x6e, 0x64, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x69, 0x6e, - 0x64, 0x65, 0x78, 0x5f, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, - 0x52, 0x0b, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x12, 0x24, 0x0a, - 0x0e, 0x6e, 0x75, 0x6d, 0x5f, 0x6d, 0x61, 0x78, 0x5f, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x18, - 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0c, 0x6e, 0x75, 0x6d, 0x4d, 0x61, 0x78, 0x45, 0x76, 0x65, - 0x6e, 0x74, 0x73, 0x12, 0x2a, 0x0a, 0x11, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x61, 0x6c, 0x69, 0x61, - 0x73, 0x5f, 0x6c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, - 0x70, 0x65, 0x65, 0x72, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x22, - 0x85, 0x03, 0x0a, 0x0f, 0x46, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x45, 0x76, - 0x65, 0x6e, 0x74, 0x12, 0x20, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x42, 0x02, 0x18, 0x01, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, - 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x20, 0x0a, 0x0a, 0x63, 0x68, 0x61, 0x6e, 0x5f, 0x69, 0x64, - 0x5f, 0x69, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x42, 0x02, 0x30, 0x01, 0x52, 0x08, 0x63, - 0x68, 0x61, 0x6e, 0x49, 0x64, 0x49, 0x6e, 0x12, 0x22, 0x0a, 0x0b, 0x63, 0x68, 0x61, 0x6e, 0x5f, - 0x69, 0x64, 0x5f, 0x6f, 0x75, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x42, 0x02, 0x30, 0x01, - 0x52, 0x09, 0x63, 0x68, 0x61, 0x6e, 0x49, 0x64, 0x4f, 0x75, 0x74, 0x12, 0x15, 0x0a, 0x06, 0x61, - 0x6d, 0x74, 0x5f, 0x69, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x61, 0x6d, 0x74, - 0x49, 0x6e, 0x12, 0x17, 0x0a, 0x07, 0x61, 0x6d, 0x74, 0x5f, 0x6f, 0x75, 0x74, 0x18, 0x06, 0x20, - 0x01, 0x28, 0x04, 0x52, 0x06, 0x61, 0x6d, 0x74, 0x4f, 0x75, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x66, - 0x65, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x04, 0x52, 0x03, 0x66, 0x65, 0x65, 0x12, 0x19, 0x0a, - 0x08, 0x66, 0x65, 0x65, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x08, 0x20, 0x01, 0x28, 0x04, 0x52, - 0x07, 0x66, 0x65, 0x65, 0x4d, 0x73, 0x61, 0x74, 0x12, 0x1e, 0x0a, 0x0b, 0x61, 0x6d, 0x74, 0x5f, - 0x69, 0x6e, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x09, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x61, - 0x6d, 0x74, 0x49, 0x6e, 0x4d, 0x73, 0x61, 0x74, 0x12, 0x20, 0x0a, 0x0c, 0x61, 0x6d, 0x74, 0x5f, - 0x6f, 0x75, 0x74, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, - 0x61, 0x6d, 0x74, 0x4f, 0x75, 0x74, 0x4d, 0x73, 0x61, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x74, 0x69, - 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x5f, 0x6e, 0x73, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x04, - 0x52, 0x0b, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x4e, 0x73, 0x12, 0x22, 0x0a, - 0x0d, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x5f, 0x69, 0x6e, 0x18, 0x0c, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x70, 0x65, 0x65, 0x72, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x49, - 0x6e, 0x12, 0x24, 0x0a, 0x0e, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x5f, - 0x6f, 0x75, 0x74, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x70, 0x65, 0x65, 0x72, 0x41, - 0x6c, 0x69, 0x61, 0x73, 0x4f, 0x75, 0x74, 0x22, 0x8c, 0x01, 0x0a, 0x19, 0x46, 0x6f, 0x72, 0x77, - 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x43, 0x0a, 0x11, 0x66, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, - 0x69, 0x6e, 0x67, 0x5f, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x16, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, - 0x69, 0x6e, 0x67, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x10, 0x66, 0x6f, 0x72, 0x77, 0x61, 0x72, - 0x64, 0x69, 0x6e, 0x67, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x2a, 0x0a, 0x11, 0x6c, 0x61, - 0x73, 0x74, 0x5f, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0f, 0x6c, 0x61, 0x73, 0x74, 0x4f, 0x66, 0x66, 0x73, 0x65, - 0x74, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x22, 0x50, 0x0a, 0x1a, 0x45, 0x78, 0x70, 0x6f, 0x72, 0x74, - 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x12, 0x32, 0x0a, 0x0a, 0x63, 0x68, 0x61, 0x6e, 0x5f, 0x70, 0x6f, 0x69, - 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, - 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x09, 0x63, - 0x68, 0x61, 0x6e, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x22, 0x64, 0x0a, 0x0d, 0x43, 0x68, 0x61, 0x6e, - 0x6e, 0x65, 0x6c, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x12, 0x32, 0x0a, 0x0a, 0x63, 0x68, 0x61, - 0x6e, 0x5f, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, - 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x50, 0x6f, 0x69, - 0x6e, 0x74, 0x52, 0x09, 0x63, 0x68, 0x61, 0x6e, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x1f, 0x0a, - 0x0b, 0x63, 0x68, 0x61, 0x6e, 0x5f, 0x62, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x0c, 0x52, 0x0a, 0x63, 0x68, 0x61, 0x6e, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x22, 0x73, - 0x0a, 0x0f, 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x43, 0x68, 0x61, 0x6e, 0x42, 0x61, 0x63, 0x6b, 0x75, - 0x70, 0x12, 0x34, 0x0a, 0x0b, 0x63, 0x68, 0x61, 0x6e, 0x5f, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, - 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, - 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x0a, 0x63, 0x68, 0x61, - 0x6e, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x12, 0x2a, 0x0a, 0x11, 0x6d, 0x75, 0x6c, 0x74, 0x69, - 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x5f, 0x62, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x0c, 0x52, 0x0f, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x43, 0x68, 0x61, 0x6e, 0x42, 0x61, 0x63, - 0x6b, 0x75, 0x70, 0x22, 0x19, 0x0a, 0x17, 0x43, 0x68, 0x61, 0x6e, 0x42, 0x61, 0x63, 0x6b, 0x75, - 0x70, 0x45, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x9f, - 0x01, 0x0a, 0x12, 0x43, 0x68, 0x61, 0x6e, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x53, 0x6e, 0x61, - 0x70, 0x73, 0x68, 0x6f, 0x74, 0x12, 0x45, 0x0a, 0x13, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x5f, - 0x63, 0x68, 0x61, 0x6e, 0x5f, 0x62, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x73, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, - 0x65, 0x6c, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x73, 0x52, 0x11, 0x73, 0x69, 0x6e, 0x67, 0x6c, - 0x65, 0x43, 0x68, 0x61, 0x6e, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x73, 0x12, 0x42, 0x0a, 0x11, - 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x5f, 0x62, 0x61, 0x63, 0x6b, 0x75, - 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, - 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x43, 0x68, 0x61, 0x6e, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x52, - 0x0f, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x43, 0x68, 0x61, 0x6e, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, - 0x22, 0x49, 0x0a, 0x0e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x42, 0x61, 0x63, 0x6b, 0x75, - 0x70, 0x73, 0x12, 0x37, 0x0a, 0x0c, 0x63, 0x68, 0x61, 0x6e, 0x5f, 0x62, 0x61, 0x63, 0x6b, 0x75, - 0x70, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, - 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x52, 0x0b, - 0x63, 0x68, 0x61, 0x6e, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x73, 0x22, 0x8e, 0x01, 0x0a, 0x18, - 0x52, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x42, 0x61, 0x63, 0x6b, 0x75, - 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x3a, 0x0a, 0x0c, 0x63, 0x68, 0x61, 0x6e, - 0x5f, 0x62, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, - 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x42, 0x61, - 0x63, 0x6b, 0x75, 0x70, 0x73, 0x48, 0x00, 0x52, 0x0b, 0x63, 0x68, 0x61, 0x6e, 0x42, 0x61, 0x63, - 0x6b, 0x75, 0x70, 0x73, 0x12, 0x2c, 0x0a, 0x11, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x5f, 0x63, 0x68, - 0x61, 0x6e, 0x5f, 0x62, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x48, - 0x00, 0x52, 0x0f, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x43, 0x68, 0x61, 0x6e, 0x42, 0x61, 0x63, 0x6b, - 0x75, 0x70, 0x42, 0x08, 0x0a, 0x06, 0x62, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x22, 0x17, 0x0a, 0x15, - 0x52, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x0a, 0x19, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, - 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, - 0x6f, 0x6e, 0x22, 0x1a, 0x0a, 0x18, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x43, 0x68, 0x61, 0x6e, - 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x44, - 0x0a, 0x12, 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, - 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x16, 0x0a, 0x06, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x12, 0x16, 0x0a, 0x06, - 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x61, 0x63, - 0x74, 0x69, 0x6f, 0x6e, 0x22, 0xb0, 0x01, 0x0a, 0x13, 0x42, 0x61, 0x6b, 0x65, 0x4d, 0x61, 0x63, - 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x3b, 0x0a, 0x0b, - 0x70, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, - 0x0b, 0x32, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, - 0x6f, 0x6e, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x0b, 0x70, 0x65, - 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x1e, 0x0a, 0x0b, 0x72, 0x6f, 0x6f, - 0x74, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, - 0x72, 0x6f, 0x6f, 0x74, 0x4b, 0x65, 0x79, 0x49, 0x64, 0x12, 0x3c, 0x0a, 0x1a, 0x61, 0x6c, 0x6c, - 0x6f, 0x77, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5f, 0x70, 0x65, 0x72, 0x6d, - 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x18, 0x61, - 0x6c, 0x6c, 0x6f, 0x77, 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x50, 0x65, 0x72, 0x6d, - 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x32, 0x0a, 0x14, 0x42, 0x61, 0x6b, 0x65, 0x4d, - 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, - 0x1a, 0x0a, 0x08, 0x6d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x08, 0x6d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x22, 0x18, 0x0a, 0x16, 0x4c, - 0x69, 0x73, 0x74, 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x49, 0x44, 0x73, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x3b, 0x0a, 0x17, 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x61, 0x63, - 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x49, 0x44, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x12, 0x20, 0x0a, 0x0c, 0x72, 0x6f, 0x6f, 0x74, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x69, 0x64, 0x73, - 0x18, 0x01, 0x20, 0x03, 0x28, 0x04, 0x52, 0x0a, 0x72, 0x6f, 0x6f, 0x74, 0x4b, 0x65, 0x79, 0x49, - 0x64, 0x73, 0x22, 0x39, 0x0a, 0x17, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4d, 0x61, 0x63, 0x61, - 0x72, 0x6f, 0x6f, 0x6e, 0x49, 0x44, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1e, 0x0a, - 0x0b, 0x72, 0x6f, 0x6f, 0x74, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x04, 0x52, 0x09, 0x72, 0x6f, 0x6f, 0x74, 0x4b, 0x65, 0x79, 0x49, 0x64, 0x22, 0x34, 0x0a, - 0x18, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x49, - 0x44, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x64, 0x65, 0x6c, - 0x65, 0x74, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x64, 0x65, 0x6c, 0x65, - 0x74, 0x65, 0x64, 0x22, 0x55, 0x0a, 0x16, 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x50, - 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x3b, 0x0a, + 0x28, 0x0d, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x24, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x46, + 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, + 0x01, 0x22, 0x59, 0x0a, 0x07, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x12, 0x12, 0x0a, 0x04, + 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, + 0x12, 0x1f, 0x0a, 0x0b, 0x69, 0x73, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x69, 0x73, 0x52, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, + 0x64, 0x12, 0x19, 0x0a, 0x08, 0x69, 0x73, 0x5f, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x18, 0x04, 0x20, + 0x01, 0x28, 0x08, 0x52, 0x07, 0x69, 0x73, 0x4b, 0x6e, 0x6f, 0x77, 0x6e, 0x22, 0x12, 0x0a, 0x10, + 0x46, 0x65, 0x65, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x22, 0x95, 0x02, 0x0a, 0x10, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x46, 0x65, 0x65, 0x52, + 0x65, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x1b, 0x0a, 0x07, 0x63, 0x68, 0x61, 0x6e, 0x5f, 0x69, 0x64, + 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x42, 0x02, 0x30, 0x01, 0x52, 0x06, 0x63, 0x68, 0x61, 0x6e, + 0x49, 0x64, 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x5f, 0x70, 0x6f, + 0x69, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x63, 0x68, 0x61, 0x6e, 0x6e, + 0x65, 0x6c, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x22, 0x0a, 0x0d, 0x62, 0x61, 0x73, 0x65, 0x5f, + 0x66, 0x65, 0x65, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b, + 0x62, 0x61, 0x73, 0x65, 0x46, 0x65, 0x65, 0x4d, 0x73, 0x61, 0x74, 0x12, 0x1e, 0x0a, 0x0b, 0x66, + 0x65, 0x65, 0x5f, 0x70, 0x65, 0x72, 0x5f, 0x6d, 0x69, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, + 0x52, 0x09, 0x66, 0x65, 0x65, 0x50, 0x65, 0x72, 0x4d, 0x69, 0x6c, 0x12, 0x19, 0x0a, 0x08, 0x66, + 0x65, 0x65, 0x5f, 0x72, 0x61, 0x74, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x01, 0x52, 0x07, 0x66, + 0x65, 0x65, 0x52, 0x61, 0x74, 0x65, 0x12, 0x31, 0x0a, 0x15, 0x69, 0x6e, 0x62, 0x6f, 0x75, 0x6e, + 0x64, 0x5f, 0x62, 0x61, 0x73, 0x65, 0x5f, 0x66, 0x65, 0x65, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, + 0x06, 0x20, 0x01, 0x28, 0x05, 0x52, 0x12, 0x69, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x42, 0x61, + 0x73, 0x65, 0x46, 0x65, 0x65, 0x4d, 0x73, 0x61, 0x74, 0x12, 0x2d, 0x0a, 0x13, 0x69, 0x6e, 0x62, + 0x6f, 0x75, 0x6e, 0x64, 0x5f, 0x66, 0x65, 0x65, 0x5f, 0x70, 0x65, 0x72, 0x5f, 0x6d, 0x69, 0x6c, + 0x18, 0x07, 0x20, 0x01, 0x28, 0x05, 0x52, 0x10, 0x69, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x46, + 0x65, 0x65, 0x50, 0x65, 0x72, 0x4d, 0x69, 0x6c, 0x22, 0xb5, 0x01, 0x0a, 0x11, 0x46, 0x65, 0x65, + 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3a, + 0x0a, 0x0c, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x5f, 0x66, 0x65, 0x65, 0x73, 0x18, 0x01, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, + 0x6e, 0x6e, 0x65, 0x6c, 0x46, 0x65, 0x65, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x52, 0x0b, 0x63, + 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x46, 0x65, 0x65, 0x73, 0x12, 0x1e, 0x0a, 0x0b, 0x64, 0x61, + 0x79, 0x5f, 0x66, 0x65, 0x65, 0x5f, 0x73, 0x75, 0x6d, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, + 0x09, 0x64, 0x61, 0x79, 0x46, 0x65, 0x65, 0x53, 0x75, 0x6d, 0x12, 0x20, 0x0a, 0x0c, 0x77, 0x65, + 0x65, 0x6b, 0x5f, 0x66, 0x65, 0x65, 0x5f, 0x73, 0x75, 0x6d, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, + 0x52, 0x0a, 0x77, 0x65, 0x65, 0x6b, 0x46, 0x65, 0x65, 0x53, 0x75, 0x6d, 0x12, 0x22, 0x0a, 0x0d, + 0x6d, 0x6f, 0x6e, 0x74, 0x68, 0x5f, 0x66, 0x65, 0x65, 0x5f, 0x73, 0x75, 0x6d, 0x18, 0x04, 0x20, + 0x01, 0x28, 0x04, 0x52, 0x0b, 0x6d, 0x6f, 0x6e, 0x74, 0x68, 0x46, 0x65, 0x65, 0x53, 0x75, 0x6d, + 0x22, 0x52, 0x0a, 0x0a, 0x49, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x46, 0x65, 0x65, 0x12, 0x22, + 0x0a, 0x0d, 0x62, 0x61, 0x73, 0x65, 0x5f, 0x66, 0x65, 0x65, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0b, 0x62, 0x61, 0x73, 0x65, 0x46, 0x65, 0x65, 0x4d, 0x73, + 0x61, 0x74, 0x12, 0x20, 0x0a, 0x0c, 0x66, 0x65, 0x65, 0x5f, 0x72, 0x61, 0x74, 0x65, 0x5f, 0x70, + 0x70, 0x6d, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x66, 0x65, 0x65, 0x52, 0x61, 0x74, + 0x65, 0x50, 0x70, 0x6d, 0x22, 0xaa, 0x03, 0x0a, 0x13, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x55, + 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x06, + 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x48, 0x00, 0x52, 0x06, + 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x12, 0x34, 0x0a, 0x0a, 0x63, 0x68, 0x61, 0x6e, 0x5f, 0x70, + 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x6c, 0x6e, 0x72, + 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x48, + 0x00, 0x52, 0x09, 0x63, 0x68, 0x61, 0x6e, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x22, 0x0a, 0x0d, + 0x62, 0x61, 0x73, 0x65, 0x5f, 0x66, 0x65, 0x65, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x03, 0x52, 0x0b, 0x62, 0x61, 0x73, 0x65, 0x46, 0x65, 0x65, 0x4d, 0x73, 0x61, 0x74, + 0x12, 0x19, 0x0a, 0x08, 0x66, 0x65, 0x65, 0x5f, 0x72, 0x61, 0x74, 0x65, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x01, 0x52, 0x07, 0x66, 0x65, 0x65, 0x52, 0x61, 0x74, 0x65, 0x12, 0x20, 0x0a, 0x0c, 0x66, + 0x65, 0x65, 0x5f, 0x72, 0x61, 0x74, 0x65, 0x5f, 0x70, 0x70, 0x6d, 0x18, 0x09, 0x20, 0x01, 0x28, + 0x0d, 0x52, 0x0a, 0x66, 0x65, 0x65, 0x52, 0x61, 0x74, 0x65, 0x50, 0x70, 0x6d, 0x12, 0x26, 0x0a, + 0x0f, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x64, 0x65, 0x6c, 0x74, 0x61, + 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0d, 0x74, 0x69, 0x6d, 0x65, 0x4c, 0x6f, 0x63, 0x6b, + 0x44, 0x65, 0x6c, 0x74, 0x61, 0x12, 0x22, 0x0a, 0x0d, 0x6d, 0x61, 0x78, 0x5f, 0x68, 0x74, 0x6c, + 0x63, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x6d, 0x61, + 0x78, 0x48, 0x74, 0x6c, 0x63, 0x4d, 0x73, 0x61, 0x74, 0x12, 0x22, 0x0a, 0x0d, 0x6d, 0x69, 0x6e, + 0x5f, 0x68, 0x74, 0x6c, 0x63, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x04, + 0x52, 0x0b, 0x6d, 0x69, 0x6e, 0x48, 0x74, 0x6c, 0x63, 0x4d, 0x73, 0x61, 0x74, 0x12, 0x35, 0x0a, + 0x17, 0x6d, 0x69, 0x6e, 0x5f, 0x68, 0x74, 0x6c, 0x63, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x5f, 0x73, + 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x65, 0x64, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, 0x14, + 0x6d, 0x69, 0x6e, 0x48, 0x74, 0x6c, 0x63, 0x4d, 0x73, 0x61, 0x74, 0x53, 0x70, 0x65, 0x63, 0x69, + 0x66, 0x69, 0x65, 0x64, 0x12, 0x32, 0x0a, 0x0b, 0x69, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x5f, + 0x66, 0x65, 0x65, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x6c, 0x6e, 0x72, 0x70, + 0x63, 0x2e, 0x49, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x46, 0x65, 0x65, 0x52, 0x0a, 0x69, 0x6e, + 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x46, 0x65, 0x65, 0x42, 0x07, 0x0a, 0x05, 0x73, 0x63, 0x6f, 0x70, + 0x65, 0x22, 0x8c, 0x01, 0x0a, 0x0c, 0x46, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x55, 0x70, 0x64, 0x61, + 0x74, 0x65, 0x12, 0x2b, 0x0a, 0x08, 0x6f, 0x75, 0x74, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4f, 0x75, 0x74, + 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x08, 0x6f, 0x75, 0x74, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, + 0x2c, 0x0a, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, + 0x14, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x46, 0x61, + 0x69, 0x6c, 0x75, 0x72, 0x65, 0x52, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x12, 0x21, 0x0a, + 0x0c, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0b, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x45, 0x72, 0x72, 0x6f, 0x72, + 0x22, 0x52, 0x0a, 0x14, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3a, 0x0a, 0x0e, 0x66, 0x61, 0x69, 0x6c, + 0x65, 0x64, 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x13, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x55, + 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x0d, 0x66, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x55, 0x70, 0x64, + 0x61, 0x74, 0x65, 0x73, 0x22, 0xc9, 0x01, 0x0a, 0x18, 0x46, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, + 0x69, 0x6e, 0x67, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x73, 0x74, 0x61, 0x72, 0x74, 0x54, 0x69, 0x6d, 0x65, + 0x12, 0x19, 0x0a, 0x08, 0x65, 0x6e, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x04, 0x52, 0x07, 0x65, 0x6e, 0x64, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x69, + 0x6e, 0x64, 0x65, 0x78, 0x5f, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x0d, 0x52, 0x0b, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x12, 0x24, + 0x0a, 0x0e, 0x6e, 0x75, 0x6d, 0x5f, 0x6d, 0x61, 0x78, 0x5f, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0c, 0x6e, 0x75, 0x6d, 0x4d, 0x61, 0x78, 0x45, 0x76, + 0x65, 0x6e, 0x74, 0x73, 0x12, 0x2a, 0x0a, 0x11, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x61, 0x6c, 0x69, + 0x61, 0x73, 0x5f, 0x6c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, + 0x0f, 0x70, 0x65, 0x65, 0x72, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, + 0x22, 0x85, 0x03, 0x0a, 0x0f, 0x46, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x45, + 0x76, 0x65, 0x6e, 0x74, 0x12, 0x20, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, + 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x42, 0x02, 0x18, 0x01, 0x52, 0x09, 0x74, 0x69, 0x6d, + 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x20, 0x0a, 0x0a, 0x63, 0x68, 0x61, 0x6e, 0x5f, 0x69, + 0x64, 0x5f, 0x69, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x42, 0x02, 0x30, 0x01, 0x52, 0x08, + 0x63, 0x68, 0x61, 0x6e, 0x49, 0x64, 0x49, 0x6e, 0x12, 0x22, 0x0a, 0x0b, 0x63, 0x68, 0x61, 0x6e, + 0x5f, 0x69, 0x64, 0x5f, 0x6f, 0x75, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x42, 0x02, 0x30, + 0x01, 0x52, 0x09, 0x63, 0x68, 0x61, 0x6e, 0x49, 0x64, 0x4f, 0x75, 0x74, 0x12, 0x15, 0x0a, 0x06, + 0x61, 0x6d, 0x74, 0x5f, 0x69, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x61, 0x6d, + 0x74, 0x49, 0x6e, 0x12, 0x17, 0x0a, 0x07, 0x61, 0x6d, 0x74, 0x5f, 0x6f, 0x75, 0x74, 0x18, 0x06, + 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x61, 0x6d, 0x74, 0x4f, 0x75, 0x74, 0x12, 0x10, 0x0a, 0x03, + 0x66, 0x65, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x04, 0x52, 0x03, 0x66, 0x65, 0x65, 0x12, 0x19, + 0x0a, 0x08, 0x66, 0x65, 0x65, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x08, 0x20, 0x01, 0x28, 0x04, + 0x52, 0x07, 0x66, 0x65, 0x65, 0x4d, 0x73, 0x61, 0x74, 0x12, 0x1e, 0x0a, 0x0b, 0x61, 0x6d, 0x74, + 0x5f, 0x69, 0x6e, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x09, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, + 0x61, 0x6d, 0x74, 0x49, 0x6e, 0x4d, 0x73, 0x61, 0x74, 0x12, 0x20, 0x0a, 0x0c, 0x61, 0x6d, 0x74, + 0x5f, 0x6f, 0x75, 0x74, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x04, 0x52, + 0x0a, 0x61, 0x6d, 0x74, 0x4f, 0x75, 0x74, 0x4d, 0x73, 0x61, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x74, + 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x5f, 0x6e, 0x73, 0x18, 0x0b, 0x20, 0x01, 0x28, + 0x04, 0x52, 0x0b, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x4e, 0x73, 0x12, 0x22, + 0x0a, 0x0d, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x5f, 0x69, 0x6e, 0x18, + 0x0c, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x70, 0x65, 0x65, 0x72, 0x41, 0x6c, 0x69, 0x61, 0x73, + 0x49, 0x6e, 0x12, 0x24, 0x0a, 0x0e, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x61, 0x6c, 0x69, 0x61, 0x73, + 0x5f, 0x6f, 0x75, 0x74, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x70, 0x65, 0x65, 0x72, + 0x41, 0x6c, 0x69, 0x61, 0x73, 0x4f, 0x75, 0x74, 0x22, 0x8c, 0x01, 0x0a, 0x19, 0x46, 0x6f, 0x72, + 0x77, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x43, 0x0a, 0x11, 0x66, 0x6f, 0x72, 0x77, 0x61, 0x72, + 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x16, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x6f, 0x72, 0x77, 0x61, 0x72, + 0x64, 0x69, 0x6e, 0x67, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x10, 0x66, 0x6f, 0x72, 0x77, 0x61, + 0x72, 0x64, 0x69, 0x6e, 0x67, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x2a, 0x0a, 0x11, 0x6c, + 0x61, 0x73, 0x74, 0x5f, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0f, 0x6c, 0x61, 0x73, 0x74, 0x4f, 0x66, 0x66, 0x73, + 0x65, 0x74, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x22, 0x50, 0x0a, 0x1a, 0x45, 0x78, 0x70, 0x6f, 0x72, + 0x74, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x32, 0x0a, 0x0a, 0x63, 0x68, 0x61, 0x6e, 0x5f, 0x70, 0x6f, + 0x69, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x6c, 0x6e, 0x72, 0x70, + 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x09, + 0x63, 0x68, 0x61, 0x6e, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x22, 0x64, 0x0a, 0x0d, 0x43, 0x68, 0x61, + 0x6e, 0x6e, 0x65, 0x6c, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x12, 0x32, 0x0a, 0x0a, 0x63, 0x68, + 0x61, 0x6e, 0x5f, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, + 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x50, 0x6f, + 0x69, 0x6e, 0x74, 0x52, 0x09, 0x63, 0x68, 0x61, 0x6e, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x1f, + 0x0a, 0x0b, 0x63, 0x68, 0x61, 0x6e, 0x5f, 0x62, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x63, 0x68, 0x61, 0x6e, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x22, + 0x73, 0x0a, 0x0f, 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x43, 0x68, 0x61, 0x6e, 0x42, 0x61, 0x63, 0x6b, + 0x75, 0x70, 0x12, 0x34, 0x0a, 0x0b, 0x63, 0x68, 0x61, 0x6e, 0x5f, 0x70, 0x6f, 0x69, 0x6e, 0x74, + 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, + 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x0a, 0x63, 0x68, + 0x61, 0x6e, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x12, 0x2a, 0x0a, 0x11, 0x6d, 0x75, 0x6c, 0x74, + 0x69, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x5f, 0x62, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0c, 0x52, 0x0f, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x43, 0x68, 0x61, 0x6e, 0x42, 0x61, + 0x63, 0x6b, 0x75, 0x70, 0x22, 0x19, 0x0a, 0x17, 0x43, 0x68, 0x61, 0x6e, 0x42, 0x61, 0x63, 0x6b, + 0x75, 0x70, 0x45, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, + 0x9f, 0x01, 0x0a, 0x12, 0x43, 0x68, 0x61, 0x6e, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x53, 0x6e, + 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x12, 0x45, 0x0a, 0x13, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, + 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x5f, 0x62, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x73, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, + 0x6e, 0x65, 0x6c, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x73, 0x52, 0x11, 0x73, 0x69, 0x6e, 0x67, + 0x6c, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x73, 0x12, 0x42, 0x0a, + 0x11, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x5f, 0x62, 0x61, 0x63, 0x6b, + 0x75, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, + 0x2e, 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x43, 0x68, 0x61, 0x6e, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, + 0x52, 0x0f, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x43, 0x68, 0x61, 0x6e, 0x42, 0x61, 0x63, 0x6b, 0x75, + 0x70, 0x22, 0x49, 0x0a, 0x0e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x42, 0x61, 0x63, 0x6b, + 0x75, 0x70, 0x73, 0x12, 0x37, 0x0a, 0x0c, 0x63, 0x68, 0x61, 0x6e, 0x5f, 0x62, 0x61, 0x63, 0x6b, + 0x75, 0x70, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x6c, 0x6e, 0x72, 0x70, + 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x52, + 0x0b, 0x63, 0x68, 0x61, 0x6e, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x73, 0x22, 0x8e, 0x01, 0x0a, + 0x18, 0x52, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x42, 0x61, 0x63, 0x6b, + 0x75, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x3a, 0x0a, 0x0c, 0x63, 0x68, 0x61, + 0x6e, 0x5f, 0x62, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x15, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x42, + 0x61, 0x63, 0x6b, 0x75, 0x70, 0x73, 0x48, 0x00, 0x52, 0x0b, 0x63, 0x68, 0x61, 0x6e, 0x42, 0x61, + 0x63, 0x6b, 0x75, 0x70, 0x73, 0x12, 0x2c, 0x0a, 0x11, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x5f, 0x63, + 0x68, 0x61, 0x6e, 0x5f, 0x62, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, + 0x48, 0x00, 0x52, 0x0f, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x43, 0x68, 0x61, 0x6e, 0x42, 0x61, 0x63, + 0x6b, 0x75, 0x70, 0x42, 0x08, 0x0a, 0x06, 0x62, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x22, 0x17, 0x0a, + 0x15, 0x52, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x0a, 0x19, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, + 0x6c, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, + 0x69, 0x6f, 0x6e, 0x22, 0x1a, 0x0a, 0x18, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x43, 0x68, 0x61, + 0x6e, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x44, 0x0a, 0x12, 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x50, 0x65, 0x72, 0x6d, 0x69, + 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x16, 0x0a, 0x06, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x12, 0x16, 0x0a, + 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x61, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0xb0, 0x01, 0x0a, 0x13, 0x42, 0x61, 0x6b, 0x65, 0x4d, 0x61, + 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x3b, 0x0a, 0x0b, 0x70, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x0b, 0x70, - 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x18, 0x0a, 0x16, 0x4c, 0x69, - 0x73, 0x74, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x22, 0xe4, 0x01, 0x0a, 0x17, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x65, 0x72, - 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x12, 0x64, 0x0a, 0x12, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x5f, 0x70, 0x65, 0x72, 0x6d, 0x69, - 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x35, 0x2e, 0x6c, - 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, - 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x4d, 0x65, 0x74, - 0x68, 0x6f, 0x64, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x45, 0x6e, - 0x74, 0x72, 0x79, 0x52, 0x11, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x50, 0x65, 0x72, 0x6d, 0x69, - 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x1a, 0x63, 0x0a, 0x16, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, - 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, - 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, - 0x65, 0x79, 0x12, 0x33, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x1d, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, - 0x6f, 0x6e, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x4c, 0x69, 0x73, 0x74, - 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xcc, 0x08, 0x0a, 0x07, - 0x46, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x12, 0x2e, 0x0a, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x61, - 0x69, 0x6c, 0x75, 0x72, 0x65, 0x2e, 0x46, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x43, 0x6f, 0x64, - 0x65, 0x52, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x12, 0x3b, 0x0a, 0x0e, 0x63, 0x68, 0x61, 0x6e, 0x6e, - 0x65, 0x6c, 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x14, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x55, - 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x0d, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x55, 0x70, - 0x64, 0x61, 0x74, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x68, 0x74, 0x6c, 0x63, 0x5f, 0x6d, 0x73, 0x61, - 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x68, 0x74, 0x6c, 0x63, 0x4d, 0x73, 0x61, - 0x74, 0x12, 0x22, 0x0a, 0x0d, 0x6f, 0x6e, 0x69, 0x6f, 0x6e, 0x5f, 0x73, 0x68, 0x61, 0x5f, 0x32, - 0x35, 0x36, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x6f, 0x6e, 0x69, 0x6f, 0x6e, 0x53, - 0x68, 0x61, 0x32, 0x35, 0x36, 0x12, 0x1f, 0x0a, 0x0b, 0x63, 0x6c, 0x74, 0x76, 0x5f, 0x65, 0x78, - 0x70, 0x69, 0x72, 0x79, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x63, 0x6c, 0x74, 0x76, - 0x45, 0x78, 0x70, 0x69, 0x72, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x18, - 0x07, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x12, 0x30, 0x0a, 0x14, - 0x66, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x69, - 0x6e, 0x64, 0x65, 0x78, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x12, 0x66, 0x61, 0x69, 0x6c, - 0x75, 0x72, 0x65, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x16, - 0x0a, 0x06, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, - 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x22, 0x8b, 0x06, 0x0a, 0x0b, 0x46, 0x61, 0x69, 0x6c, 0x75, - 0x72, 0x65, 0x43, 0x6f, 0x64, 0x65, 0x12, 0x0c, 0x0a, 0x08, 0x52, 0x45, 0x53, 0x45, 0x52, 0x56, - 0x45, 0x44, 0x10, 0x00, 0x12, 0x28, 0x0a, 0x24, 0x49, 0x4e, 0x43, 0x4f, 0x52, 0x52, 0x45, 0x43, - 0x54, 0x5f, 0x4f, 0x52, 0x5f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x5f, 0x50, 0x41, 0x59, - 0x4d, 0x45, 0x4e, 0x54, 0x5f, 0x44, 0x45, 0x54, 0x41, 0x49, 0x4c, 0x53, 0x10, 0x01, 0x12, 0x1c, - 0x0a, 0x18, 0x49, 0x4e, 0x43, 0x4f, 0x52, 0x52, 0x45, 0x43, 0x54, 0x5f, 0x50, 0x41, 0x59, 0x4d, - 0x45, 0x4e, 0x54, 0x5f, 0x41, 0x4d, 0x4f, 0x55, 0x4e, 0x54, 0x10, 0x02, 0x12, 0x1f, 0x0a, 0x1b, - 0x46, 0x49, 0x4e, 0x41, 0x4c, 0x5f, 0x49, 0x4e, 0x43, 0x4f, 0x52, 0x52, 0x45, 0x43, 0x54, 0x5f, - 0x43, 0x4c, 0x54, 0x56, 0x5f, 0x45, 0x58, 0x50, 0x49, 0x52, 0x59, 0x10, 0x03, 0x12, 0x1f, 0x0a, + 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x1e, 0x0a, 0x0b, 0x72, 0x6f, + 0x6f, 0x74, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, + 0x09, 0x72, 0x6f, 0x6f, 0x74, 0x4b, 0x65, 0x79, 0x49, 0x64, 0x12, 0x3c, 0x0a, 0x1a, 0x61, 0x6c, + 0x6c, 0x6f, 0x77, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5f, 0x70, 0x65, 0x72, + 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x18, + 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x50, 0x65, 0x72, + 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x32, 0x0a, 0x14, 0x42, 0x61, 0x6b, 0x65, + 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x1a, 0x0a, 0x08, 0x6d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x08, 0x6d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x22, 0x18, 0x0a, 0x16, + 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x49, 0x44, 0x73, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x3b, 0x0a, 0x17, 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x61, + 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x49, 0x44, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x20, 0x0a, 0x0c, 0x72, 0x6f, 0x6f, 0x74, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x69, 0x64, + 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x04, 0x52, 0x0a, 0x72, 0x6f, 0x6f, 0x74, 0x4b, 0x65, 0x79, + 0x49, 0x64, 0x73, 0x22, 0x39, 0x0a, 0x17, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4d, 0x61, 0x63, + 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x49, 0x44, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1e, + 0x0a, 0x0b, 0x72, 0x6f, 0x6f, 0x74, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x04, 0x52, 0x09, 0x72, 0x6f, 0x6f, 0x74, 0x4b, 0x65, 0x79, 0x49, 0x64, 0x22, 0x34, + 0x0a, 0x18, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, + 0x49, 0x44, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x64, 0x65, + 0x6c, 0x65, 0x74, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x64, 0x65, 0x6c, + 0x65, 0x74, 0x65, 0x64, 0x22, 0x55, 0x0a, 0x16, 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, + 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x3b, + 0x0a, 0x0b, 0x70, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4d, 0x61, 0x63, 0x61, + 0x72, 0x6f, 0x6f, 0x6e, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x0b, + 0x70, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x18, 0x0a, 0x16, 0x4c, + 0x69, 0x73, 0x74, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0xe4, 0x01, 0x0a, 0x17, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x65, + 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x64, 0x0a, 0x12, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x5f, 0x70, 0x65, 0x72, 0x6d, + 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x35, 0x2e, + 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, + 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x4d, 0x65, + 0x74, 0x68, 0x6f, 0x64, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x45, + 0x6e, 0x74, 0x72, 0x79, 0x52, 0x11, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x50, 0x65, 0x72, 0x6d, + 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x1a, 0x63, 0x0a, 0x16, 0x4d, 0x65, 0x74, 0x68, 0x6f, + 0x64, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72, + 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, + 0x6b, 0x65, 0x79, 0x12, 0x33, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4d, 0x61, 0x63, 0x61, 0x72, + 0x6f, 0x6f, 0x6e, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x4c, 0x69, 0x73, + 0x74, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xcc, 0x08, 0x0a, + 0x07, 0x46, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x12, 0x2e, 0x0a, 0x04, 0x63, 0x6f, 0x64, 0x65, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x46, + 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x2e, 0x46, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x43, 0x6f, + 0x64, 0x65, 0x52, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x12, 0x3b, 0x0a, 0x0e, 0x63, 0x68, 0x61, 0x6e, + 0x6e, 0x65, 0x6c, 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x14, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, + 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x0d, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x55, + 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x68, 0x74, 0x6c, 0x63, 0x5f, 0x6d, 0x73, + 0x61, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x68, 0x74, 0x6c, 0x63, 0x4d, 0x73, + 0x61, 0x74, 0x12, 0x22, 0x0a, 0x0d, 0x6f, 0x6e, 0x69, 0x6f, 0x6e, 0x5f, 0x73, 0x68, 0x61, 0x5f, + 0x32, 0x35, 0x36, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x6f, 0x6e, 0x69, 0x6f, 0x6e, + 0x53, 0x68, 0x61, 0x32, 0x35, 0x36, 0x12, 0x1f, 0x0a, 0x0b, 0x63, 0x6c, 0x74, 0x76, 0x5f, 0x65, + 0x78, 0x70, 0x69, 0x72, 0x79, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x63, 0x6c, 0x74, + 0x76, 0x45, 0x78, 0x70, 0x69, 0x72, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x66, 0x6c, 0x61, 0x67, 0x73, + 0x18, 0x07, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x12, 0x30, 0x0a, + 0x14, 0x66, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, + 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x12, 0x66, 0x61, 0x69, + 0x6c, 0x75, 0x72, 0x65, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, + 0x16, 0x0a, 0x06, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0d, 0x52, + 0x06, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x22, 0x8b, 0x06, 0x0a, 0x0b, 0x46, 0x61, 0x69, 0x6c, + 0x75, 0x72, 0x65, 0x43, 0x6f, 0x64, 0x65, 0x12, 0x0c, 0x0a, 0x08, 0x52, 0x45, 0x53, 0x45, 0x52, + 0x56, 0x45, 0x44, 0x10, 0x00, 0x12, 0x28, 0x0a, 0x24, 0x49, 0x4e, 0x43, 0x4f, 0x52, 0x52, 0x45, + 0x43, 0x54, 0x5f, 0x4f, 0x52, 0x5f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x5f, 0x50, 0x41, + 0x59, 0x4d, 0x45, 0x4e, 0x54, 0x5f, 0x44, 0x45, 0x54, 0x41, 0x49, 0x4c, 0x53, 0x10, 0x01, 0x12, + 0x1c, 0x0a, 0x18, 0x49, 0x4e, 0x43, 0x4f, 0x52, 0x52, 0x45, 0x43, 0x54, 0x5f, 0x50, 0x41, 0x59, + 0x4d, 0x45, 0x4e, 0x54, 0x5f, 0x41, 0x4d, 0x4f, 0x55, 0x4e, 0x54, 0x10, 0x02, 0x12, 0x1f, 0x0a, 0x1b, 0x46, 0x49, 0x4e, 0x41, 0x4c, 0x5f, 0x49, 0x4e, 0x43, 0x4f, 0x52, 0x52, 0x45, 0x43, 0x54, - 0x5f, 0x48, 0x54, 0x4c, 0x43, 0x5f, 0x41, 0x4d, 0x4f, 0x55, 0x4e, 0x54, 0x10, 0x04, 0x12, 0x19, - 0x0a, 0x15, 0x46, 0x49, 0x4e, 0x41, 0x4c, 0x5f, 0x45, 0x58, 0x50, 0x49, 0x52, 0x59, 0x5f, 0x54, - 0x4f, 0x4f, 0x5f, 0x53, 0x4f, 0x4f, 0x4e, 0x10, 0x05, 0x12, 0x11, 0x0a, 0x0d, 0x49, 0x4e, 0x56, - 0x41, 0x4c, 0x49, 0x44, 0x5f, 0x52, 0x45, 0x41, 0x4c, 0x4d, 0x10, 0x06, 0x12, 0x13, 0x0a, 0x0f, - 0x45, 0x58, 0x50, 0x49, 0x52, 0x59, 0x5f, 0x54, 0x4f, 0x4f, 0x5f, 0x53, 0x4f, 0x4f, 0x4e, 0x10, - 0x07, 0x12, 0x19, 0x0a, 0x15, 0x49, 0x4e, 0x56, 0x41, 0x4c, 0x49, 0x44, 0x5f, 0x4f, 0x4e, 0x49, - 0x4f, 0x4e, 0x5f, 0x56, 0x45, 0x52, 0x53, 0x49, 0x4f, 0x4e, 0x10, 0x08, 0x12, 0x16, 0x0a, 0x12, - 0x49, 0x4e, 0x56, 0x41, 0x4c, 0x49, 0x44, 0x5f, 0x4f, 0x4e, 0x49, 0x4f, 0x4e, 0x5f, 0x48, 0x4d, - 0x41, 0x43, 0x10, 0x09, 0x12, 0x15, 0x0a, 0x11, 0x49, 0x4e, 0x56, 0x41, 0x4c, 0x49, 0x44, 0x5f, - 0x4f, 0x4e, 0x49, 0x4f, 0x4e, 0x5f, 0x4b, 0x45, 0x59, 0x10, 0x0a, 0x12, 0x18, 0x0a, 0x14, 0x41, - 0x4d, 0x4f, 0x55, 0x4e, 0x54, 0x5f, 0x42, 0x45, 0x4c, 0x4f, 0x57, 0x5f, 0x4d, 0x49, 0x4e, 0x49, - 0x4d, 0x55, 0x4d, 0x10, 0x0b, 0x12, 0x14, 0x0a, 0x10, 0x46, 0x45, 0x45, 0x5f, 0x49, 0x4e, 0x53, - 0x55, 0x46, 0x46, 0x49, 0x43, 0x49, 0x45, 0x4e, 0x54, 0x10, 0x0c, 0x12, 0x19, 0x0a, 0x15, 0x49, - 0x4e, 0x43, 0x4f, 0x52, 0x52, 0x45, 0x43, 0x54, 0x5f, 0x43, 0x4c, 0x54, 0x56, 0x5f, 0x45, 0x58, - 0x50, 0x49, 0x52, 0x59, 0x10, 0x0d, 0x12, 0x14, 0x0a, 0x10, 0x43, 0x48, 0x41, 0x4e, 0x4e, 0x45, - 0x4c, 0x5f, 0x44, 0x49, 0x53, 0x41, 0x42, 0x4c, 0x45, 0x44, 0x10, 0x0e, 0x12, 0x1d, 0x0a, 0x19, - 0x54, 0x45, 0x4d, 0x50, 0x4f, 0x52, 0x41, 0x52, 0x59, 0x5f, 0x43, 0x48, 0x41, 0x4e, 0x4e, 0x45, - 0x4c, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x10, 0x0f, 0x12, 0x21, 0x0a, 0x1d, 0x52, - 0x45, 0x51, 0x55, 0x49, 0x52, 0x45, 0x44, 0x5f, 0x4e, 0x4f, 0x44, 0x45, 0x5f, 0x46, 0x45, 0x41, - 0x54, 0x55, 0x52, 0x45, 0x5f, 0x4d, 0x49, 0x53, 0x53, 0x49, 0x4e, 0x47, 0x10, 0x10, 0x12, 0x24, - 0x0a, 0x20, 0x52, 0x45, 0x51, 0x55, 0x49, 0x52, 0x45, 0x44, 0x5f, 0x43, 0x48, 0x41, 0x4e, 0x4e, - 0x45, 0x4c, 0x5f, 0x46, 0x45, 0x41, 0x54, 0x55, 0x52, 0x45, 0x5f, 0x4d, 0x49, 0x53, 0x53, 0x49, - 0x4e, 0x47, 0x10, 0x11, 0x12, 0x15, 0x0a, 0x11, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x5f, - 0x4e, 0x45, 0x58, 0x54, 0x5f, 0x50, 0x45, 0x45, 0x52, 0x10, 0x12, 0x12, 0x1a, 0x0a, 0x16, 0x54, - 0x45, 0x4d, 0x50, 0x4f, 0x52, 0x41, 0x52, 0x59, 0x5f, 0x4e, 0x4f, 0x44, 0x45, 0x5f, 0x46, 0x41, - 0x49, 0x4c, 0x55, 0x52, 0x45, 0x10, 0x13, 0x12, 0x1a, 0x0a, 0x16, 0x50, 0x45, 0x52, 0x4d, 0x41, - 0x4e, 0x45, 0x4e, 0x54, 0x5f, 0x4e, 0x4f, 0x44, 0x45, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, - 0x45, 0x10, 0x14, 0x12, 0x1d, 0x0a, 0x19, 0x50, 0x45, 0x52, 0x4d, 0x41, 0x4e, 0x45, 0x4e, 0x54, - 0x5f, 0x43, 0x48, 0x41, 0x4e, 0x4e, 0x45, 0x4c, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, - 0x10, 0x15, 0x12, 0x12, 0x0a, 0x0e, 0x45, 0x58, 0x50, 0x49, 0x52, 0x59, 0x5f, 0x54, 0x4f, 0x4f, - 0x5f, 0x46, 0x41, 0x52, 0x10, 0x16, 0x12, 0x0f, 0x0a, 0x0b, 0x4d, 0x50, 0x50, 0x5f, 0x54, 0x49, - 0x4d, 0x45, 0x4f, 0x55, 0x54, 0x10, 0x17, 0x12, 0x19, 0x0a, 0x15, 0x49, 0x4e, 0x56, 0x41, 0x4c, - 0x49, 0x44, 0x5f, 0x4f, 0x4e, 0x49, 0x4f, 0x4e, 0x5f, 0x50, 0x41, 0x59, 0x4c, 0x4f, 0x41, 0x44, - 0x10, 0x18, 0x12, 0x1a, 0x0a, 0x16, 0x49, 0x4e, 0x56, 0x41, 0x4c, 0x49, 0x44, 0x5f, 0x4f, 0x4e, - 0x49, 0x4f, 0x4e, 0x5f, 0x42, 0x4c, 0x49, 0x4e, 0x44, 0x49, 0x4e, 0x47, 0x10, 0x19, 0x12, 0x15, - 0x0a, 0x10, 0x49, 0x4e, 0x54, 0x45, 0x52, 0x4e, 0x41, 0x4c, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x55, - 0x52, 0x45, 0x10, 0xe5, 0x07, 0x12, 0x14, 0x0a, 0x0f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, - 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x10, 0xe6, 0x07, 0x12, 0x17, 0x0a, 0x12, 0x55, - 0x4e, 0x52, 0x45, 0x41, 0x44, 0x41, 0x42, 0x4c, 0x45, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, - 0x45, 0x10, 0xe7, 0x07, 0x4a, 0x04, 0x08, 0x02, 0x10, 0x03, 0x22, 0xb3, 0x03, 0x0a, 0x0d, 0x43, - 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x1c, 0x0a, 0x09, - 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, - 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x68, - 0x61, 0x69, 0x6e, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, - 0x63, 0x68, 0x61, 0x69, 0x6e, 0x48, 0x61, 0x73, 0x68, 0x12, 0x1b, 0x0a, 0x07, 0x63, 0x68, 0x61, - 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x42, 0x02, 0x30, 0x01, 0x52, 0x06, - 0x63, 0x68, 0x61, 0x6e, 0x49, 0x64, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, - 0x61, 0x6d, 0x70, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, - 0x74, 0x61, 0x6d, 0x70, 0x12, 0x23, 0x0a, 0x0d, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, - 0x66, 0x6c, 0x61, 0x67, 0x73, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0c, 0x6d, 0x65, 0x73, - 0x73, 0x61, 0x67, 0x65, 0x46, 0x6c, 0x61, 0x67, 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x68, 0x61, - 0x6e, 0x6e, 0x65, 0x6c, 0x5f, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, - 0x52, 0x0c, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x46, 0x6c, 0x61, 0x67, 0x73, 0x12, 0x26, - 0x0a, 0x0f, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x64, 0x65, 0x6c, 0x74, - 0x61, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0d, 0x74, 0x69, 0x6d, 0x65, 0x4c, 0x6f, 0x63, - 0x6b, 0x44, 0x65, 0x6c, 0x74, 0x61, 0x12, 0x2a, 0x0a, 0x11, 0x68, 0x74, 0x6c, 0x63, 0x5f, 0x6d, - 0x69, 0x6e, 0x69, 0x6d, 0x75, 0x6d, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, - 0x04, 0x52, 0x0f, 0x68, 0x74, 0x6c, 0x63, 0x4d, 0x69, 0x6e, 0x69, 0x6d, 0x75, 0x6d, 0x4d, 0x73, - 0x61, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x62, 0x61, 0x73, 0x65, 0x5f, 0x66, 0x65, 0x65, 0x18, 0x08, - 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x62, 0x61, 0x73, 0x65, 0x46, 0x65, 0x65, 0x12, 0x19, 0x0a, - 0x08, 0x66, 0x65, 0x65, 0x5f, 0x72, 0x61, 0x74, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0d, 0x52, - 0x07, 0x66, 0x65, 0x65, 0x52, 0x61, 0x74, 0x65, 0x12, 0x2a, 0x0a, 0x11, 0x68, 0x74, 0x6c, 0x63, - 0x5f, 0x6d, 0x61, 0x78, 0x69, 0x6d, 0x75, 0x6d, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x0b, 0x20, - 0x01, 0x28, 0x04, 0x52, 0x0f, 0x68, 0x74, 0x6c, 0x63, 0x4d, 0x61, 0x78, 0x69, 0x6d, 0x75, 0x6d, - 0x4d, 0x73, 0x61, 0x74, 0x12, 0x2a, 0x0a, 0x11, 0x65, 0x78, 0x74, 0x72, 0x61, 0x5f, 0x6f, 0x70, - 0x61, 0x71, 0x75, 0x65, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0c, 0x52, - 0x0f, 0x65, 0x78, 0x74, 0x72, 0x61, 0x4f, 0x70, 0x61, 0x71, 0x75, 0x65, 0x44, 0x61, 0x74, 0x61, - 0x22, 0x5d, 0x0a, 0x0a, 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x14, - 0x0a, 0x05, 0x6e, 0x6f, 0x6e, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x6e, - 0x6f, 0x6e, 0x63, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x49, - 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, - 0x49, 0x64, 0x12, 0x1b, 0x0a, 0x03, 0x6f, 0x70, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x09, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4f, 0x70, 0x52, 0x03, 0x6f, 0x70, 0x73, 0x22, - 0x36, 0x0a, 0x02, 0x4f, 0x70, 0x12, 0x16, 0x0a, 0x06, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x12, 0x18, 0x0a, - 0x07, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, - 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x8e, 0x01, 0x0a, 0x13, 0x43, 0x68, 0x65, 0x63, - 0x6b, 0x4d, 0x61, 0x63, 0x50, 0x65, 0x72, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, - 0x1a, 0x0a, 0x08, 0x6d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x0c, 0x52, 0x08, 0x6d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x12, 0x3b, 0x0a, 0x0b, 0x70, - 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, - 0x6e, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x0b, 0x70, 0x65, 0x72, - 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x1e, 0x0a, 0x0a, 0x66, 0x75, 0x6c, 0x6c, - 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x66, 0x75, - 0x6c, 0x6c, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x22, 0x2c, 0x0a, 0x14, 0x43, 0x68, 0x65, 0x63, - 0x6b, 0x4d, 0x61, 0x63, 0x50, 0x65, 0x72, 0x6d, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, - 0x05, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x22, 0xf4, 0x02, 0x0a, 0x14, 0x52, 0x50, 0x43, 0x4d, 0x69, - 0x64, 0x64, 0x6c, 0x65, 0x77, 0x61, 0x72, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, - 0x1d, 0x0a, 0x0a, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x04, 0x52, 0x09, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x49, 0x64, 0x12, 0x21, - 0x0a, 0x0c, 0x72, 0x61, 0x77, 0x5f, 0x6d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x72, 0x61, 0x77, 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, - 0x6e, 0x12, 0x36, 0x0a, 0x17, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5f, 0x63, 0x61, 0x76, 0x65, - 0x61, 0x74, 0x5f, 0x63, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x15, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x43, 0x61, 0x76, 0x65, 0x61, 0x74, - 0x43, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x34, 0x0a, 0x0b, 0x73, 0x74, 0x72, - 0x65, 0x61, 0x6d, 0x5f, 0x61, 0x75, 0x74, 0x68, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, - 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x41, 0x75, 0x74, - 0x68, 0x48, 0x00, 0x52, 0x0a, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x41, 0x75, 0x74, 0x68, 0x12, - 0x2d, 0x0a, 0x07, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x11, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x50, 0x43, 0x4d, 0x65, 0x73, 0x73, - 0x61, 0x67, 0x65, 0x48, 0x00, 0x52, 0x07, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2f, - 0x0a, 0x08, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x11, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x50, 0x43, 0x4d, 0x65, 0x73, 0x73, - 0x61, 0x67, 0x65, 0x48, 0x00, 0x52, 0x08, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, - 0x23, 0x0a, 0x0c, 0x72, 0x65, 0x67, 0x5f, 0x63, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x18, - 0x08, 0x20, 0x01, 0x28, 0x08, 0x48, 0x00, 0x52, 0x0b, 0x72, 0x65, 0x67, 0x43, 0x6f, 0x6d, 0x70, - 0x6c, 0x65, 0x74, 0x65, 0x12, 0x15, 0x0a, 0x06, 0x6d, 0x73, 0x67, 0x5f, 0x69, 0x64, 0x18, 0x07, - 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x6d, 0x73, 0x67, 0x49, 0x64, 0x42, 0x10, 0x0a, 0x0e, 0x69, - 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x22, 0x34, 0x0a, - 0x0a, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x41, 0x75, 0x74, 0x68, 0x12, 0x26, 0x0a, 0x0f, 0x6d, - 0x65, 0x74, 0x68, 0x6f, 0x64, 0x5f, 0x66, 0x75, 0x6c, 0x6c, 0x5f, 0x75, 0x72, 0x69, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x46, 0x75, 0x6c, 0x6c, - 0x55, 0x72, 0x69, 0x22, 0xab, 0x01, 0x0a, 0x0a, 0x52, 0x50, 0x43, 0x4d, 0x65, 0x73, 0x73, 0x61, - 0x67, 0x65, 0x12, 0x26, 0x0a, 0x0f, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x5f, 0x66, 0x75, 0x6c, - 0x6c, 0x5f, 0x75, 0x72, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x6d, 0x65, 0x74, - 0x68, 0x6f, 0x64, 0x46, 0x75, 0x6c, 0x6c, 0x55, 0x72, 0x69, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x74, - 0x72, 0x65, 0x61, 0x6d, 0x5f, 0x72, 0x70, 0x63, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, - 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x70, 0x63, 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x79, 0x70, - 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x79, - 0x70, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, - 0x69, 0x7a, 0x65, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x73, 0x65, 0x72, 0x69, - 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x12, 0x19, 0x0a, 0x08, 0x69, 0x73, 0x5f, 0x65, 0x72, 0x72, - 0x6f, 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x69, 0x73, 0x45, 0x72, 0x72, 0x6f, - 0x72, 0x22, 0xc0, 0x01, 0x0a, 0x15, 0x52, 0x50, 0x43, 0x4d, 0x69, 0x64, 0x64, 0x6c, 0x65, 0x77, - 0x61, 0x72, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1c, 0x0a, 0x0a, 0x72, - 0x65, 0x66, 0x5f, 0x6d, 0x73, 0x67, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, - 0x08, 0x72, 0x65, 0x66, 0x4d, 0x73, 0x67, 0x49, 0x64, 0x12, 0x3b, 0x0a, 0x08, 0x72, 0x65, 0x67, - 0x69, 0x73, 0x74, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x6c, 0x6e, - 0x72, 0x70, 0x63, 0x2e, 0x4d, 0x69, 0x64, 0x64, 0x6c, 0x65, 0x77, 0x61, 0x72, 0x65, 0x52, 0x65, - 0x67, 0x69, 0x73, 0x74, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x00, 0x52, 0x08, 0x72, 0x65, - 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x12, 0x36, 0x0a, 0x08, 0x66, 0x65, 0x65, 0x64, 0x62, 0x61, - 0x63, 0x6b, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, - 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x46, 0x65, 0x65, 0x64, 0x62, 0x61, - 0x63, 0x6b, 0x48, 0x00, 0x52, 0x08, 0x66, 0x65, 0x65, 0x64, 0x62, 0x61, 0x63, 0x6b, 0x42, 0x14, - 0x0a, 0x12, 0x6d, 0x69, 0x64, 0x64, 0x6c, 0x65, 0x77, 0x61, 0x72, 0x65, 0x5f, 0x6d, 0x65, 0x73, - 0x73, 0x61, 0x67, 0x65, 0x22, 0xa6, 0x01, 0x0a, 0x16, 0x4d, 0x69, 0x64, 0x64, 0x6c, 0x65, 0x77, - 0x61, 0x72, 0x65, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, - 0x27, 0x0a, 0x0f, 0x6d, 0x69, 0x64, 0x64, 0x6c, 0x65, 0x77, 0x61, 0x72, 0x65, 0x5f, 0x6e, 0x61, - 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x6d, 0x69, 0x64, 0x64, 0x6c, 0x65, - 0x77, 0x61, 0x72, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x3d, 0x0a, 0x1b, 0x63, 0x75, 0x73, 0x74, - 0x6f, 0x6d, 0x5f, 0x6d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x5f, 0x63, 0x61, 0x76, 0x65, - 0x61, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x18, 0x63, - 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x43, 0x61, 0x76, - 0x65, 0x61, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x24, 0x0a, 0x0e, 0x72, 0x65, 0x61, 0x64, 0x5f, - 0x6f, 0x6e, 0x6c, 0x79, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, - 0x0c, 0x72, 0x65, 0x61, 0x64, 0x4f, 0x6e, 0x6c, 0x79, 0x4d, 0x6f, 0x64, 0x65, 0x22, 0x8b, 0x01, - 0x0a, 0x11, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x46, 0x65, 0x65, 0x64, 0x62, - 0x61, 0x63, 0x6b, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x29, 0x0a, 0x10, 0x72, 0x65, 0x70, - 0x6c, 0x61, 0x63, 0x65, 0x5f, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x08, 0x52, 0x0f, 0x72, 0x65, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x35, 0x0a, 0x16, 0x72, 0x65, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x6d, - 0x65, 0x6e, 0x74, 0x5f, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x0c, 0x52, 0x15, 0x72, 0x65, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x6d, 0x65, 0x6e, - 0x74, 0x53, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x2a, 0xcb, 0x02, 0x0a, 0x10, - 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x54, 0x79, 0x70, 0x65, - 0x12, 0x1b, 0x0a, 0x17, 0x53, 0x43, 0x52, 0x49, 0x50, 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, - 0x50, 0x55, 0x42, 0x4b, 0x45, 0x59, 0x5f, 0x48, 0x41, 0x53, 0x48, 0x10, 0x00, 0x12, 0x1b, 0x0a, - 0x17, 0x53, 0x43, 0x52, 0x49, 0x50, 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x53, 0x43, 0x52, - 0x49, 0x50, 0x54, 0x5f, 0x48, 0x41, 0x53, 0x48, 0x10, 0x01, 0x12, 0x26, 0x0a, 0x22, 0x53, 0x43, - 0x52, 0x49, 0x50, 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x57, 0x49, 0x54, 0x4e, 0x45, 0x53, - 0x53, 0x5f, 0x56, 0x30, 0x5f, 0x50, 0x55, 0x42, 0x4b, 0x45, 0x59, 0x5f, 0x48, 0x41, 0x53, 0x48, - 0x10, 0x02, 0x12, 0x26, 0x0a, 0x22, 0x53, 0x43, 0x52, 0x49, 0x50, 0x54, 0x5f, 0x54, 0x59, 0x50, - 0x45, 0x5f, 0x57, 0x49, 0x54, 0x4e, 0x45, 0x53, 0x53, 0x5f, 0x56, 0x30, 0x5f, 0x53, 0x43, 0x52, - 0x49, 0x50, 0x54, 0x5f, 0x48, 0x41, 0x53, 0x48, 0x10, 0x03, 0x12, 0x16, 0x0a, 0x12, 0x53, 0x43, - 0x52, 0x49, 0x50, 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x50, 0x55, 0x42, 0x4b, 0x45, 0x59, - 0x10, 0x04, 0x12, 0x18, 0x0a, 0x14, 0x53, 0x43, 0x52, 0x49, 0x50, 0x54, 0x5f, 0x54, 0x59, 0x50, - 0x45, 0x5f, 0x4d, 0x55, 0x4c, 0x54, 0x49, 0x53, 0x49, 0x47, 0x10, 0x05, 0x12, 0x18, 0x0a, 0x14, - 0x53, 0x43, 0x52, 0x49, 0x50, 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x4e, 0x55, 0x4c, 0x4c, - 0x44, 0x41, 0x54, 0x41, 0x10, 0x06, 0x12, 0x1c, 0x0a, 0x18, 0x53, 0x43, 0x52, 0x49, 0x50, 0x54, - 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x4e, 0x4f, 0x4e, 0x5f, 0x53, 0x54, 0x41, 0x4e, 0x44, 0x41, - 0x52, 0x44, 0x10, 0x07, 0x12, 0x1f, 0x0a, 0x1b, 0x53, 0x43, 0x52, 0x49, 0x50, 0x54, 0x5f, 0x54, - 0x59, 0x50, 0x45, 0x5f, 0x57, 0x49, 0x54, 0x4e, 0x45, 0x53, 0x53, 0x5f, 0x55, 0x4e, 0x4b, 0x4e, - 0x4f, 0x57, 0x4e, 0x10, 0x08, 0x12, 0x22, 0x0a, 0x1e, 0x53, 0x43, 0x52, 0x49, 0x50, 0x54, 0x5f, - 0x54, 0x59, 0x50, 0x45, 0x5f, 0x57, 0x49, 0x54, 0x4e, 0x45, 0x53, 0x53, 0x5f, 0x56, 0x31, 0x5f, - 0x54, 0x41, 0x50, 0x52, 0x4f, 0x4f, 0x54, 0x10, 0x09, 0x2a, 0x62, 0x0a, 0x15, 0x43, 0x6f, 0x69, - 0x6e, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, - 0x67, 0x79, 0x12, 0x1e, 0x0a, 0x1a, 0x53, 0x54, 0x52, 0x41, 0x54, 0x45, 0x47, 0x59, 0x5f, 0x55, - 0x53, 0x45, 0x5f, 0x47, 0x4c, 0x4f, 0x42, 0x41, 0x4c, 0x5f, 0x43, 0x4f, 0x4e, 0x46, 0x49, 0x47, - 0x10, 0x00, 0x12, 0x14, 0x0a, 0x10, 0x53, 0x54, 0x52, 0x41, 0x54, 0x45, 0x47, 0x59, 0x5f, 0x4c, - 0x41, 0x52, 0x47, 0x45, 0x53, 0x54, 0x10, 0x01, 0x12, 0x13, 0x0a, 0x0f, 0x53, 0x54, 0x52, 0x41, - 0x54, 0x45, 0x47, 0x59, 0x5f, 0x52, 0x41, 0x4e, 0x44, 0x4f, 0x4d, 0x10, 0x02, 0x2a, 0xac, 0x01, - 0x0a, 0x0b, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x54, 0x79, 0x70, 0x65, 0x12, 0x17, 0x0a, - 0x13, 0x57, 0x49, 0x54, 0x4e, 0x45, 0x53, 0x53, 0x5f, 0x50, 0x55, 0x42, 0x4b, 0x45, 0x59, 0x5f, - 0x48, 0x41, 0x53, 0x48, 0x10, 0x00, 0x12, 0x16, 0x0a, 0x12, 0x4e, 0x45, 0x53, 0x54, 0x45, 0x44, - 0x5f, 0x50, 0x55, 0x42, 0x4b, 0x45, 0x59, 0x5f, 0x48, 0x41, 0x53, 0x48, 0x10, 0x01, 0x12, 0x1e, - 0x0a, 0x1a, 0x55, 0x4e, 0x55, 0x53, 0x45, 0x44, 0x5f, 0x57, 0x49, 0x54, 0x4e, 0x45, 0x53, 0x53, - 0x5f, 0x50, 0x55, 0x42, 0x4b, 0x45, 0x59, 0x5f, 0x48, 0x41, 0x53, 0x48, 0x10, 0x02, 0x12, 0x1d, - 0x0a, 0x19, 0x55, 0x4e, 0x55, 0x53, 0x45, 0x44, 0x5f, 0x4e, 0x45, 0x53, 0x54, 0x45, 0x44, 0x5f, - 0x50, 0x55, 0x42, 0x4b, 0x45, 0x59, 0x5f, 0x48, 0x41, 0x53, 0x48, 0x10, 0x03, 0x12, 0x12, 0x0a, - 0x0e, 0x54, 0x41, 0x50, 0x52, 0x4f, 0x4f, 0x54, 0x5f, 0x50, 0x55, 0x42, 0x4b, 0x45, 0x59, 0x10, - 0x04, 0x12, 0x19, 0x0a, 0x15, 0x55, 0x4e, 0x55, 0x53, 0x45, 0x44, 0x5f, 0x54, 0x41, 0x50, 0x52, - 0x4f, 0x4f, 0x54, 0x5f, 0x50, 0x55, 0x42, 0x4b, 0x45, 0x59, 0x10, 0x05, 0x2a, 0x8c, 0x01, 0x0a, - 0x0e, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x6d, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, - 0x1b, 0x0a, 0x17, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x5f, 0x43, 0x4f, 0x4d, 0x4d, 0x49, - 0x54, 0x4d, 0x45, 0x4e, 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, - 0x4c, 0x45, 0x47, 0x41, 0x43, 0x59, 0x10, 0x01, 0x12, 0x15, 0x0a, 0x11, 0x53, 0x54, 0x41, 0x54, - 0x49, 0x43, 0x5f, 0x52, 0x45, 0x4d, 0x4f, 0x54, 0x45, 0x5f, 0x4b, 0x45, 0x59, 0x10, 0x02, 0x12, - 0x0b, 0x0a, 0x07, 0x41, 0x4e, 0x43, 0x48, 0x4f, 0x52, 0x53, 0x10, 0x03, 0x12, 0x19, 0x0a, 0x15, - 0x53, 0x43, 0x52, 0x49, 0x50, 0x54, 0x5f, 0x45, 0x4e, 0x46, 0x4f, 0x52, 0x43, 0x45, 0x44, 0x5f, - 0x4c, 0x45, 0x41, 0x53, 0x45, 0x10, 0x04, 0x12, 0x12, 0x0a, 0x0e, 0x53, 0x49, 0x4d, 0x50, 0x4c, - 0x45, 0x5f, 0x54, 0x41, 0x50, 0x52, 0x4f, 0x4f, 0x54, 0x10, 0x05, 0x2a, 0x61, 0x0a, 0x09, 0x49, - 0x6e, 0x69, 0x74, 0x69, 0x61, 0x74, 0x6f, 0x72, 0x12, 0x15, 0x0a, 0x11, 0x49, 0x4e, 0x49, 0x54, - 0x49, 0x41, 0x54, 0x4f, 0x52, 0x5f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, - 0x13, 0x0a, 0x0f, 0x49, 0x4e, 0x49, 0x54, 0x49, 0x41, 0x54, 0x4f, 0x52, 0x5f, 0x4c, 0x4f, 0x43, - 0x41, 0x4c, 0x10, 0x01, 0x12, 0x14, 0x0a, 0x10, 0x49, 0x4e, 0x49, 0x54, 0x49, 0x41, 0x54, 0x4f, - 0x52, 0x5f, 0x52, 0x45, 0x4d, 0x4f, 0x54, 0x45, 0x10, 0x02, 0x12, 0x12, 0x0a, 0x0e, 0x49, 0x4e, - 0x49, 0x54, 0x49, 0x41, 0x54, 0x4f, 0x52, 0x5f, 0x42, 0x4f, 0x54, 0x48, 0x10, 0x03, 0x2a, 0x60, - 0x0a, 0x0e, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x79, 0x70, 0x65, - 0x12, 0x10, 0x0a, 0x0c, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, - 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x41, 0x4e, 0x43, 0x48, 0x4f, 0x52, 0x10, 0x01, 0x12, 0x11, - 0x0a, 0x0d, 0x49, 0x4e, 0x43, 0x4f, 0x4d, 0x49, 0x4e, 0x47, 0x5f, 0x48, 0x54, 0x4c, 0x43, 0x10, - 0x02, 0x12, 0x11, 0x0a, 0x0d, 0x4f, 0x55, 0x54, 0x47, 0x4f, 0x49, 0x4e, 0x47, 0x5f, 0x48, 0x54, - 0x4c, 0x43, 0x10, 0x03, 0x12, 0x0a, 0x0a, 0x06, 0x43, 0x4f, 0x4d, 0x4d, 0x49, 0x54, 0x10, 0x04, - 0x2a, 0x71, 0x0a, 0x11, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x4f, 0x75, - 0x74, 0x63, 0x6f, 0x6d, 0x65, 0x12, 0x13, 0x0a, 0x0f, 0x4f, 0x55, 0x54, 0x43, 0x4f, 0x4d, 0x45, - 0x5f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x43, 0x4c, - 0x41, 0x49, 0x4d, 0x45, 0x44, 0x10, 0x01, 0x12, 0x0d, 0x0a, 0x09, 0x55, 0x4e, 0x43, 0x4c, 0x41, - 0x49, 0x4d, 0x45, 0x44, 0x10, 0x02, 0x12, 0x0d, 0x0a, 0x09, 0x41, 0x42, 0x41, 0x4e, 0x44, 0x4f, - 0x4e, 0x45, 0x44, 0x10, 0x03, 0x12, 0x0f, 0x0a, 0x0b, 0x46, 0x49, 0x52, 0x53, 0x54, 0x5f, 0x53, - 0x54, 0x41, 0x47, 0x45, 0x10, 0x04, 0x12, 0x0b, 0x0a, 0x07, 0x54, 0x49, 0x4d, 0x45, 0x4f, 0x55, - 0x54, 0x10, 0x05, 0x2a, 0x39, 0x0a, 0x0e, 0x4e, 0x6f, 0x64, 0x65, 0x4d, 0x65, 0x74, 0x72, 0x69, - 0x63, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, - 0x10, 0x00, 0x12, 0x1a, 0x0a, 0x16, 0x42, 0x45, 0x54, 0x57, 0x45, 0x45, 0x4e, 0x4e, 0x45, 0x53, - 0x53, 0x5f, 0x43, 0x45, 0x4e, 0x54, 0x52, 0x41, 0x4c, 0x49, 0x54, 0x59, 0x10, 0x01, 0x2a, 0x3b, - 0x0a, 0x10, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x48, 0x54, 0x4c, 0x43, 0x53, 0x74, 0x61, - 0x74, 0x65, 0x12, 0x0c, 0x0a, 0x08, 0x41, 0x43, 0x43, 0x45, 0x50, 0x54, 0x45, 0x44, 0x10, 0x00, - 0x12, 0x0b, 0x0a, 0x07, 0x53, 0x45, 0x54, 0x54, 0x4c, 0x45, 0x44, 0x10, 0x01, 0x12, 0x0c, 0x0a, - 0x08, 0x43, 0x41, 0x4e, 0x43, 0x45, 0x4c, 0x45, 0x44, 0x10, 0x02, 0x2a, 0xf6, 0x01, 0x0a, 0x14, - 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x46, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x52, 0x65, - 0x61, 0x73, 0x6f, 0x6e, 0x12, 0x17, 0x0a, 0x13, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, - 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x4e, 0x4f, 0x4e, 0x45, 0x10, 0x00, 0x12, 0x1a, 0x0a, - 0x16, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, - 0x54, 0x49, 0x4d, 0x45, 0x4f, 0x55, 0x54, 0x10, 0x01, 0x12, 0x1b, 0x0a, 0x17, 0x46, 0x41, 0x49, - 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x4e, 0x4f, 0x5f, 0x52, - 0x4f, 0x55, 0x54, 0x45, 0x10, 0x02, 0x12, 0x18, 0x0a, 0x14, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, - 0x45, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, 0x03, - 0x12, 0x2c, 0x0a, 0x28, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x52, 0x45, 0x41, 0x53, - 0x4f, 0x4e, 0x5f, 0x49, 0x4e, 0x43, 0x4f, 0x52, 0x52, 0x45, 0x43, 0x54, 0x5f, 0x50, 0x41, 0x59, - 0x4d, 0x45, 0x4e, 0x54, 0x5f, 0x44, 0x45, 0x54, 0x41, 0x49, 0x4c, 0x53, 0x10, 0x04, 0x12, 0x27, - 0x0a, 0x23, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, - 0x5f, 0x49, 0x4e, 0x53, 0x55, 0x46, 0x46, 0x49, 0x43, 0x49, 0x45, 0x4e, 0x54, 0x5f, 0x42, 0x41, - 0x4c, 0x41, 0x4e, 0x43, 0x45, 0x10, 0x05, 0x12, 0x1b, 0x0a, 0x17, 0x46, 0x41, 0x49, 0x4c, 0x55, - 0x52, 0x45, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x43, 0x41, 0x4e, 0x43, 0x45, 0x4c, - 0x45, 0x44, 0x10, 0x06, 0x2a, 0x89, 0x05, 0x0a, 0x0a, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, - 0x42, 0x69, 0x74, 0x12, 0x18, 0x0a, 0x14, 0x44, 0x41, 0x54, 0x41, 0x4c, 0x4f, 0x53, 0x53, 0x5f, - 0x50, 0x52, 0x4f, 0x54, 0x45, 0x43, 0x54, 0x5f, 0x52, 0x45, 0x51, 0x10, 0x00, 0x12, 0x18, 0x0a, - 0x14, 0x44, 0x41, 0x54, 0x41, 0x4c, 0x4f, 0x53, 0x53, 0x5f, 0x50, 0x52, 0x4f, 0x54, 0x45, 0x43, - 0x54, 0x5f, 0x4f, 0x50, 0x54, 0x10, 0x01, 0x12, 0x17, 0x0a, 0x13, 0x49, 0x4e, 0x49, 0x54, 0x49, - 0x41, 0x4c, 0x5f, 0x52, 0x4f, 0x55, 0x49, 0x4e, 0x47, 0x5f, 0x53, 0x59, 0x4e, 0x43, 0x10, 0x03, - 0x12, 0x1f, 0x0a, 0x1b, 0x55, 0x50, 0x46, 0x52, 0x4f, 0x4e, 0x54, 0x5f, 0x53, 0x48, 0x55, 0x54, - 0x44, 0x4f, 0x57, 0x4e, 0x5f, 0x53, 0x43, 0x52, 0x49, 0x50, 0x54, 0x5f, 0x52, 0x45, 0x51, 0x10, - 0x04, 0x12, 0x1f, 0x0a, 0x1b, 0x55, 0x50, 0x46, 0x52, 0x4f, 0x4e, 0x54, 0x5f, 0x53, 0x48, 0x55, - 0x54, 0x44, 0x4f, 0x57, 0x4e, 0x5f, 0x53, 0x43, 0x52, 0x49, 0x50, 0x54, 0x5f, 0x4f, 0x50, 0x54, - 0x10, 0x05, 0x12, 0x16, 0x0a, 0x12, 0x47, 0x4f, 0x53, 0x53, 0x49, 0x50, 0x5f, 0x51, 0x55, 0x45, - 0x52, 0x49, 0x45, 0x53, 0x5f, 0x52, 0x45, 0x51, 0x10, 0x06, 0x12, 0x16, 0x0a, 0x12, 0x47, 0x4f, - 0x53, 0x53, 0x49, 0x50, 0x5f, 0x51, 0x55, 0x45, 0x52, 0x49, 0x45, 0x53, 0x5f, 0x4f, 0x50, 0x54, - 0x10, 0x07, 0x12, 0x11, 0x0a, 0x0d, 0x54, 0x4c, 0x56, 0x5f, 0x4f, 0x4e, 0x49, 0x4f, 0x4e, 0x5f, - 0x52, 0x45, 0x51, 0x10, 0x08, 0x12, 0x11, 0x0a, 0x0d, 0x54, 0x4c, 0x56, 0x5f, 0x4f, 0x4e, 0x49, - 0x4f, 0x4e, 0x5f, 0x4f, 0x50, 0x54, 0x10, 0x09, 0x12, 0x1a, 0x0a, 0x16, 0x45, 0x58, 0x54, 0x5f, - 0x47, 0x4f, 0x53, 0x53, 0x49, 0x50, 0x5f, 0x51, 0x55, 0x45, 0x52, 0x49, 0x45, 0x53, 0x5f, 0x52, - 0x45, 0x51, 0x10, 0x0a, 0x12, 0x1a, 0x0a, 0x16, 0x45, 0x58, 0x54, 0x5f, 0x47, 0x4f, 0x53, 0x53, - 0x49, 0x50, 0x5f, 0x51, 0x55, 0x45, 0x52, 0x49, 0x45, 0x53, 0x5f, 0x4f, 0x50, 0x54, 0x10, 0x0b, - 0x12, 0x19, 0x0a, 0x15, 0x53, 0x54, 0x41, 0x54, 0x49, 0x43, 0x5f, 0x52, 0x45, 0x4d, 0x4f, 0x54, - 0x45, 0x5f, 0x4b, 0x45, 0x59, 0x5f, 0x52, 0x45, 0x51, 0x10, 0x0c, 0x12, 0x19, 0x0a, 0x15, 0x53, - 0x54, 0x41, 0x54, 0x49, 0x43, 0x5f, 0x52, 0x45, 0x4d, 0x4f, 0x54, 0x45, 0x5f, 0x4b, 0x45, 0x59, - 0x5f, 0x4f, 0x50, 0x54, 0x10, 0x0d, 0x12, 0x14, 0x0a, 0x10, 0x50, 0x41, 0x59, 0x4d, 0x45, 0x4e, - 0x54, 0x5f, 0x41, 0x44, 0x44, 0x52, 0x5f, 0x52, 0x45, 0x51, 0x10, 0x0e, 0x12, 0x14, 0x0a, 0x10, - 0x50, 0x41, 0x59, 0x4d, 0x45, 0x4e, 0x54, 0x5f, 0x41, 0x44, 0x44, 0x52, 0x5f, 0x4f, 0x50, 0x54, - 0x10, 0x0f, 0x12, 0x0b, 0x0a, 0x07, 0x4d, 0x50, 0x50, 0x5f, 0x52, 0x45, 0x51, 0x10, 0x10, 0x12, - 0x0b, 0x0a, 0x07, 0x4d, 0x50, 0x50, 0x5f, 0x4f, 0x50, 0x54, 0x10, 0x11, 0x12, 0x16, 0x0a, 0x12, - 0x57, 0x55, 0x4d, 0x42, 0x4f, 0x5f, 0x43, 0x48, 0x41, 0x4e, 0x4e, 0x45, 0x4c, 0x53, 0x5f, 0x52, - 0x45, 0x51, 0x10, 0x12, 0x12, 0x16, 0x0a, 0x12, 0x57, 0x55, 0x4d, 0x42, 0x4f, 0x5f, 0x43, 0x48, - 0x41, 0x4e, 0x4e, 0x45, 0x4c, 0x53, 0x5f, 0x4f, 0x50, 0x54, 0x10, 0x13, 0x12, 0x0f, 0x0a, 0x0b, - 0x41, 0x4e, 0x43, 0x48, 0x4f, 0x52, 0x53, 0x5f, 0x52, 0x45, 0x51, 0x10, 0x14, 0x12, 0x0f, 0x0a, - 0x0b, 0x41, 0x4e, 0x43, 0x48, 0x4f, 0x52, 0x53, 0x5f, 0x4f, 0x50, 0x54, 0x10, 0x15, 0x12, 0x1d, + 0x5f, 0x43, 0x4c, 0x54, 0x56, 0x5f, 0x45, 0x58, 0x50, 0x49, 0x52, 0x59, 0x10, 0x03, 0x12, 0x1f, + 0x0a, 0x1b, 0x46, 0x49, 0x4e, 0x41, 0x4c, 0x5f, 0x49, 0x4e, 0x43, 0x4f, 0x52, 0x52, 0x45, 0x43, + 0x54, 0x5f, 0x48, 0x54, 0x4c, 0x43, 0x5f, 0x41, 0x4d, 0x4f, 0x55, 0x4e, 0x54, 0x10, 0x04, 0x12, + 0x19, 0x0a, 0x15, 0x46, 0x49, 0x4e, 0x41, 0x4c, 0x5f, 0x45, 0x58, 0x50, 0x49, 0x52, 0x59, 0x5f, + 0x54, 0x4f, 0x4f, 0x5f, 0x53, 0x4f, 0x4f, 0x4e, 0x10, 0x05, 0x12, 0x11, 0x0a, 0x0d, 0x49, 0x4e, + 0x56, 0x41, 0x4c, 0x49, 0x44, 0x5f, 0x52, 0x45, 0x41, 0x4c, 0x4d, 0x10, 0x06, 0x12, 0x13, 0x0a, + 0x0f, 0x45, 0x58, 0x50, 0x49, 0x52, 0x59, 0x5f, 0x54, 0x4f, 0x4f, 0x5f, 0x53, 0x4f, 0x4f, 0x4e, + 0x10, 0x07, 0x12, 0x19, 0x0a, 0x15, 0x49, 0x4e, 0x56, 0x41, 0x4c, 0x49, 0x44, 0x5f, 0x4f, 0x4e, + 0x49, 0x4f, 0x4e, 0x5f, 0x56, 0x45, 0x52, 0x53, 0x49, 0x4f, 0x4e, 0x10, 0x08, 0x12, 0x16, 0x0a, + 0x12, 0x49, 0x4e, 0x56, 0x41, 0x4c, 0x49, 0x44, 0x5f, 0x4f, 0x4e, 0x49, 0x4f, 0x4e, 0x5f, 0x48, + 0x4d, 0x41, 0x43, 0x10, 0x09, 0x12, 0x15, 0x0a, 0x11, 0x49, 0x4e, 0x56, 0x41, 0x4c, 0x49, 0x44, + 0x5f, 0x4f, 0x4e, 0x49, 0x4f, 0x4e, 0x5f, 0x4b, 0x45, 0x59, 0x10, 0x0a, 0x12, 0x18, 0x0a, 0x14, + 0x41, 0x4d, 0x4f, 0x55, 0x4e, 0x54, 0x5f, 0x42, 0x45, 0x4c, 0x4f, 0x57, 0x5f, 0x4d, 0x49, 0x4e, + 0x49, 0x4d, 0x55, 0x4d, 0x10, 0x0b, 0x12, 0x14, 0x0a, 0x10, 0x46, 0x45, 0x45, 0x5f, 0x49, 0x4e, + 0x53, 0x55, 0x46, 0x46, 0x49, 0x43, 0x49, 0x45, 0x4e, 0x54, 0x10, 0x0c, 0x12, 0x19, 0x0a, 0x15, + 0x49, 0x4e, 0x43, 0x4f, 0x52, 0x52, 0x45, 0x43, 0x54, 0x5f, 0x43, 0x4c, 0x54, 0x56, 0x5f, 0x45, + 0x58, 0x50, 0x49, 0x52, 0x59, 0x10, 0x0d, 0x12, 0x14, 0x0a, 0x10, 0x43, 0x48, 0x41, 0x4e, 0x4e, + 0x45, 0x4c, 0x5f, 0x44, 0x49, 0x53, 0x41, 0x42, 0x4c, 0x45, 0x44, 0x10, 0x0e, 0x12, 0x1d, 0x0a, + 0x19, 0x54, 0x45, 0x4d, 0x50, 0x4f, 0x52, 0x41, 0x52, 0x59, 0x5f, 0x43, 0x48, 0x41, 0x4e, 0x4e, + 0x45, 0x4c, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x10, 0x0f, 0x12, 0x21, 0x0a, 0x1d, + 0x52, 0x45, 0x51, 0x55, 0x49, 0x52, 0x45, 0x44, 0x5f, 0x4e, 0x4f, 0x44, 0x45, 0x5f, 0x46, 0x45, + 0x41, 0x54, 0x55, 0x52, 0x45, 0x5f, 0x4d, 0x49, 0x53, 0x53, 0x49, 0x4e, 0x47, 0x10, 0x10, 0x12, + 0x24, 0x0a, 0x20, 0x52, 0x45, 0x51, 0x55, 0x49, 0x52, 0x45, 0x44, 0x5f, 0x43, 0x48, 0x41, 0x4e, + 0x4e, 0x45, 0x4c, 0x5f, 0x46, 0x45, 0x41, 0x54, 0x55, 0x52, 0x45, 0x5f, 0x4d, 0x49, 0x53, 0x53, + 0x49, 0x4e, 0x47, 0x10, 0x11, 0x12, 0x15, 0x0a, 0x11, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, + 0x5f, 0x4e, 0x45, 0x58, 0x54, 0x5f, 0x50, 0x45, 0x45, 0x52, 0x10, 0x12, 0x12, 0x1a, 0x0a, 0x16, + 0x54, 0x45, 0x4d, 0x50, 0x4f, 0x52, 0x41, 0x52, 0x59, 0x5f, 0x4e, 0x4f, 0x44, 0x45, 0x5f, 0x46, + 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x10, 0x13, 0x12, 0x1a, 0x0a, 0x16, 0x50, 0x45, 0x52, 0x4d, + 0x41, 0x4e, 0x45, 0x4e, 0x54, 0x5f, 0x4e, 0x4f, 0x44, 0x45, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x55, + 0x52, 0x45, 0x10, 0x14, 0x12, 0x1d, 0x0a, 0x19, 0x50, 0x45, 0x52, 0x4d, 0x41, 0x4e, 0x45, 0x4e, + 0x54, 0x5f, 0x43, 0x48, 0x41, 0x4e, 0x4e, 0x45, 0x4c, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, + 0x45, 0x10, 0x15, 0x12, 0x12, 0x0a, 0x0e, 0x45, 0x58, 0x50, 0x49, 0x52, 0x59, 0x5f, 0x54, 0x4f, + 0x4f, 0x5f, 0x46, 0x41, 0x52, 0x10, 0x16, 0x12, 0x0f, 0x0a, 0x0b, 0x4d, 0x50, 0x50, 0x5f, 0x54, + 0x49, 0x4d, 0x45, 0x4f, 0x55, 0x54, 0x10, 0x17, 0x12, 0x19, 0x0a, 0x15, 0x49, 0x4e, 0x56, 0x41, + 0x4c, 0x49, 0x44, 0x5f, 0x4f, 0x4e, 0x49, 0x4f, 0x4e, 0x5f, 0x50, 0x41, 0x59, 0x4c, 0x4f, 0x41, + 0x44, 0x10, 0x18, 0x12, 0x1a, 0x0a, 0x16, 0x49, 0x4e, 0x56, 0x41, 0x4c, 0x49, 0x44, 0x5f, 0x4f, + 0x4e, 0x49, 0x4f, 0x4e, 0x5f, 0x42, 0x4c, 0x49, 0x4e, 0x44, 0x49, 0x4e, 0x47, 0x10, 0x19, 0x12, + 0x15, 0x0a, 0x10, 0x49, 0x4e, 0x54, 0x45, 0x52, 0x4e, 0x41, 0x4c, 0x5f, 0x46, 0x41, 0x49, 0x4c, + 0x55, 0x52, 0x45, 0x10, 0xe5, 0x07, 0x12, 0x14, 0x0a, 0x0f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, + 0x4e, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x10, 0xe6, 0x07, 0x12, 0x17, 0x0a, 0x12, + 0x55, 0x4e, 0x52, 0x45, 0x41, 0x44, 0x41, 0x42, 0x4c, 0x45, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x55, + 0x52, 0x45, 0x10, 0xe7, 0x07, 0x4a, 0x04, 0x08, 0x02, 0x10, 0x03, 0x22, 0xb3, 0x03, 0x0a, 0x0d, + 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x1c, 0x0a, + 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, + 0x52, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x63, + 0x68, 0x61, 0x69, 0x6e, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, + 0x09, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x48, 0x61, 0x73, 0x68, 0x12, 0x1b, 0x0a, 0x07, 0x63, 0x68, + 0x61, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x42, 0x02, 0x30, 0x01, 0x52, + 0x06, 0x63, 0x68, 0x61, 0x6e, 0x49, 0x64, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, + 0x74, 0x61, 0x6d, 0x70, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, + 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x23, 0x0a, 0x0d, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, + 0x5f, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0c, 0x6d, 0x65, + 0x73, 0x73, 0x61, 0x67, 0x65, 0x46, 0x6c, 0x61, 0x67, 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x68, + 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x5f, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, + 0x0d, 0x52, 0x0c, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x46, 0x6c, 0x61, 0x67, 0x73, 0x12, + 0x26, 0x0a, 0x0f, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x64, 0x65, 0x6c, + 0x74, 0x61, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0d, 0x74, 0x69, 0x6d, 0x65, 0x4c, 0x6f, + 0x63, 0x6b, 0x44, 0x65, 0x6c, 0x74, 0x61, 0x12, 0x2a, 0x0a, 0x11, 0x68, 0x74, 0x6c, 0x63, 0x5f, + 0x6d, 0x69, 0x6e, 0x69, 0x6d, 0x75, 0x6d, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x07, 0x20, 0x01, + 0x28, 0x04, 0x52, 0x0f, 0x68, 0x74, 0x6c, 0x63, 0x4d, 0x69, 0x6e, 0x69, 0x6d, 0x75, 0x6d, 0x4d, + 0x73, 0x61, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x62, 0x61, 0x73, 0x65, 0x5f, 0x66, 0x65, 0x65, 0x18, + 0x08, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x62, 0x61, 0x73, 0x65, 0x46, 0x65, 0x65, 0x12, 0x19, + 0x0a, 0x08, 0x66, 0x65, 0x65, 0x5f, 0x72, 0x61, 0x74, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0d, + 0x52, 0x07, 0x66, 0x65, 0x65, 0x52, 0x61, 0x74, 0x65, 0x12, 0x2a, 0x0a, 0x11, 0x68, 0x74, 0x6c, + 0x63, 0x5f, 0x6d, 0x61, 0x78, 0x69, 0x6d, 0x75, 0x6d, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x0b, + 0x20, 0x01, 0x28, 0x04, 0x52, 0x0f, 0x68, 0x74, 0x6c, 0x63, 0x4d, 0x61, 0x78, 0x69, 0x6d, 0x75, + 0x6d, 0x4d, 0x73, 0x61, 0x74, 0x12, 0x2a, 0x0a, 0x11, 0x65, 0x78, 0x74, 0x72, 0x61, 0x5f, 0x6f, + 0x70, 0x61, 0x71, 0x75, 0x65, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0c, + 0x52, 0x0f, 0x65, 0x78, 0x74, 0x72, 0x61, 0x4f, 0x70, 0x61, 0x71, 0x75, 0x65, 0x44, 0x61, 0x74, + 0x61, 0x22, 0x5d, 0x0a, 0x0a, 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x49, 0x64, 0x12, + 0x14, 0x0a, 0x05, 0x6e, 0x6f, 0x6e, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, + 0x6e, 0x6f, 0x6e, 0x63, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, + 0x49, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, + 0x65, 0x49, 0x64, 0x12, 0x1b, 0x0a, 0x03, 0x6f, 0x70, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x09, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4f, 0x70, 0x52, 0x03, 0x6f, 0x70, 0x73, + 0x22, 0x36, 0x0a, 0x02, 0x4f, 0x70, 0x12, 0x16, 0x0a, 0x06, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x12, 0x18, + 0x0a, 0x07, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, + 0x07, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x8e, 0x01, 0x0a, 0x13, 0x43, 0x68, 0x65, + 0x63, 0x6b, 0x4d, 0x61, 0x63, 0x50, 0x65, 0x72, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x12, 0x1a, 0x0a, 0x08, 0x6d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0c, 0x52, 0x08, 0x6d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x12, 0x3b, 0x0a, 0x0b, + 0x70, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, + 0x6f, 0x6e, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x0b, 0x70, 0x65, + 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x1e, 0x0a, 0x0a, 0x66, 0x75, 0x6c, + 0x6c, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x66, + 0x75, 0x6c, 0x6c, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x22, 0x2c, 0x0a, 0x14, 0x43, 0x68, 0x65, + 0x63, 0x6b, 0x4d, 0x61, 0x63, 0x50, 0x65, 0x72, 0x6d, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, + 0x52, 0x05, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x22, 0xf4, 0x02, 0x0a, 0x14, 0x52, 0x50, 0x43, 0x4d, + 0x69, 0x64, 0x64, 0x6c, 0x65, 0x77, 0x61, 0x72, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x49, 0x64, 0x12, + 0x21, 0x0a, 0x0c, 0x72, 0x61, 0x77, 0x5f, 0x6d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x72, 0x61, 0x77, 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, + 0x6f, 0x6e, 0x12, 0x36, 0x0a, 0x17, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5f, 0x63, 0x61, 0x76, + 0x65, 0x61, 0x74, 0x5f, 0x63, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x15, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x43, 0x61, 0x76, 0x65, 0x61, + 0x74, 0x43, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x34, 0x0a, 0x0b, 0x73, 0x74, + 0x72, 0x65, 0x61, 0x6d, 0x5f, 0x61, 0x75, 0x74, 0x68, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x11, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x41, 0x75, + 0x74, 0x68, 0x48, 0x00, 0x52, 0x0a, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x41, 0x75, 0x74, 0x68, + 0x12, 0x2d, 0x0a, 0x07, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x11, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x50, 0x43, 0x4d, 0x65, 0x73, + 0x73, 0x61, 0x67, 0x65, 0x48, 0x00, 0x52, 0x07, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, + 0x2f, 0x0a, 0x08, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x11, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x50, 0x43, 0x4d, 0x65, 0x73, + 0x73, 0x61, 0x67, 0x65, 0x48, 0x00, 0x52, 0x08, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x23, 0x0a, 0x0c, 0x72, 0x65, 0x67, 0x5f, 0x63, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, + 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x48, 0x00, 0x52, 0x0b, 0x72, 0x65, 0x67, 0x43, 0x6f, 0x6d, + 0x70, 0x6c, 0x65, 0x74, 0x65, 0x12, 0x15, 0x0a, 0x06, 0x6d, 0x73, 0x67, 0x5f, 0x69, 0x64, 0x18, + 0x07, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x6d, 0x73, 0x67, 0x49, 0x64, 0x42, 0x10, 0x0a, 0x0e, + 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x22, 0x34, + 0x0a, 0x0a, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x41, 0x75, 0x74, 0x68, 0x12, 0x26, 0x0a, 0x0f, + 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x5f, 0x66, 0x75, 0x6c, 0x6c, 0x5f, 0x75, 0x72, 0x69, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x46, 0x75, 0x6c, + 0x6c, 0x55, 0x72, 0x69, 0x22, 0xab, 0x01, 0x0a, 0x0a, 0x52, 0x50, 0x43, 0x4d, 0x65, 0x73, 0x73, + 0x61, 0x67, 0x65, 0x12, 0x26, 0x0a, 0x0f, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x5f, 0x66, 0x75, + 0x6c, 0x6c, 0x5f, 0x75, 0x72, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x6d, 0x65, + 0x74, 0x68, 0x6f, 0x64, 0x46, 0x75, 0x6c, 0x6c, 0x55, 0x72, 0x69, 0x12, 0x1d, 0x0a, 0x0a, 0x73, + 0x74, 0x72, 0x65, 0x61, 0x6d, 0x5f, 0x72, 0x70, 0x63, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, + 0x09, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x70, 0x63, 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x79, + 0x70, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, + 0x79, 0x70, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x73, 0x65, 0x72, 0x69, 0x61, + 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x73, 0x65, 0x72, + 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x12, 0x19, 0x0a, 0x08, 0x69, 0x73, 0x5f, 0x65, 0x72, + 0x72, 0x6f, 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x69, 0x73, 0x45, 0x72, 0x72, + 0x6f, 0x72, 0x22, 0xc0, 0x01, 0x0a, 0x15, 0x52, 0x50, 0x43, 0x4d, 0x69, 0x64, 0x64, 0x6c, 0x65, + 0x77, 0x61, 0x72, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1c, 0x0a, 0x0a, + 0x72, 0x65, 0x66, 0x5f, 0x6d, 0x73, 0x67, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, + 0x52, 0x08, 0x72, 0x65, 0x66, 0x4d, 0x73, 0x67, 0x49, 0x64, 0x12, 0x3b, 0x0a, 0x08, 0x72, 0x65, + 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x6c, + 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4d, 0x69, 0x64, 0x64, 0x6c, 0x65, 0x77, 0x61, 0x72, 0x65, 0x52, + 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x00, 0x52, 0x08, 0x72, + 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x12, 0x36, 0x0a, 0x08, 0x66, 0x65, 0x65, 0x64, 0x62, + 0x61, 0x63, 0x6b, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x6c, 0x6e, 0x72, 0x70, + 0x63, 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x46, 0x65, 0x65, 0x64, 0x62, + 0x61, 0x63, 0x6b, 0x48, 0x00, 0x52, 0x08, 0x66, 0x65, 0x65, 0x64, 0x62, 0x61, 0x63, 0x6b, 0x42, + 0x14, 0x0a, 0x12, 0x6d, 0x69, 0x64, 0x64, 0x6c, 0x65, 0x77, 0x61, 0x72, 0x65, 0x5f, 0x6d, 0x65, + 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0xa6, 0x01, 0x0a, 0x16, 0x4d, 0x69, 0x64, 0x64, 0x6c, 0x65, + 0x77, 0x61, 0x72, 0x65, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x12, 0x27, 0x0a, 0x0f, 0x6d, 0x69, 0x64, 0x64, 0x6c, 0x65, 0x77, 0x61, 0x72, 0x65, 0x5f, 0x6e, + 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x6d, 0x69, 0x64, 0x64, 0x6c, + 0x65, 0x77, 0x61, 0x72, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x3d, 0x0a, 0x1b, 0x63, 0x75, 0x73, + 0x74, 0x6f, 0x6d, 0x5f, 0x6d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x5f, 0x63, 0x61, 0x76, + 0x65, 0x61, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x18, + 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x43, 0x61, + 0x76, 0x65, 0x61, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x24, 0x0a, 0x0e, 0x72, 0x65, 0x61, 0x64, + 0x5f, 0x6f, 0x6e, 0x6c, 0x79, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, + 0x52, 0x0c, 0x72, 0x65, 0x61, 0x64, 0x4f, 0x6e, 0x6c, 0x79, 0x4d, 0x6f, 0x64, 0x65, 0x22, 0x8b, + 0x01, 0x0a, 0x11, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x46, 0x65, 0x65, 0x64, + 0x62, 0x61, 0x63, 0x6b, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x29, 0x0a, 0x10, 0x72, 0x65, + 0x70, 0x6c, 0x61, 0x63, 0x65, 0x5f, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, 0x72, 0x65, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x35, 0x0a, 0x16, 0x72, 0x65, 0x70, 0x6c, 0x61, 0x63, 0x65, + 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x15, 0x72, 0x65, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x6d, 0x65, + 0x6e, 0x74, 0x53, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x2a, 0xcb, 0x02, 0x0a, + 0x10, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x54, 0x79, 0x70, + 0x65, 0x12, 0x1b, 0x0a, 0x17, 0x53, 0x43, 0x52, 0x49, 0x50, 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, + 0x5f, 0x50, 0x55, 0x42, 0x4b, 0x45, 0x59, 0x5f, 0x48, 0x41, 0x53, 0x48, 0x10, 0x00, 0x12, 0x1b, + 0x0a, 0x17, 0x53, 0x43, 0x52, 0x49, 0x50, 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x53, 0x43, + 0x52, 0x49, 0x50, 0x54, 0x5f, 0x48, 0x41, 0x53, 0x48, 0x10, 0x01, 0x12, 0x26, 0x0a, 0x22, 0x53, + 0x43, 0x52, 0x49, 0x50, 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x57, 0x49, 0x54, 0x4e, 0x45, + 0x53, 0x53, 0x5f, 0x56, 0x30, 0x5f, 0x50, 0x55, 0x42, 0x4b, 0x45, 0x59, 0x5f, 0x48, 0x41, 0x53, + 0x48, 0x10, 0x02, 0x12, 0x26, 0x0a, 0x22, 0x53, 0x43, 0x52, 0x49, 0x50, 0x54, 0x5f, 0x54, 0x59, + 0x50, 0x45, 0x5f, 0x57, 0x49, 0x54, 0x4e, 0x45, 0x53, 0x53, 0x5f, 0x56, 0x30, 0x5f, 0x53, 0x43, + 0x52, 0x49, 0x50, 0x54, 0x5f, 0x48, 0x41, 0x53, 0x48, 0x10, 0x03, 0x12, 0x16, 0x0a, 0x12, 0x53, + 0x43, 0x52, 0x49, 0x50, 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x50, 0x55, 0x42, 0x4b, 0x45, + 0x59, 0x10, 0x04, 0x12, 0x18, 0x0a, 0x14, 0x53, 0x43, 0x52, 0x49, 0x50, 0x54, 0x5f, 0x54, 0x59, + 0x50, 0x45, 0x5f, 0x4d, 0x55, 0x4c, 0x54, 0x49, 0x53, 0x49, 0x47, 0x10, 0x05, 0x12, 0x18, 0x0a, + 0x14, 0x53, 0x43, 0x52, 0x49, 0x50, 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x4e, 0x55, 0x4c, + 0x4c, 0x44, 0x41, 0x54, 0x41, 0x10, 0x06, 0x12, 0x1c, 0x0a, 0x18, 0x53, 0x43, 0x52, 0x49, 0x50, + 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x4e, 0x4f, 0x4e, 0x5f, 0x53, 0x54, 0x41, 0x4e, 0x44, + 0x41, 0x52, 0x44, 0x10, 0x07, 0x12, 0x1f, 0x0a, 0x1b, 0x53, 0x43, 0x52, 0x49, 0x50, 0x54, 0x5f, + 0x54, 0x59, 0x50, 0x45, 0x5f, 0x57, 0x49, 0x54, 0x4e, 0x45, 0x53, 0x53, 0x5f, 0x55, 0x4e, 0x4b, + 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x08, 0x12, 0x22, 0x0a, 0x1e, 0x53, 0x43, 0x52, 0x49, 0x50, 0x54, + 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x57, 0x49, 0x54, 0x4e, 0x45, 0x53, 0x53, 0x5f, 0x56, 0x31, + 0x5f, 0x54, 0x41, 0x50, 0x52, 0x4f, 0x4f, 0x54, 0x10, 0x09, 0x2a, 0x62, 0x0a, 0x15, 0x43, 0x6f, + 0x69, 0x6e, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x72, 0x61, 0x74, + 0x65, 0x67, 0x79, 0x12, 0x1e, 0x0a, 0x1a, 0x53, 0x54, 0x52, 0x41, 0x54, 0x45, 0x47, 0x59, 0x5f, + 0x55, 0x53, 0x45, 0x5f, 0x47, 0x4c, 0x4f, 0x42, 0x41, 0x4c, 0x5f, 0x43, 0x4f, 0x4e, 0x46, 0x49, + 0x47, 0x10, 0x00, 0x12, 0x14, 0x0a, 0x10, 0x53, 0x54, 0x52, 0x41, 0x54, 0x45, 0x47, 0x59, 0x5f, + 0x4c, 0x41, 0x52, 0x47, 0x45, 0x53, 0x54, 0x10, 0x01, 0x12, 0x13, 0x0a, 0x0f, 0x53, 0x54, 0x52, + 0x41, 0x54, 0x45, 0x47, 0x59, 0x5f, 0x52, 0x41, 0x4e, 0x44, 0x4f, 0x4d, 0x10, 0x02, 0x2a, 0xac, + 0x01, 0x0a, 0x0b, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x54, 0x79, 0x70, 0x65, 0x12, 0x17, + 0x0a, 0x13, 0x57, 0x49, 0x54, 0x4e, 0x45, 0x53, 0x53, 0x5f, 0x50, 0x55, 0x42, 0x4b, 0x45, 0x59, + 0x5f, 0x48, 0x41, 0x53, 0x48, 0x10, 0x00, 0x12, 0x16, 0x0a, 0x12, 0x4e, 0x45, 0x53, 0x54, 0x45, + 0x44, 0x5f, 0x50, 0x55, 0x42, 0x4b, 0x45, 0x59, 0x5f, 0x48, 0x41, 0x53, 0x48, 0x10, 0x01, 0x12, + 0x1e, 0x0a, 0x1a, 0x55, 0x4e, 0x55, 0x53, 0x45, 0x44, 0x5f, 0x57, 0x49, 0x54, 0x4e, 0x45, 0x53, + 0x53, 0x5f, 0x50, 0x55, 0x42, 0x4b, 0x45, 0x59, 0x5f, 0x48, 0x41, 0x53, 0x48, 0x10, 0x02, 0x12, + 0x1d, 0x0a, 0x19, 0x55, 0x4e, 0x55, 0x53, 0x45, 0x44, 0x5f, 0x4e, 0x45, 0x53, 0x54, 0x45, 0x44, + 0x5f, 0x50, 0x55, 0x42, 0x4b, 0x45, 0x59, 0x5f, 0x48, 0x41, 0x53, 0x48, 0x10, 0x03, 0x12, 0x12, + 0x0a, 0x0e, 0x54, 0x41, 0x50, 0x52, 0x4f, 0x4f, 0x54, 0x5f, 0x50, 0x55, 0x42, 0x4b, 0x45, 0x59, + 0x10, 0x04, 0x12, 0x19, 0x0a, 0x15, 0x55, 0x4e, 0x55, 0x53, 0x45, 0x44, 0x5f, 0x54, 0x41, 0x50, + 0x52, 0x4f, 0x4f, 0x54, 0x5f, 0x50, 0x55, 0x42, 0x4b, 0x45, 0x59, 0x10, 0x05, 0x2a, 0x8c, 0x01, + 0x0a, 0x0e, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x6d, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, + 0x12, 0x1b, 0x0a, 0x17, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x5f, 0x43, 0x4f, 0x4d, 0x4d, + 0x49, 0x54, 0x4d, 0x45, 0x4e, 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x10, 0x00, 0x12, 0x0a, 0x0a, + 0x06, 0x4c, 0x45, 0x47, 0x41, 0x43, 0x59, 0x10, 0x01, 0x12, 0x15, 0x0a, 0x11, 0x53, 0x54, 0x41, + 0x54, 0x49, 0x43, 0x5f, 0x52, 0x45, 0x4d, 0x4f, 0x54, 0x45, 0x5f, 0x4b, 0x45, 0x59, 0x10, 0x02, + 0x12, 0x0b, 0x0a, 0x07, 0x41, 0x4e, 0x43, 0x48, 0x4f, 0x52, 0x53, 0x10, 0x03, 0x12, 0x19, 0x0a, + 0x15, 0x53, 0x43, 0x52, 0x49, 0x50, 0x54, 0x5f, 0x45, 0x4e, 0x46, 0x4f, 0x52, 0x43, 0x45, 0x44, + 0x5f, 0x4c, 0x45, 0x41, 0x53, 0x45, 0x10, 0x04, 0x12, 0x12, 0x0a, 0x0e, 0x53, 0x49, 0x4d, 0x50, + 0x4c, 0x45, 0x5f, 0x54, 0x41, 0x50, 0x52, 0x4f, 0x4f, 0x54, 0x10, 0x05, 0x2a, 0x61, 0x0a, 0x09, + 0x49, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x74, 0x6f, 0x72, 0x12, 0x15, 0x0a, 0x11, 0x49, 0x4e, 0x49, + 0x54, 0x49, 0x41, 0x54, 0x4f, 0x52, 0x5f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, + 0x12, 0x13, 0x0a, 0x0f, 0x49, 0x4e, 0x49, 0x54, 0x49, 0x41, 0x54, 0x4f, 0x52, 0x5f, 0x4c, 0x4f, + 0x43, 0x41, 0x4c, 0x10, 0x01, 0x12, 0x14, 0x0a, 0x10, 0x49, 0x4e, 0x49, 0x54, 0x49, 0x41, 0x54, + 0x4f, 0x52, 0x5f, 0x52, 0x45, 0x4d, 0x4f, 0x54, 0x45, 0x10, 0x02, 0x12, 0x12, 0x0a, 0x0e, 0x49, + 0x4e, 0x49, 0x54, 0x49, 0x41, 0x54, 0x4f, 0x52, 0x5f, 0x42, 0x4f, 0x54, 0x48, 0x10, 0x03, 0x2a, + 0x60, 0x0a, 0x0e, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x79, 0x70, + 0x65, 0x12, 0x10, 0x0a, 0x0c, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, + 0x4e, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x41, 0x4e, 0x43, 0x48, 0x4f, 0x52, 0x10, 0x01, 0x12, + 0x11, 0x0a, 0x0d, 0x49, 0x4e, 0x43, 0x4f, 0x4d, 0x49, 0x4e, 0x47, 0x5f, 0x48, 0x54, 0x4c, 0x43, + 0x10, 0x02, 0x12, 0x11, 0x0a, 0x0d, 0x4f, 0x55, 0x54, 0x47, 0x4f, 0x49, 0x4e, 0x47, 0x5f, 0x48, + 0x54, 0x4c, 0x43, 0x10, 0x03, 0x12, 0x0a, 0x0a, 0x06, 0x43, 0x4f, 0x4d, 0x4d, 0x49, 0x54, 0x10, + 0x04, 0x2a, 0x71, 0x0a, 0x11, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x4f, + 0x75, 0x74, 0x63, 0x6f, 0x6d, 0x65, 0x12, 0x13, 0x0a, 0x0f, 0x4f, 0x55, 0x54, 0x43, 0x4f, 0x4d, + 0x45, 0x5f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x43, + 0x4c, 0x41, 0x49, 0x4d, 0x45, 0x44, 0x10, 0x01, 0x12, 0x0d, 0x0a, 0x09, 0x55, 0x4e, 0x43, 0x4c, + 0x41, 0x49, 0x4d, 0x45, 0x44, 0x10, 0x02, 0x12, 0x0d, 0x0a, 0x09, 0x41, 0x42, 0x41, 0x4e, 0x44, + 0x4f, 0x4e, 0x45, 0x44, 0x10, 0x03, 0x12, 0x0f, 0x0a, 0x0b, 0x46, 0x49, 0x52, 0x53, 0x54, 0x5f, + 0x53, 0x54, 0x41, 0x47, 0x45, 0x10, 0x04, 0x12, 0x0b, 0x0a, 0x07, 0x54, 0x49, 0x4d, 0x45, 0x4f, + 0x55, 0x54, 0x10, 0x05, 0x2a, 0x39, 0x0a, 0x0e, 0x4e, 0x6f, 0x64, 0x65, 0x4d, 0x65, 0x74, 0x72, + 0x69, 0x63, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, + 0x4e, 0x10, 0x00, 0x12, 0x1a, 0x0a, 0x16, 0x42, 0x45, 0x54, 0x57, 0x45, 0x45, 0x4e, 0x4e, 0x45, + 0x53, 0x53, 0x5f, 0x43, 0x45, 0x4e, 0x54, 0x52, 0x41, 0x4c, 0x49, 0x54, 0x59, 0x10, 0x01, 0x2a, + 0x3b, 0x0a, 0x10, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x48, 0x54, 0x4c, 0x43, 0x53, 0x74, + 0x61, 0x74, 0x65, 0x12, 0x0c, 0x0a, 0x08, 0x41, 0x43, 0x43, 0x45, 0x50, 0x54, 0x45, 0x44, 0x10, + 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x53, 0x45, 0x54, 0x54, 0x4c, 0x45, 0x44, 0x10, 0x01, 0x12, 0x0c, + 0x0a, 0x08, 0x43, 0x41, 0x4e, 0x43, 0x45, 0x4c, 0x45, 0x44, 0x10, 0x02, 0x2a, 0xf6, 0x01, 0x0a, + 0x14, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x46, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x52, + 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x12, 0x17, 0x0a, 0x13, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, + 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x4e, 0x4f, 0x4e, 0x45, 0x10, 0x00, 0x12, 0x1a, + 0x0a, 0x16, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, + 0x5f, 0x54, 0x49, 0x4d, 0x45, 0x4f, 0x55, 0x54, 0x10, 0x01, 0x12, 0x1b, 0x0a, 0x17, 0x46, 0x41, + 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x4e, 0x4f, 0x5f, + 0x52, 0x4f, 0x55, 0x54, 0x45, 0x10, 0x02, 0x12, 0x18, 0x0a, 0x14, 0x46, 0x41, 0x49, 0x4c, 0x55, + 0x52, 0x45, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, + 0x03, 0x12, 0x2c, 0x0a, 0x28, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x52, 0x45, 0x41, + 0x53, 0x4f, 0x4e, 0x5f, 0x49, 0x4e, 0x43, 0x4f, 0x52, 0x52, 0x45, 0x43, 0x54, 0x5f, 0x50, 0x41, + 0x59, 0x4d, 0x45, 0x4e, 0x54, 0x5f, 0x44, 0x45, 0x54, 0x41, 0x49, 0x4c, 0x53, 0x10, 0x04, 0x12, + 0x27, 0x0a, 0x23, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, + 0x4e, 0x5f, 0x49, 0x4e, 0x53, 0x55, 0x46, 0x46, 0x49, 0x43, 0x49, 0x45, 0x4e, 0x54, 0x5f, 0x42, + 0x41, 0x4c, 0x41, 0x4e, 0x43, 0x45, 0x10, 0x05, 0x12, 0x1b, 0x0a, 0x17, 0x46, 0x41, 0x49, 0x4c, + 0x55, 0x52, 0x45, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x43, 0x41, 0x4e, 0x43, 0x45, + 0x4c, 0x45, 0x44, 0x10, 0x06, 0x2a, 0x89, 0x05, 0x0a, 0x0a, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, + 0x65, 0x42, 0x69, 0x74, 0x12, 0x18, 0x0a, 0x14, 0x44, 0x41, 0x54, 0x41, 0x4c, 0x4f, 0x53, 0x53, + 0x5f, 0x50, 0x52, 0x4f, 0x54, 0x45, 0x43, 0x54, 0x5f, 0x52, 0x45, 0x51, 0x10, 0x00, 0x12, 0x18, + 0x0a, 0x14, 0x44, 0x41, 0x54, 0x41, 0x4c, 0x4f, 0x53, 0x53, 0x5f, 0x50, 0x52, 0x4f, 0x54, 0x45, + 0x43, 0x54, 0x5f, 0x4f, 0x50, 0x54, 0x10, 0x01, 0x12, 0x17, 0x0a, 0x13, 0x49, 0x4e, 0x49, 0x54, + 0x49, 0x41, 0x4c, 0x5f, 0x52, 0x4f, 0x55, 0x49, 0x4e, 0x47, 0x5f, 0x53, 0x59, 0x4e, 0x43, 0x10, + 0x03, 0x12, 0x1f, 0x0a, 0x1b, 0x55, 0x50, 0x46, 0x52, 0x4f, 0x4e, 0x54, 0x5f, 0x53, 0x48, 0x55, + 0x54, 0x44, 0x4f, 0x57, 0x4e, 0x5f, 0x53, 0x43, 0x52, 0x49, 0x50, 0x54, 0x5f, 0x52, 0x45, 0x51, + 0x10, 0x04, 0x12, 0x1f, 0x0a, 0x1b, 0x55, 0x50, 0x46, 0x52, 0x4f, 0x4e, 0x54, 0x5f, 0x53, 0x48, + 0x55, 0x54, 0x44, 0x4f, 0x57, 0x4e, 0x5f, 0x53, 0x43, 0x52, 0x49, 0x50, 0x54, 0x5f, 0x4f, 0x50, + 0x54, 0x10, 0x05, 0x12, 0x16, 0x0a, 0x12, 0x47, 0x4f, 0x53, 0x53, 0x49, 0x50, 0x5f, 0x51, 0x55, + 0x45, 0x52, 0x49, 0x45, 0x53, 0x5f, 0x52, 0x45, 0x51, 0x10, 0x06, 0x12, 0x16, 0x0a, 0x12, 0x47, + 0x4f, 0x53, 0x53, 0x49, 0x50, 0x5f, 0x51, 0x55, 0x45, 0x52, 0x49, 0x45, 0x53, 0x5f, 0x4f, 0x50, + 0x54, 0x10, 0x07, 0x12, 0x11, 0x0a, 0x0d, 0x54, 0x4c, 0x56, 0x5f, 0x4f, 0x4e, 0x49, 0x4f, 0x4e, + 0x5f, 0x52, 0x45, 0x51, 0x10, 0x08, 0x12, 0x11, 0x0a, 0x0d, 0x54, 0x4c, 0x56, 0x5f, 0x4f, 0x4e, + 0x49, 0x4f, 0x4e, 0x5f, 0x4f, 0x50, 0x54, 0x10, 0x09, 0x12, 0x1a, 0x0a, 0x16, 0x45, 0x58, 0x54, + 0x5f, 0x47, 0x4f, 0x53, 0x53, 0x49, 0x50, 0x5f, 0x51, 0x55, 0x45, 0x52, 0x49, 0x45, 0x53, 0x5f, + 0x52, 0x45, 0x51, 0x10, 0x0a, 0x12, 0x1a, 0x0a, 0x16, 0x45, 0x58, 0x54, 0x5f, 0x47, 0x4f, 0x53, + 0x53, 0x49, 0x50, 0x5f, 0x51, 0x55, 0x45, 0x52, 0x49, 0x45, 0x53, 0x5f, 0x4f, 0x50, 0x54, 0x10, + 0x0b, 0x12, 0x19, 0x0a, 0x15, 0x53, 0x54, 0x41, 0x54, 0x49, 0x43, 0x5f, 0x52, 0x45, 0x4d, 0x4f, + 0x54, 0x45, 0x5f, 0x4b, 0x45, 0x59, 0x5f, 0x52, 0x45, 0x51, 0x10, 0x0c, 0x12, 0x19, 0x0a, 0x15, + 0x53, 0x54, 0x41, 0x54, 0x49, 0x43, 0x5f, 0x52, 0x45, 0x4d, 0x4f, 0x54, 0x45, 0x5f, 0x4b, 0x45, + 0x59, 0x5f, 0x4f, 0x50, 0x54, 0x10, 0x0d, 0x12, 0x14, 0x0a, 0x10, 0x50, 0x41, 0x59, 0x4d, 0x45, + 0x4e, 0x54, 0x5f, 0x41, 0x44, 0x44, 0x52, 0x5f, 0x52, 0x45, 0x51, 0x10, 0x0e, 0x12, 0x14, 0x0a, + 0x10, 0x50, 0x41, 0x59, 0x4d, 0x45, 0x4e, 0x54, 0x5f, 0x41, 0x44, 0x44, 0x52, 0x5f, 0x4f, 0x50, + 0x54, 0x10, 0x0f, 0x12, 0x0b, 0x0a, 0x07, 0x4d, 0x50, 0x50, 0x5f, 0x52, 0x45, 0x51, 0x10, 0x10, + 0x12, 0x0b, 0x0a, 0x07, 0x4d, 0x50, 0x50, 0x5f, 0x4f, 0x50, 0x54, 0x10, 0x11, 0x12, 0x16, 0x0a, + 0x12, 0x57, 0x55, 0x4d, 0x42, 0x4f, 0x5f, 0x43, 0x48, 0x41, 0x4e, 0x4e, 0x45, 0x4c, 0x53, 0x5f, + 0x52, 0x45, 0x51, 0x10, 0x12, 0x12, 0x16, 0x0a, 0x12, 0x57, 0x55, 0x4d, 0x42, 0x4f, 0x5f, 0x43, + 0x48, 0x41, 0x4e, 0x4e, 0x45, 0x4c, 0x53, 0x5f, 0x4f, 0x50, 0x54, 0x10, 0x13, 0x12, 0x0f, 0x0a, + 0x0b, 0x41, 0x4e, 0x43, 0x48, 0x4f, 0x52, 0x53, 0x5f, 0x52, 0x45, 0x51, 0x10, 0x14, 0x12, 0x0f, + 0x0a, 0x0b, 0x41, 0x4e, 0x43, 0x48, 0x4f, 0x52, 0x53, 0x5f, 0x4f, 0x50, 0x54, 0x10, 0x15, 0x12, + 0x1d, 0x0a, 0x19, 0x41, 0x4e, 0x43, 0x48, 0x4f, 0x52, 0x53, 0x5f, 0x5a, 0x45, 0x52, 0x4f, 0x5f, + 0x46, 0x45, 0x45, 0x5f, 0x48, 0x54, 0x4c, 0x43, 0x5f, 0x52, 0x45, 0x51, 0x10, 0x16, 0x12, 0x1d, 0x0a, 0x19, 0x41, 0x4e, 0x43, 0x48, 0x4f, 0x52, 0x53, 0x5f, 0x5a, 0x45, 0x52, 0x4f, 0x5f, 0x46, - 0x45, 0x45, 0x5f, 0x48, 0x54, 0x4c, 0x43, 0x5f, 0x52, 0x45, 0x51, 0x10, 0x16, 0x12, 0x1d, 0x0a, - 0x19, 0x41, 0x4e, 0x43, 0x48, 0x4f, 0x52, 0x53, 0x5f, 0x5a, 0x45, 0x52, 0x4f, 0x5f, 0x46, 0x45, - 0x45, 0x5f, 0x48, 0x54, 0x4c, 0x43, 0x5f, 0x4f, 0x50, 0x54, 0x10, 0x17, 0x12, 0x1b, 0x0a, 0x17, - 0x52, 0x4f, 0x55, 0x54, 0x45, 0x5f, 0x42, 0x4c, 0x49, 0x4e, 0x44, 0x49, 0x4e, 0x47, 0x5f, 0x52, - 0x45, 0x51, 0x55, 0x49, 0x52, 0x45, 0x44, 0x10, 0x18, 0x12, 0x1b, 0x0a, 0x17, 0x52, 0x4f, 0x55, - 0x54, 0x45, 0x5f, 0x42, 0x4c, 0x49, 0x4e, 0x44, 0x49, 0x4e, 0x47, 0x5f, 0x4f, 0x50, 0x54, 0x49, - 0x4f, 0x4e, 0x41, 0x4c, 0x10, 0x19, 0x12, 0x0b, 0x0a, 0x07, 0x41, 0x4d, 0x50, 0x5f, 0x52, 0x45, - 0x51, 0x10, 0x1e, 0x12, 0x0b, 0x0a, 0x07, 0x41, 0x4d, 0x50, 0x5f, 0x4f, 0x50, 0x54, 0x10, 0x1f, - 0x2a, 0xac, 0x01, 0x0a, 0x0d, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x46, 0x61, 0x69, 0x6c, 0x75, - 0x72, 0x65, 0x12, 0x1a, 0x0a, 0x16, 0x55, 0x50, 0x44, 0x41, 0x54, 0x45, 0x5f, 0x46, 0x41, 0x49, - 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x1a, - 0x0a, 0x16, 0x55, 0x50, 0x44, 0x41, 0x54, 0x45, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, - 0x5f, 0x50, 0x45, 0x4e, 0x44, 0x49, 0x4e, 0x47, 0x10, 0x01, 0x12, 0x1c, 0x0a, 0x18, 0x55, 0x50, - 0x44, 0x41, 0x54, 0x45, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x4e, 0x4f, 0x54, - 0x5f, 0x46, 0x4f, 0x55, 0x4e, 0x44, 0x10, 0x02, 0x12, 0x1f, 0x0a, 0x1b, 0x55, 0x50, 0x44, 0x41, - 0x54, 0x45, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x49, 0x4e, 0x54, 0x45, 0x52, - 0x4e, 0x41, 0x4c, 0x5f, 0x45, 0x52, 0x52, 0x10, 0x03, 0x12, 0x24, 0x0a, 0x20, 0x55, 0x50, 0x44, - 0x41, 0x54, 0x45, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x49, 0x4e, 0x56, 0x41, - 0x4c, 0x49, 0x44, 0x5f, 0x50, 0x41, 0x52, 0x41, 0x4d, 0x45, 0x54, 0x45, 0x52, 0x10, 0x04, 0x32, - 0xb9, 0x27, 0x0a, 0x09, 0x4c, 0x69, 0x67, 0x68, 0x74, 0x6e, 0x69, 0x6e, 0x67, 0x12, 0x4a, 0x0a, - 0x0d, 0x57, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x12, 0x1b, - 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x42, 0x61, 0x6c, - 0x61, 0x6e, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x6c, 0x6e, - 0x72, 0x70, 0x63, 0x2e, 0x57, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, - 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4d, 0x0a, 0x0e, 0x43, 0x68, 0x61, - 0x6e, 0x6e, 0x65, 0x6c, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x12, 0x1c, 0x2e, 0x6c, 0x6e, - 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x42, 0x61, 0x6c, 0x61, 0x6e, - 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x6c, 0x6e, 0x72, 0x70, - 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4b, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x54, + 0x45, 0x45, 0x5f, 0x48, 0x54, 0x4c, 0x43, 0x5f, 0x4f, 0x50, 0x54, 0x10, 0x17, 0x12, 0x1b, 0x0a, + 0x17, 0x52, 0x4f, 0x55, 0x54, 0x45, 0x5f, 0x42, 0x4c, 0x49, 0x4e, 0x44, 0x49, 0x4e, 0x47, 0x5f, + 0x52, 0x45, 0x51, 0x55, 0x49, 0x52, 0x45, 0x44, 0x10, 0x18, 0x12, 0x1b, 0x0a, 0x17, 0x52, 0x4f, + 0x55, 0x54, 0x45, 0x5f, 0x42, 0x4c, 0x49, 0x4e, 0x44, 0x49, 0x4e, 0x47, 0x5f, 0x4f, 0x50, 0x54, + 0x49, 0x4f, 0x4e, 0x41, 0x4c, 0x10, 0x19, 0x12, 0x0b, 0x0a, 0x07, 0x41, 0x4d, 0x50, 0x5f, 0x52, + 0x45, 0x51, 0x10, 0x1e, 0x12, 0x0b, 0x0a, 0x07, 0x41, 0x4d, 0x50, 0x5f, 0x4f, 0x50, 0x54, 0x10, + 0x1f, 0x2a, 0xac, 0x01, 0x0a, 0x0d, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x46, 0x61, 0x69, 0x6c, + 0x75, 0x72, 0x65, 0x12, 0x1a, 0x0a, 0x16, 0x55, 0x50, 0x44, 0x41, 0x54, 0x45, 0x5f, 0x46, 0x41, + 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, + 0x1a, 0x0a, 0x16, 0x55, 0x50, 0x44, 0x41, 0x54, 0x45, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, + 0x45, 0x5f, 0x50, 0x45, 0x4e, 0x44, 0x49, 0x4e, 0x47, 0x10, 0x01, 0x12, 0x1c, 0x0a, 0x18, 0x55, + 0x50, 0x44, 0x41, 0x54, 0x45, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x4e, 0x4f, + 0x54, 0x5f, 0x46, 0x4f, 0x55, 0x4e, 0x44, 0x10, 0x02, 0x12, 0x1f, 0x0a, 0x1b, 0x55, 0x50, 0x44, + 0x41, 0x54, 0x45, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x49, 0x4e, 0x54, 0x45, + 0x52, 0x4e, 0x41, 0x4c, 0x5f, 0x45, 0x52, 0x52, 0x10, 0x03, 0x12, 0x24, 0x0a, 0x20, 0x55, 0x50, + 0x44, 0x41, 0x54, 0x45, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x49, 0x4e, 0x56, + 0x41, 0x4c, 0x49, 0x44, 0x5f, 0x50, 0x41, 0x52, 0x41, 0x4d, 0x45, 0x54, 0x45, 0x52, 0x10, 0x04, + 0x32, 0xb9, 0x27, 0x0a, 0x09, 0x4c, 0x69, 0x67, 0x68, 0x74, 0x6e, 0x69, 0x6e, 0x67, 0x12, 0x4a, + 0x0a, 0x0d, 0x57, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x12, + 0x1b, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x42, 0x61, + 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x6c, + 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x42, 0x61, 0x6c, 0x61, 0x6e, + 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4d, 0x0a, 0x0e, 0x43, 0x68, + 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x12, 0x1c, 0x2e, 0x6c, + 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x42, 0x61, 0x6c, 0x61, + 0x6e, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x6c, 0x6e, 0x72, + 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, + 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4b, 0x0a, 0x0f, 0x47, 0x65, 0x74, + 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x1d, 0x2e, 0x6c, + 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x6c, 0x6e, + 0x72, 0x70, 0x63, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x44, + 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x44, 0x0a, 0x0b, 0x45, 0x73, 0x74, 0x69, 0x6d, 0x61, + 0x74, 0x65, 0x46, 0x65, 0x65, 0x12, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x45, 0x73, + 0x74, 0x69, 0x6d, 0x61, 0x74, 0x65, 0x46, 0x65, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x45, 0x73, 0x74, 0x69, 0x6d, 0x61, 0x74, + 0x65, 0x46, 0x65, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3e, 0x0a, 0x09, + 0x53, 0x65, 0x6e, 0x64, 0x43, 0x6f, 0x69, 0x6e, 0x73, 0x12, 0x17, 0x2e, 0x6c, 0x6e, 0x72, 0x70, + 0x63, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x43, 0x6f, 0x69, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x43, + 0x6f, 0x69, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x44, 0x0a, 0x0b, + 0x4c, 0x69, 0x73, 0x74, 0x55, 0x6e, 0x73, 0x70, 0x65, 0x6e, 0x74, 0x12, 0x19, 0x2e, 0x6c, 0x6e, + 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x55, 0x6e, 0x73, 0x70, 0x65, 0x6e, 0x74, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4c, + 0x69, 0x73, 0x74, 0x55, 0x6e, 0x73, 0x70, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x4c, 0x0a, 0x15, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x1d, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, - 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x6c, 0x6e, 0x72, - 0x70, 0x63, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x65, - 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x44, 0x0a, 0x0b, 0x45, 0x73, 0x74, 0x69, 0x6d, 0x61, 0x74, - 0x65, 0x46, 0x65, 0x65, 0x12, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x45, 0x73, 0x74, - 0x69, 0x6d, 0x61, 0x74, 0x65, 0x46, 0x65, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x45, 0x73, 0x74, 0x69, 0x6d, 0x61, 0x74, 0x65, - 0x46, 0x65, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3e, 0x0a, 0x09, 0x53, - 0x65, 0x6e, 0x64, 0x43, 0x6f, 0x69, 0x6e, 0x73, 0x12, 0x17, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, - 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x43, 0x6f, 0x69, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x18, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x43, 0x6f, - 0x69, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x44, 0x0a, 0x0b, 0x4c, - 0x69, 0x73, 0x74, 0x55, 0x6e, 0x73, 0x70, 0x65, 0x6e, 0x74, 0x12, 0x19, 0x2e, 0x6c, 0x6e, 0x72, - 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x55, 0x6e, 0x73, 0x70, 0x65, 0x6e, 0x74, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, - 0x73, 0x74, 0x55, 0x6e, 0x73, 0x70, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x4c, 0x0a, 0x15, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x54, 0x72, - 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x1d, 0x2e, 0x6c, 0x6e, 0x72, - 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, - 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x12, 0x2e, 0x6c, 0x6e, 0x72, 0x70, - 0x63, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x30, 0x01, 0x12, - 0x3b, 0x0a, 0x08, 0x53, 0x65, 0x6e, 0x64, 0x4d, 0x61, 0x6e, 0x79, 0x12, 0x16, 0x2e, 0x6c, 0x6e, - 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x4d, 0x61, 0x6e, 0x79, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x6e, 0x64, - 0x4d, 0x61, 0x6e, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x41, 0x0a, 0x0a, - 0x4e, 0x65, 0x77, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x18, 0x2e, 0x6c, 0x6e, 0x72, - 0x70, 0x63, 0x2e, 0x4e, 0x65, 0x77, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4e, 0x65, 0x77, - 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, - 0x44, 0x0a, 0x0b, 0x53, 0x69, 0x67, 0x6e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x19, - 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x4d, 0x65, 0x73, 0x73, 0x61, - 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, - 0x63, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4a, 0x0a, 0x0d, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x4d, - 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x1b, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x56, - 0x65, 0x72, 0x69, 0x66, 0x79, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x56, 0x65, 0x72, 0x69, - 0x66, 0x79, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x44, 0x0a, 0x0b, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x50, 0x65, 0x65, 0x72, - 0x12, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, - 0x50, 0x65, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x6c, 0x6e, - 0x72, 0x70, 0x63, 0x2e, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x50, 0x65, 0x65, 0x72, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4d, 0x0a, 0x0e, 0x44, 0x69, 0x73, 0x63, 0x6f, - 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x50, 0x65, 0x65, 0x72, 0x12, 0x1c, 0x2e, 0x6c, 0x6e, 0x72, 0x70, - 0x63, 0x2e, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x50, 0x65, 0x65, 0x72, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, - 0x44, 0x69, 0x73, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x50, 0x65, 0x65, 0x72, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3e, 0x0a, 0x09, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x65, - 0x65, 0x72, 0x73, 0x12, 0x17, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, - 0x50, 0x65, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x6c, - 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x65, 0x65, 0x72, 0x73, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x47, 0x0a, 0x13, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, - 0x69, 0x62, 0x65, 0x50, 0x65, 0x65, 0x72, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x1c, 0x2e, - 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x65, 0x65, 0x72, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x53, - 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x1a, 0x10, 0x2e, 0x6c, 0x6e, - 0x72, 0x70, 0x63, 0x2e, 0x50, 0x65, 0x65, 0x72, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x30, 0x01, 0x12, - 0x38, 0x0a, 0x07, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x15, 0x2e, 0x6c, 0x6e, 0x72, - 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x16, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, - 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x47, 0x0a, 0x0c, 0x47, 0x65, 0x74, - 0x44, 0x65, 0x62, 0x75, 0x67, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, - 0x63, 0x2e, 0x47, 0x65, 0x74, 0x44, 0x65, 0x62, 0x75, 0x67, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, - 0x74, 0x44, 0x65, 0x62, 0x75, 0x67, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x50, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x52, 0x65, 0x63, 0x6f, 0x76, 0x65, 0x72, - 0x79, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1d, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, - 0x74, 0x52, 0x65, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, - 0x52, 0x65, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x50, 0x0a, 0x0f, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x43, - 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x12, 0x1d, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, + 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x12, 0x2e, 0x6c, 0x6e, 0x72, + 0x70, 0x63, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x30, 0x01, + 0x12, 0x3b, 0x0a, 0x08, 0x53, 0x65, 0x6e, 0x64, 0x4d, 0x61, 0x6e, 0x79, 0x12, 0x16, 0x2e, 0x6c, + 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x4d, 0x61, 0x6e, 0x79, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x6e, + 0x64, 0x4d, 0x61, 0x6e, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x41, 0x0a, + 0x0a, 0x4e, 0x65, 0x77, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x18, 0x2e, 0x6c, 0x6e, + 0x72, 0x70, 0x63, 0x2e, 0x4e, 0x65, 0x77, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4e, 0x65, + 0x77, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x44, 0x0a, 0x0b, 0x53, 0x69, 0x67, 0x6e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, + 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x4d, 0x65, 0x73, 0x73, + 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, + 0x70, 0x63, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4a, 0x0a, 0x0d, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, + 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x1b, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, + 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x56, 0x65, 0x72, + 0x69, 0x66, 0x79, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x44, 0x0a, 0x0b, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x50, 0x65, 0x65, + 0x72, 0x12, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, + 0x74, 0x50, 0x65, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x6c, + 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x50, 0x65, 0x65, 0x72, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4d, 0x0a, 0x0e, 0x44, 0x69, 0x73, 0x63, + 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x50, 0x65, 0x65, 0x72, 0x12, 0x1c, 0x2e, 0x6c, 0x6e, 0x72, + 0x70, 0x63, 0x2e, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x50, 0x65, 0x65, + 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, + 0x2e, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x50, 0x65, 0x65, 0x72, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3e, 0x0a, 0x09, 0x4c, 0x69, 0x73, 0x74, 0x50, + 0x65, 0x65, 0x72, 0x73, 0x12, 0x17, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, + 0x74, 0x50, 0x65, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, + 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x65, 0x65, 0x72, 0x73, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x47, 0x0a, 0x13, 0x53, 0x75, 0x62, 0x73, 0x63, + 0x72, 0x69, 0x62, 0x65, 0x50, 0x65, 0x65, 0x72, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x1c, + 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x65, 0x65, 0x72, 0x45, 0x76, 0x65, 0x6e, 0x74, + 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x1a, 0x10, 0x2e, 0x6c, + 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x65, 0x65, 0x72, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x30, 0x01, + 0x12, 0x38, 0x0a, 0x07, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x15, 0x2e, 0x6c, 0x6e, + 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x49, 0x6e, + 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x47, 0x0a, 0x0c, 0x47, 0x65, + 0x74, 0x44, 0x65, 0x62, 0x75, 0x67, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, + 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x44, 0x65, 0x62, 0x75, 0x67, 0x49, 0x6e, 0x66, 0x6f, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x47, + 0x65, 0x74, 0x44, 0x65, 0x62, 0x75, 0x67, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x50, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x52, 0x65, 0x63, 0x6f, 0x76, 0x65, + 0x72, 0x79, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1d, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x47, + 0x65, 0x74, 0x52, 0x65, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, + 0x74, 0x52, 0x65, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x50, 0x0a, 0x0f, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, + 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x12, 0x1d, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, + 0x2e, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x50, - 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x47, 0x0a, 0x0c, 0x4c, 0x69, 0x73, 0x74, 0x43, 0x68, - 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x12, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4c, - 0x69, 0x73, 0x74, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x43, - 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, - 0x56, 0x0a, 0x16, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x43, 0x68, 0x61, 0x6e, - 0x6e, 0x65, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x1f, 0x2e, 0x6c, 0x6e, 0x72, 0x70, - 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x53, 0x75, - 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x1a, 0x19, 0x2e, 0x6c, 0x6e, 0x72, - 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x55, - 0x70, 0x64, 0x61, 0x74, 0x65, 0x30, 0x01, 0x12, 0x4d, 0x0a, 0x0e, 0x43, 0x6c, 0x6f, 0x73, 0x65, - 0x64, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x12, 0x1c, 0x2e, 0x6c, 0x6e, 0x72, 0x70, - 0x63, 0x2e, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x64, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, - 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x64, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x41, 0x0a, 0x0f, 0x4f, 0x70, 0x65, 0x6e, 0x43, 0x68, - 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x53, 0x79, 0x6e, 0x63, 0x12, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x47, 0x0a, 0x0c, 0x4c, 0x69, 0x73, 0x74, 0x43, + 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x12, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, + 0x4c, 0x69, 0x73, 0x74, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, + 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x56, 0x0a, 0x16, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x43, 0x68, 0x61, + 0x6e, 0x6e, 0x65, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x1f, 0x2e, 0x6c, 0x6e, 0x72, + 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x53, + 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x1a, 0x19, 0x2e, 0x6c, 0x6e, + 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, + 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x30, 0x01, 0x12, 0x4d, 0x0a, 0x0e, 0x43, 0x6c, 0x6f, 0x73, + 0x65, 0x64, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x12, 0x1c, 0x2e, 0x6c, 0x6e, 0x72, + 0x70, 0x63, 0x2e, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x64, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, + 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, + 0x2e, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x64, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x41, 0x0a, 0x0f, 0x4f, 0x70, 0x65, 0x6e, 0x43, + 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x53, 0x79, 0x6e, 0x63, 0x12, 0x19, 0x2e, 0x6c, 0x6e, 0x72, + 0x70, 0x63, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x13, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, + 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x43, 0x0a, 0x0b, 0x4f, 0x70, + 0x65, 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x12, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x13, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, - 0x6e, 0x6e, 0x65, 0x6c, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x43, 0x0a, 0x0b, 0x4f, 0x70, 0x65, - 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x12, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, - 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4f, 0x70, 0x65, 0x6e, - 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x30, 0x01, 0x12, 0x53, - 0x0a, 0x10, 0x42, 0x61, 0x74, 0x63, 0x68, 0x4f, 0x70, 0x65, 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x6e, - 0x65, 0x6c, 0x12, 0x1e, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x42, 0x61, 0x74, 0x63, 0x68, - 0x4f, 0x70, 0x65, 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x42, 0x61, 0x74, 0x63, 0x68, - 0x4f, 0x70, 0x65, 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x4c, 0x0a, 0x10, 0x46, 0x75, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x53, 0x74, - 0x61, 0x74, 0x65, 0x53, 0x74, 0x65, 0x70, 0x12, 0x1b, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, - 0x46, 0x75, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x6f, - 0x6e, 0x4d, 0x73, 0x67, 0x1a, 0x1b, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x75, 0x6e, - 0x64, 0x69, 0x6e, 0x67, 0x53, 0x74, 0x61, 0x74, 0x65, 0x53, 0x74, 0x65, 0x70, 0x52, 0x65, 0x73, - 0x70, 0x12, 0x50, 0x0a, 0x0f, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x41, 0x63, 0x63, 0x65, - 0x70, 0x74, 0x6f, 0x72, 0x12, 0x1c, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, - 0x6e, 0x6e, 0x65, 0x6c, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x1a, 0x1b, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, - 0x65, 0x6c, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x28, - 0x01, 0x30, 0x01, 0x12, 0x46, 0x0a, 0x0c, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x43, 0x68, 0x61, 0x6e, - 0x6e, 0x65, 0x6c, 0x12, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x6c, 0x6f, 0x73, - 0x65, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x18, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x53, 0x74, 0x61, - 0x74, 0x75, 0x73, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x30, 0x01, 0x12, 0x4d, 0x0a, 0x0e, 0x41, - 0x62, 0x61, 0x6e, 0x64, 0x6f, 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x12, 0x1c, 0x2e, - 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x62, 0x61, 0x6e, 0x64, 0x6f, 0x6e, 0x43, 0x68, 0x61, - 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x6c, 0x6e, - 0x72, 0x70, 0x63, 0x2e, 0x41, 0x62, 0x61, 0x6e, 0x64, 0x6f, 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x6e, - 0x65, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3f, 0x0a, 0x0b, 0x53, 0x65, - 0x6e, 0x64, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x12, 0x2e, 0x6c, 0x6e, 0x72, 0x70, - 0x63, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x13, 0x2e, - 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x03, 0x88, 0x02, 0x01, 0x28, 0x01, 0x30, 0x01, 0x12, 0x3a, 0x0a, 0x0f, 0x53, - 0x65, 0x6e, 0x64, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x53, 0x79, 0x6e, 0x63, 0x12, 0x12, - 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4f, 0x70, 0x65, + 0x6e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x30, 0x01, 0x12, + 0x53, 0x0a, 0x10, 0x42, 0x61, 0x74, 0x63, 0x68, 0x4f, 0x70, 0x65, 0x6e, 0x43, 0x68, 0x61, 0x6e, + 0x6e, 0x65, 0x6c, 0x12, 0x1e, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x42, 0x61, 0x74, 0x63, + 0x68, 0x4f, 0x70, 0x65, 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x42, 0x61, 0x74, 0x63, + 0x68, 0x4f, 0x70, 0x65, 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4c, 0x0a, 0x10, 0x46, 0x75, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x53, + 0x74, 0x61, 0x74, 0x65, 0x53, 0x74, 0x65, 0x70, 0x12, 0x1b, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, + 0x2e, 0x46, 0x75, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x69, 0x74, 0x69, + 0x6f, 0x6e, 0x4d, 0x73, 0x67, 0x1a, 0x1b, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x75, + 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x53, 0x74, 0x61, 0x74, 0x65, 0x53, 0x74, 0x65, 0x70, 0x52, 0x65, + 0x73, 0x70, 0x12, 0x50, 0x0a, 0x0f, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x41, 0x63, 0x63, + 0x65, 0x70, 0x74, 0x6f, 0x72, 0x12, 0x1c, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, + 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x1a, 0x1b, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, + 0x6e, 0x65, 0x6c, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x28, 0x01, 0x30, 0x01, 0x12, 0x46, 0x0a, 0x0c, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x43, 0x68, 0x61, + 0x6e, 0x6e, 0x65, 0x6c, 0x12, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x6c, 0x6f, + 0x73, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x18, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x53, 0x74, + 0x61, 0x74, 0x75, 0x73, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x30, 0x01, 0x12, 0x4d, 0x0a, 0x0e, + 0x41, 0x62, 0x61, 0x6e, 0x64, 0x6f, 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x12, 0x1c, + 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x62, 0x61, 0x6e, 0x64, 0x6f, 0x6e, 0x43, 0x68, + 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x6c, + 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x62, 0x61, 0x6e, 0x64, 0x6f, 0x6e, 0x43, 0x68, 0x61, 0x6e, + 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3f, 0x0a, 0x0b, 0x53, + 0x65, 0x6e, 0x64, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x12, 0x2e, 0x6c, 0x6e, 0x72, + 0x70, 0x63, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x13, + 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x03, 0x88, 0x02, 0x01, 0x28, 0x01, 0x30, 0x01, 0x12, 0x3a, 0x0a, 0x0f, + 0x53, 0x65, 0x6e, 0x64, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x53, 0x79, 0x6e, 0x63, 0x12, + 0x12, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x13, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x6e, 0x64, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x46, 0x0a, 0x0b, 0x53, 0x65, 0x6e, 0x64, + 0x54, 0x6f, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x12, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, + 0x53, 0x65, 0x6e, 0x64, 0x54, 0x6f, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x13, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x46, 0x0a, 0x0b, 0x53, 0x65, 0x6e, 0x64, 0x54, - 0x6f, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x12, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, - 0x65, 0x6e, 0x64, 0x54, 0x6f, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x13, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x03, 0x88, 0x02, 0x01, 0x28, 0x01, 0x30, 0x01, 0x12, - 0x41, 0x0a, 0x0f, 0x53, 0x65, 0x6e, 0x64, 0x54, 0x6f, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x53, 0x79, - 0x6e, 0x63, 0x12, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x54, - 0x6f, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x13, 0x2e, - 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x37, 0x0a, 0x0a, 0x41, 0x64, 0x64, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, - 0x12, 0x0e, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, - 0x1a, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x64, 0x64, 0x49, 0x6e, 0x76, 0x6f, - 0x69, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x45, 0x0a, 0x0c, 0x4c, - 0x69, 0x73, 0x74, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x73, 0x12, 0x19, 0x2e, 0x6c, 0x6e, - 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4c, - 0x69, 0x73, 0x74, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x33, 0x0a, 0x0d, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x49, 0x6e, 0x76, 0x6f, - 0x69, 0x63, 0x65, 0x12, 0x12, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x61, 0x79, 0x6d, - 0x65, 0x6e, 0x74, 0x48, 0x61, 0x73, 0x68, 0x1a, 0x0e, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, - 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x12, 0x41, 0x0a, 0x11, 0x53, 0x75, 0x62, 0x73, 0x63, - 0x72, 0x69, 0x62, 0x65, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x73, 0x12, 0x1a, 0x2e, 0x6c, - 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x53, 0x75, 0x62, 0x73, - 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x1a, 0x0e, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, - 0x2e, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x30, 0x01, 0x12, 0x32, 0x0a, 0x0c, 0x44, 0x65, - 0x63, 0x6f, 0x64, 0x65, 0x50, 0x61, 0x79, 0x52, 0x65, 0x71, 0x12, 0x13, 0x2e, 0x6c, 0x6e, 0x72, - 0x70, 0x63, 0x2e, 0x50, 0x61, 0x79, 0x52, 0x65, 0x71, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x1a, - 0x0d, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x61, 0x79, 0x52, 0x65, 0x71, 0x12, 0x47, - 0x0a, 0x0c, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x1a, - 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x61, 0x79, 0x6d, 0x65, - 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x6c, 0x6e, 0x72, - 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4a, 0x0a, 0x0d, 0x44, 0x65, 0x6c, 0x65, 0x74, - 0x65, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x1b, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, - 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x44, 0x65, - 0x6c, 0x65, 0x74, 0x65, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x56, 0x0a, 0x11, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x41, 0x6c, 0x6c, - 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x1f, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, - 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x41, 0x6c, 0x6c, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, - 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x6c, 0x6e, 0x72, 0x70, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x03, 0x88, 0x02, 0x01, 0x28, 0x01, 0x30, 0x01, + 0x12, 0x41, 0x0a, 0x0f, 0x53, 0x65, 0x6e, 0x64, 0x54, 0x6f, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x53, + 0x79, 0x6e, 0x63, 0x12, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x6e, 0x64, + 0x54, 0x6f, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x13, + 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x37, 0x0a, 0x0a, 0x41, 0x64, 0x64, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, + 0x65, 0x12, 0x0e, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, + 0x65, 0x1a, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x64, 0x64, 0x49, 0x6e, 0x76, + 0x6f, 0x69, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x45, 0x0a, 0x0c, + 0x4c, 0x69, 0x73, 0x74, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x73, 0x12, 0x19, 0x2e, 0x6c, + 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, + 0x4c, 0x69, 0x73, 0x74, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x33, 0x0a, 0x0d, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x49, 0x6e, 0x76, + 0x6f, 0x69, 0x63, 0x65, 0x12, 0x12, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x61, 0x79, + 0x6d, 0x65, 0x6e, 0x74, 0x48, 0x61, 0x73, 0x68, 0x1a, 0x0e, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, + 0x2e, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x12, 0x41, 0x0a, 0x11, 0x53, 0x75, 0x62, 0x73, + 0x63, 0x72, 0x69, 0x62, 0x65, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x73, 0x12, 0x1a, 0x2e, + 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x53, 0x75, 0x62, + 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x1a, 0x0e, 0x2e, 0x6c, 0x6e, 0x72, 0x70, + 0x63, 0x2e, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x30, 0x01, 0x12, 0x32, 0x0a, 0x0c, 0x44, + 0x65, 0x63, 0x6f, 0x64, 0x65, 0x50, 0x61, 0x79, 0x52, 0x65, 0x71, 0x12, 0x13, 0x2e, 0x6c, 0x6e, + 0x72, 0x70, 0x63, 0x2e, 0x50, 0x61, 0x79, 0x52, 0x65, 0x71, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, + 0x1a, 0x0d, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x61, 0x79, 0x52, 0x65, 0x71, 0x12, + 0x47, 0x0a, 0x0c, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x12, + 0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x61, 0x79, 0x6d, + 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x6c, 0x6e, + 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x73, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4a, 0x0a, 0x0d, 0x44, 0x65, 0x6c, 0x65, + 0x74, 0x65, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x1b, 0x2e, 0x6c, 0x6e, 0x72, 0x70, + 0x63, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x44, + 0x65, 0x6c, 0x65, 0x74, 0x65, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x56, 0x0a, 0x11, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x41, 0x6c, + 0x6c, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x1f, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x41, 0x6c, 0x6c, 0x50, 0x61, 0x79, 0x6d, 0x65, - 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x40, 0x0a, 0x0d, 0x44, - 0x65, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x47, 0x72, 0x61, 0x70, 0x68, 0x12, 0x1a, 0x2e, 0x6c, - 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x47, 0x72, 0x61, 0x70, - 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x13, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, - 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x47, 0x72, 0x61, 0x70, 0x68, 0x12, 0x47, 0x0a, - 0x0e, 0x47, 0x65, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x12, - 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x4d, 0x65, 0x74, 0x72, - 0x69, 0x63, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, - 0x70, 0x63, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x39, 0x0a, 0x0b, 0x47, 0x65, 0x74, 0x43, 0x68, 0x61, - 0x6e, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x16, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, - 0x61, 0x6e, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x12, 0x2e, - 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x45, 0x64, 0x67, - 0x65, 0x12, 0x36, 0x0a, 0x0b, 0x47, 0x65, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, - 0x12, 0x16, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, - 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x0f, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, - 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x44, 0x0a, 0x0b, 0x51, 0x75, 0x65, - 0x72, 0x79, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x12, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, - 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x51, 0x75, 0x65, 0x72, - 0x79, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, - 0x3f, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x49, 0x6e, 0x66, - 0x6f, 0x12, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, - 0x6b, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x12, 0x2e, 0x6c, - 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x49, 0x6e, 0x66, 0x6f, - 0x12, 0x35, 0x0a, 0x0a, 0x53, 0x74, 0x6f, 0x70, 0x44, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x12, 0x12, - 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x74, 0x6f, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x13, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x74, 0x6f, 0x70, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x57, 0x0a, 0x15, 0x53, 0x75, 0x62, 0x73, 0x63, - 0x72, 0x69, 0x62, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x47, 0x72, 0x61, 0x70, 0x68, - 0x12, 0x20, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x72, 0x61, 0x70, 0x68, 0x54, 0x6f, - 0x70, 0x6f, 0x6c, 0x6f, 0x67, 0x79, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, - 0x6f, 0x6e, 0x1a, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x72, 0x61, 0x70, 0x68, - 0x54, 0x6f, 0x70, 0x6f, 0x6c, 0x6f, 0x67, 0x79, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x30, 0x01, - 0x12, 0x41, 0x0a, 0x0a, 0x44, 0x65, 0x62, 0x75, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x18, - 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x44, 0x65, 0x62, 0x75, 0x67, 0x4c, 0x65, 0x76, 0x65, - 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, - 0x2e, 0x44, 0x65, 0x62, 0x75, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x3e, 0x0a, 0x09, 0x46, 0x65, 0x65, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, - 0x12, 0x17, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x65, 0x65, 0x52, 0x65, 0x70, 0x6f, - 0x72, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x6c, 0x6e, 0x72, 0x70, - 0x63, 0x2e, 0x46, 0x65, 0x65, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x4e, 0x0a, 0x13, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x43, 0x68, 0x61, - 0x6e, 0x6e, 0x65, 0x6c, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, - 0x70, 0x63, 0x2e, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x50, - 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x56, 0x0a, 0x11, 0x46, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x69, 0x6e, - 0x67, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x12, 0x1f, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, - 0x2e, 0x46, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x48, 0x69, 0x73, 0x74, 0x6f, - 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x6c, 0x6e, 0x72, 0x70, + 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x6c, 0x6e, 0x72, + 0x70, 0x63, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x41, 0x6c, 0x6c, 0x50, 0x61, 0x79, 0x6d, + 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x40, 0x0a, 0x0d, + 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x47, 0x72, 0x61, 0x70, 0x68, 0x12, 0x1a, 0x2e, + 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x47, 0x72, 0x61, + 0x70, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x13, 0x2e, 0x6c, 0x6e, 0x72, 0x70, + 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x47, 0x72, 0x61, 0x70, 0x68, 0x12, 0x47, + 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, + 0x12, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x4d, 0x65, 0x74, + 0x72, 0x69, 0x63, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x6c, 0x6e, + 0x72, 0x70, 0x63, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x39, 0x0a, 0x0b, 0x47, 0x65, 0x74, 0x43, 0x68, + 0x61, 0x6e, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x16, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, + 0x68, 0x61, 0x6e, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x12, + 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x45, 0x64, + 0x67, 0x65, 0x12, 0x36, 0x0a, 0x0b, 0x47, 0x65, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, + 0x6f, 0x12, 0x16, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x6e, + 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x0f, 0x2e, 0x6c, 0x6e, 0x72, 0x70, + 0x63, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x44, 0x0a, 0x0b, 0x51, 0x75, + 0x65, 0x72, 0x79, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x12, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, + 0x63, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x51, 0x75, 0x65, + 0x72, 0x79, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x3f, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x49, 0x6e, + 0x66, 0x6f, 0x12, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4e, 0x65, 0x74, 0x77, 0x6f, + 0x72, 0x6b, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x12, 0x2e, + 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x49, 0x6e, 0x66, + 0x6f, 0x12, 0x35, 0x0a, 0x0a, 0x53, 0x74, 0x6f, 0x70, 0x44, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x12, + 0x12, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x74, 0x6f, 0x70, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x13, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x74, 0x6f, 0x70, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x57, 0x0a, 0x15, 0x53, 0x75, 0x62, 0x73, + 0x63, 0x72, 0x69, 0x62, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x47, 0x72, 0x61, 0x70, + 0x68, 0x12, 0x20, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x72, 0x61, 0x70, 0x68, 0x54, + 0x6f, 0x70, 0x6f, 0x6c, 0x6f, 0x67, 0x79, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, + 0x69, 0x6f, 0x6e, 0x1a, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x72, 0x61, 0x70, + 0x68, 0x54, 0x6f, 0x70, 0x6f, 0x6c, 0x6f, 0x67, 0x79, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x30, + 0x01, 0x12, 0x41, 0x0a, 0x0a, 0x44, 0x65, 0x62, 0x75, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x12, + 0x18, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x44, 0x65, 0x62, 0x75, 0x67, 0x4c, 0x65, 0x76, + 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, + 0x63, 0x2e, 0x44, 0x65, 0x62, 0x75, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3e, 0x0a, 0x09, 0x46, 0x65, 0x65, 0x52, 0x65, 0x70, 0x6f, 0x72, + 0x74, 0x12, 0x17, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x65, 0x65, 0x52, 0x65, 0x70, + 0x6f, 0x72, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x6c, 0x6e, 0x72, + 0x70, 0x63, 0x2e, 0x46, 0x65, 0x65, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4e, 0x0a, 0x13, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x43, 0x68, + 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x1a, 0x2e, 0x6c, 0x6e, + 0x72, 0x70, 0x63, 0x2e, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, + 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x56, 0x0a, 0x11, 0x46, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x69, + 0x6e, 0x67, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x12, 0x1f, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x48, 0x69, 0x73, 0x74, - 0x6f, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4e, 0x0a, 0x13, 0x45, - 0x78, 0x70, 0x6f, 0x72, 0x74, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x42, 0x61, 0x63, 0x6b, - 0x75, 0x70, 0x12, 0x21, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x45, 0x78, 0x70, 0x6f, 0x72, - 0x74, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x14, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, - 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x12, 0x54, 0x0a, 0x17, 0x45, - 0x78, 0x70, 0x6f, 0x72, 0x74, 0x41, 0x6c, 0x6c, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x42, - 0x61, 0x63, 0x6b, 0x75, 0x70, 0x73, 0x12, 0x1e, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, - 0x68, 0x61, 0x6e, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x45, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, - 0x68, 0x61, 0x6e, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, - 0x74, 0x12, 0x4e, 0x0a, 0x10, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x43, 0x68, 0x61, 0x6e, 0x42, - 0x61, 0x63, 0x6b, 0x75, 0x70, 0x12, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, - 0x61, 0x6e, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, - 0x1a, 0x1f, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x43, - 0x68, 0x61, 0x6e, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x56, 0x0a, 0x15, 0x52, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x43, 0x68, 0x61, 0x6e, - 0x6e, 0x65, 0x6c, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x73, 0x12, 0x1f, 0x2e, 0x6c, 0x6e, 0x72, - 0x70, 0x63, 0x2e, 0x52, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x42, 0x61, - 0x63, 0x6b, 0x75, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x6c, 0x6e, - 0x72, 0x70, 0x63, 0x2e, 0x52, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x42, 0x61, 0x63, 0x6b, 0x75, - 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x58, 0x0a, 0x17, 0x53, 0x75, 0x62, - 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x42, 0x61, 0x63, - 0x6b, 0x75, 0x70, 0x73, 0x12, 0x20, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, - 0x6e, 0x6e, 0x65, 0x6c, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, - 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x1a, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, + 0x6f, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x6c, 0x6e, 0x72, + 0x70, 0x63, 0x2e, 0x46, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x48, 0x69, 0x73, + 0x74, 0x6f, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4e, 0x0a, 0x13, + 0x45, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x42, 0x61, 0x63, + 0x6b, 0x75, 0x70, 0x12, 0x21, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x45, 0x78, 0x70, 0x6f, + 0x72, 0x74, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x14, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, + 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x12, 0x54, 0x0a, 0x17, + 0x45, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x41, 0x6c, 0x6c, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, + 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x73, 0x12, 0x1e, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, + 0x43, 0x68, 0x61, 0x6e, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x45, 0x78, 0x70, 0x6f, 0x72, 0x74, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, + 0x43, 0x68, 0x61, 0x6e, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, + 0x6f, 0x74, 0x12, 0x4e, 0x0a, 0x10, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x43, 0x68, 0x61, 0x6e, + 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x12, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, - 0x74, 0x30, 0x01, 0x12, 0x47, 0x0a, 0x0c, 0x42, 0x61, 0x6b, 0x65, 0x4d, 0x61, 0x63, 0x61, 0x72, - 0x6f, 0x6f, 0x6e, 0x12, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x42, 0x61, 0x6b, 0x65, - 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x1b, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x42, 0x61, 0x6b, 0x65, 0x4d, 0x61, 0x63, 0x61, - 0x72, 0x6f, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x50, 0x0a, 0x0f, - 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x49, 0x44, 0x73, 0x12, - 0x1d, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x61, 0x63, 0x61, - 0x72, 0x6f, 0x6f, 0x6e, 0x49, 0x44, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, - 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x61, 0x63, 0x61, 0x72, - 0x6f, 0x6f, 0x6e, 0x49, 0x44, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x53, - 0x0a, 0x10, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, - 0x49, 0x44, 0x12, 0x1e, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, - 0x65, 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x49, 0x44, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, - 0x65, 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x49, 0x44, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x50, 0x0a, 0x0f, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x65, 0x72, 0x6d, 0x69, - 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x1d, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4c, + 0x74, 0x1a, 0x1f, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, + 0x43, 0x68, 0x61, 0x6e, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x56, 0x0a, 0x15, 0x52, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x43, 0x68, 0x61, + 0x6e, 0x6e, 0x65, 0x6c, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x73, 0x12, 0x1f, 0x2e, 0x6c, 0x6e, + 0x72, 0x70, 0x63, 0x2e, 0x52, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x42, + 0x61, 0x63, 0x6b, 0x75, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x6c, + 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x42, 0x61, 0x63, 0x6b, + 0x75, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x58, 0x0a, 0x17, 0x53, 0x75, + 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x42, 0x61, + 0x63, 0x6b, 0x75, 0x70, 0x73, 0x12, 0x20, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, + 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x53, 0x75, 0x62, 0x73, 0x63, + 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x1a, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, + 0x43, 0x68, 0x61, 0x6e, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, + 0x6f, 0x74, 0x30, 0x01, 0x12, 0x47, 0x0a, 0x0c, 0x42, 0x61, 0x6b, 0x65, 0x4d, 0x61, 0x63, 0x61, + 0x72, 0x6f, 0x6f, 0x6e, 0x12, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x42, 0x61, 0x6b, + 0x65, 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x1b, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x42, 0x61, 0x6b, 0x65, 0x4d, 0x61, 0x63, + 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x50, 0x0a, + 0x0f, 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x49, 0x44, 0x73, + 0x12, 0x1d, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x61, 0x63, + 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x49, 0x44, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x1e, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x61, 0x63, 0x61, + 0x72, 0x6f, 0x6f, 0x6e, 0x49, 0x44, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x53, 0x0a, 0x10, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, + 0x6e, 0x49, 0x44, 0x12, 0x1e, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x44, 0x65, 0x6c, 0x65, + 0x74, 0x65, 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x49, 0x44, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x44, 0x65, 0x6c, 0x65, + 0x74, 0x65, 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x49, 0x44, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x50, 0x0a, 0x0f, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x65, 0x72, 0x6d, + 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x1d, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, + 0x4c, 0x69, 0x73, 0x74, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, - 0x73, 0x74, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x53, 0x0a, 0x18, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x4d, 0x61, - 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, - 0x73, 0x12, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x4d, - 0x61, 0x63, 0x50, 0x65, 0x72, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, - 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x4d, 0x61, 0x63, 0x50, 0x65, - 0x72, 0x6d, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x56, 0x0a, 0x15, 0x52, 0x65, - 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x52, 0x50, 0x43, 0x4d, 0x69, 0x64, 0x64, 0x6c, 0x65, 0x77, - 0x61, 0x72, 0x65, 0x12, 0x1c, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x50, 0x43, 0x4d, - 0x69, 0x64, 0x64, 0x6c, 0x65, 0x77, 0x61, 0x72, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x1a, 0x1b, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x50, 0x43, 0x4d, 0x69, 0x64, - 0x64, 0x6c, 0x65, 0x77, 0x61, 0x72, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x28, 0x01, - 0x30, 0x01, 0x12, 0x56, 0x0a, 0x11, 0x53, 0x65, 0x6e, 0x64, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, - 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x1f, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, - 0x53, 0x65, 0x6e, 0x64, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, - 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x53, 0x0a, 0x18, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x4d, + 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, + 0x6e, 0x73, 0x12, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x65, 0x63, 0x6b, + 0x4d, 0x61, 0x63, 0x50, 0x65, 0x72, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, + 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x4d, 0x61, 0x63, 0x50, + 0x65, 0x72, 0x6d, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x56, 0x0a, 0x15, 0x52, + 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x52, 0x50, 0x43, 0x4d, 0x69, 0x64, 0x64, 0x6c, 0x65, + 0x77, 0x61, 0x72, 0x65, 0x12, 0x1c, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x50, 0x43, + 0x4d, 0x69, 0x64, 0x64, 0x6c, 0x65, 0x77, 0x61, 0x72, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x1a, 0x1b, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x50, 0x43, 0x4d, 0x69, + 0x64, 0x64, 0x6c, 0x65, 0x77, 0x61, 0x72, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x28, + 0x01, 0x30, 0x01, 0x12, 0x56, 0x0a, 0x11, 0x53, 0x65, 0x6e, 0x64, 0x43, 0x75, 0x73, 0x74, 0x6f, + 0x6d, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x1f, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x4d, 0x65, 0x73, 0x73, 0x61, - 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x58, 0x0a, 0x17, 0x53, 0x75, - 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x4d, 0x65, 0x73, - 0x73, 0x61, 0x67, 0x65, 0x73, 0x12, 0x25, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x75, - 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x4d, 0x65, 0x73, - 0x73, 0x61, 0x67, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x14, 0x2e, 0x6c, - 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x4d, 0x65, 0x73, 0x73, 0x61, - 0x67, 0x65, 0x30, 0x01, 0x12, 0x44, 0x0a, 0x0b, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x6c, 0x69, 0x61, - 0x73, 0x65, 0x73, 0x12, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, - 0x41, 0x6c, 0x69, 0x61, 0x73, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, - 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x6c, 0x69, 0x61, 0x73, - 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5f, 0x0a, 0x14, 0x4c, 0x6f, - 0x6f, 0x6b, 0x75, 0x70, 0x48, 0x74, 0x6c, 0x63, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, - 0x6f, 0x6e, 0x12, 0x22, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, - 0x70, 0x48, 0x74, 0x6c, 0x63, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4c, + 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x6c, 0x6e, 0x72, 0x70, + 0x63, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x4d, 0x65, 0x73, 0x73, + 0x61, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x58, 0x0a, 0x17, 0x53, + 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x4d, 0x65, + 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x12, 0x25, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, + 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x4d, 0x65, + 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x14, 0x2e, + 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x4d, 0x65, 0x73, 0x73, + 0x61, 0x67, 0x65, 0x30, 0x01, 0x12, 0x44, 0x0a, 0x0b, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x6c, 0x69, + 0x61, 0x73, 0x65, 0x73, 0x12, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, + 0x74, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x6c, 0x69, 0x61, + 0x73, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5f, 0x0a, 0x14, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x48, 0x74, 0x6c, 0x63, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, - 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x27, 0x5a, 0x25, 0x67, - 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x6e, - 0x69, 0x6e, 0x67, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x2f, 0x6c, 0x6e, 0x64, 0x2f, 0x6c, - 0x6e, 0x72, 0x70, 0x63, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x69, 0x6f, 0x6e, 0x12, 0x22, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x6f, 0x6f, 0x6b, + 0x75, 0x70, 0x48, 0x74, 0x6c, 0x63, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, + 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x48, 0x74, 0x6c, 0x63, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, + 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x27, 0x5a, 0x25, + 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6c, 0x69, 0x67, 0x68, 0x74, + 0x6e, 0x69, 0x6e, 0x67, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x2f, 0x6c, 0x6e, 0x64, 0x2f, + 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -21380,7 +21509,7 @@ func file_lightning_proto_rawDescGZIP() []byte { } var file_lightning_proto_enumTypes = make([]protoimpl.EnumInfo, 21) -var file_lightning_proto_msgTypes = make([]protoimpl.MessageInfo, 227) +var file_lightning_proto_msgTypes = make([]protoimpl.MessageInfo, 228) var file_lightning_proto_goTypes = []interface{}{ (OutputScriptType)(0), // 0: lnrpc.OutputScriptType (CoinSelectionStrategy)(0), // 1: lnrpc.CoinSelectionStrategy @@ -21469,167 +21598,168 @@ var file_lightning_proto_goTypes = []interface{}{ (*Chain)(nil), // 84: lnrpc.Chain (*ConfirmationUpdate)(nil), // 85: lnrpc.ConfirmationUpdate (*ChannelOpenUpdate)(nil), // 86: lnrpc.ChannelOpenUpdate - (*ChannelCloseUpdate)(nil), // 87: lnrpc.ChannelCloseUpdate - (*CloseChannelRequest)(nil), // 88: lnrpc.CloseChannelRequest - (*CloseStatusUpdate)(nil), // 89: lnrpc.CloseStatusUpdate - (*PendingUpdate)(nil), // 90: lnrpc.PendingUpdate - (*InstantUpdate)(nil), // 91: lnrpc.InstantUpdate - (*ReadyForPsbtFunding)(nil), // 92: lnrpc.ReadyForPsbtFunding - (*BatchOpenChannelRequest)(nil), // 93: lnrpc.BatchOpenChannelRequest - (*BatchOpenChannel)(nil), // 94: lnrpc.BatchOpenChannel - (*BatchOpenChannelResponse)(nil), // 95: lnrpc.BatchOpenChannelResponse - (*OpenChannelRequest)(nil), // 96: lnrpc.OpenChannelRequest - (*OpenStatusUpdate)(nil), // 97: lnrpc.OpenStatusUpdate - (*KeyLocator)(nil), // 98: lnrpc.KeyLocator - (*KeyDescriptor)(nil), // 99: lnrpc.KeyDescriptor - (*ChanPointShim)(nil), // 100: lnrpc.ChanPointShim - (*PsbtShim)(nil), // 101: lnrpc.PsbtShim - (*FundingShim)(nil), // 102: lnrpc.FundingShim - (*FundingShimCancel)(nil), // 103: lnrpc.FundingShimCancel - (*FundingPsbtVerify)(nil), // 104: lnrpc.FundingPsbtVerify - (*FundingPsbtFinalize)(nil), // 105: lnrpc.FundingPsbtFinalize - (*FundingTransitionMsg)(nil), // 106: lnrpc.FundingTransitionMsg - (*FundingStateStepResp)(nil), // 107: lnrpc.FundingStateStepResp - (*PendingHTLC)(nil), // 108: lnrpc.PendingHTLC - (*PendingChannelsRequest)(nil), // 109: lnrpc.PendingChannelsRequest - (*PendingChannelsResponse)(nil), // 110: lnrpc.PendingChannelsResponse - (*ChannelEventSubscription)(nil), // 111: lnrpc.ChannelEventSubscription - (*ChannelEventUpdate)(nil), // 112: lnrpc.ChannelEventUpdate - (*WalletAccountBalance)(nil), // 113: lnrpc.WalletAccountBalance - (*WalletBalanceRequest)(nil), // 114: lnrpc.WalletBalanceRequest - (*WalletBalanceResponse)(nil), // 115: lnrpc.WalletBalanceResponse - (*Amount)(nil), // 116: lnrpc.Amount - (*ChannelBalanceRequest)(nil), // 117: lnrpc.ChannelBalanceRequest - (*ChannelBalanceResponse)(nil), // 118: lnrpc.ChannelBalanceResponse - (*QueryRoutesRequest)(nil), // 119: lnrpc.QueryRoutesRequest - (*NodePair)(nil), // 120: lnrpc.NodePair - (*EdgeLocator)(nil), // 121: lnrpc.EdgeLocator - (*QueryRoutesResponse)(nil), // 122: lnrpc.QueryRoutesResponse - (*Hop)(nil), // 123: lnrpc.Hop - (*MPPRecord)(nil), // 124: lnrpc.MPPRecord - (*AMPRecord)(nil), // 125: lnrpc.AMPRecord - (*Route)(nil), // 126: lnrpc.Route - (*NodeInfoRequest)(nil), // 127: lnrpc.NodeInfoRequest - (*NodeInfo)(nil), // 128: lnrpc.NodeInfo - (*LightningNode)(nil), // 129: lnrpc.LightningNode - (*NodeAddress)(nil), // 130: lnrpc.NodeAddress - (*RoutingPolicy)(nil), // 131: lnrpc.RoutingPolicy - (*ChannelEdge)(nil), // 132: lnrpc.ChannelEdge - (*ChannelGraphRequest)(nil), // 133: lnrpc.ChannelGraphRequest - (*ChannelGraph)(nil), // 134: lnrpc.ChannelGraph - (*NodeMetricsRequest)(nil), // 135: lnrpc.NodeMetricsRequest - (*NodeMetricsResponse)(nil), // 136: lnrpc.NodeMetricsResponse - (*FloatMetric)(nil), // 137: lnrpc.FloatMetric - (*ChanInfoRequest)(nil), // 138: lnrpc.ChanInfoRequest - (*NetworkInfoRequest)(nil), // 139: lnrpc.NetworkInfoRequest - (*NetworkInfo)(nil), // 140: lnrpc.NetworkInfo - (*StopRequest)(nil), // 141: lnrpc.StopRequest - (*StopResponse)(nil), // 142: lnrpc.StopResponse - (*GraphTopologySubscription)(nil), // 143: lnrpc.GraphTopologySubscription - (*GraphTopologyUpdate)(nil), // 144: lnrpc.GraphTopologyUpdate - (*NodeUpdate)(nil), // 145: lnrpc.NodeUpdate - (*ChannelEdgeUpdate)(nil), // 146: lnrpc.ChannelEdgeUpdate - (*ClosedChannelUpdate)(nil), // 147: lnrpc.ClosedChannelUpdate - (*HopHint)(nil), // 148: lnrpc.HopHint - (*SetID)(nil), // 149: lnrpc.SetID - (*RouteHint)(nil), // 150: lnrpc.RouteHint - (*BlindedPaymentPath)(nil), // 151: lnrpc.BlindedPaymentPath - (*BlindedPath)(nil), // 152: lnrpc.BlindedPath - (*BlindedHop)(nil), // 153: lnrpc.BlindedHop - (*AMPInvoiceState)(nil), // 154: lnrpc.AMPInvoiceState - (*Invoice)(nil), // 155: lnrpc.Invoice - (*BlindedPathConfig)(nil), // 156: lnrpc.BlindedPathConfig - (*InvoiceHTLC)(nil), // 157: lnrpc.InvoiceHTLC - (*AMP)(nil), // 158: lnrpc.AMP - (*AddInvoiceResponse)(nil), // 159: lnrpc.AddInvoiceResponse - (*PaymentHash)(nil), // 160: lnrpc.PaymentHash - (*ListInvoiceRequest)(nil), // 161: lnrpc.ListInvoiceRequest - (*ListInvoiceResponse)(nil), // 162: lnrpc.ListInvoiceResponse - (*InvoiceSubscription)(nil), // 163: lnrpc.InvoiceSubscription - (*Payment)(nil), // 164: lnrpc.Payment - (*HTLCAttempt)(nil), // 165: lnrpc.HTLCAttempt - (*ListPaymentsRequest)(nil), // 166: lnrpc.ListPaymentsRequest - (*ListPaymentsResponse)(nil), // 167: lnrpc.ListPaymentsResponse - (*DeletePaymentRequest)(nil), // 168: lnrpc.DeletePaymentRequest - (*DeleteAllPaymentsRequest)(nil), // 169: lnrpc.DeleteAllPaymentsRequest - (*DeletePaymentResponse)(nil), // 170: lnrpc.DeletePaymentResponse - (*DeleteAllPaymentsResponse)(nil), // 171: lnrpc.DeleteAllPaymentsResponse - (*AbandonChannelRequest)(nil), // 172: lnrpc.AbandonChannelRequest - (*AbandonChannelResponse)(nil), // 173: lnrpc.AbandonChannelResponse - (*DebugLevelRequest)(nil), // 174: lnrpc.DebugLevelRequest - (*DebugLevelResponse)(nil), // 175: lnrpc.DebugLevelResponse - (*PayReqString)(nil), // 176: lnrpc.PayReqString - (*PayReq)(nil), // 177: lnrpc.PayReq - (*Feature)(nil), // 178: lnrpc.Feature - (*FeeReportRequest)(nil), // 179: lnrpc.FeeReportRequest - (*ChannelFeeReport)(nil), // 180: lnrpc.ChannelFeeReport - (*FeeReportResponse)(nil), // 181: lnrpc.FeeReportResponse - (*InboundFee)(nil), // 182: lnrpc.InboundFee - (*PolicyUpdateRequest)(nil), // 183: lnrpc.PolicyUpdateRequest - (*FailedUpdate)(nil), // 184: lnrpc.FailedUpdate - (*PolicyUpdateResponse)(nil), // 185: lnrpc.PolicyUpdateResponse - (*ForwardingHistoryRequest)(nil), // 186: lnrpc.ForwardingHistoryRequest - (*ForwardingEvent)(nil), // 187: lnrpc.ForwardingEvent - (*ForwardingHistoryResponse)(nil), // 188: lnrpc.ForwardingHistoryResponse - (*ExportChannelBackupRequest)(nil), // 189: lnrpc.ExportChannelBackupRequest - (*ChannelBackup)(nil), // 190: lnrpc.ChannelBackup - (*MultiChanBackup)(nil), // 191: lnrpc.MultiChanBackup - (*ChanBackupExportRequest)(nil), // 192: lnrpc.ChanBackupExportRequest - (*ChanBackupSnapshot)(nil), // 193: lnrpc.ChanBackupSnapshot - (*ChannelBackups)(nil), // 194: lnrpc.ChannelBackups - (*RestoreChanBackupRequest)(nil), // 195: lnrpc.RestoreChanBackupRequest - (*RestoreBackupResponse)(nil), // 196: lnrpc.RestoreBackupResponse - (*ChannelBackupSubscription)(nil), // 197: lnrpc.ChannelBackupSubscription - (*VerifyChanBackupResponse)(nil), // 198: lnrpc.VerifyChanBackupResponse - (*MacaroonPermission)(nil), // 199: lnrpc.MacaroonPermission - (*BakeMacaroonRequest)(nil), // 200: lnrpc.BakeMacaroonRequest - (*BakeMacaroonResponse)(nil), // 201: lnrpc.BakeMacaroonResponse - (*ListMacaroonIDsRequest)(nil), // 202: lnrpc.ListMacaroonIDsRequest - (*ListMacaroonIDsResponse)(nil), // 203: lnrpc.ListMacaroonIDsResponse - (*DeleteMacaroonIDRequest)(nil), // 204: lnrpc.DeleteMacaroonIDRequest - (*DeleteMacaroonIDResponse)(nil), // 205: lnrpc.DeleteMacaroonIDResponse - (*MacaroonPermissionList)(nil), // 206: lnrpc.MacaroonPermissionList - (*ListPermissionsRequest)(nil), // 207: lnrpc.ListPermissionsRequest - (*ListPermissionsResponse)(nil), // 208: lnrpc.ListPermissionsResponse - (*Failure)(nil), // 209: lnrpc.Failure - (*ChannelUpdate)(nil), // 210: lnrpc.ChannelUpdate - (*MacaroonId)(nil), // 211: lnrpc.MacaroonId - (*Op)(nil), // 212: lnrpc.Op - (*CheckMacPermRequest)(nil), // 213: lnrpc.CheckMacPermRequest - (*CheckMacPermResponse)(nil), // 214: lnrpc.CheckMacPermResponse - (*RPCMiddlewareRequest)(nil), // 215: lnrpc.RPCMiddlewareRequest - (*StreamAuth)(nil), // 216: lnrpc.StreamAuth - (*RPCMessage)(nil), // 217: lnrpc.RPCMessage - (*RPCMiddlewareResponse)(nil), // 218: lnrpc.RPCMiddlewareResponse - (*MiddlewareRegistration)(nil), // 219: lnrpc.MiddlewareRegistration - (*InterceptFeedback)(nil), // 220: lnrpc.InterceptFeedback - nil, // 221: lnrpc.SendRequest.DestCustomRecordsEntry - nil, // 222: lnrpc.EstimateFeeRequest.AddrToAmountEntry - nil, // 223: lnrpc.SendManyRequest.AddrToAmountEntry - nil, // 224: lnrpc.Peer.FeaturesEntry - nil, // 225: lnrpc.GetInfoResponse.FeaturesEntry - nil, // 226: lnrpc.GetDebugInfoResponse.ConfigEntry - (*PendingChannelsResponse_PendingChannel)(nil), // 227: lnrpc.PendingChannelsResponse.PendingChannel - (*PendingChannelsResponse_PendingOpenChannel)(nil), // 228: lnrpc.PendingChannelsResponse.PendingOpenChannel - (*PendingChannelsResponse_WaitingCloseChannel)(nil), // 229: lnrpc.PendingChannelsResponse.WaitingCloseChannel - (*PendingChannelsResponse_Commitments)(nil), // 230: lnrpc.PendingChannelsResponse.Commitments - (*PendingChannelsResponse_ClosedChannel)(nil), // 231: lnrpc.PendingChannelsResponse.ClosedChannel - (*PendingChannelsResponse_ForceClosedChannel)(nil), // 232: lnrpc.PendingChannelsResponse.ForceClosedChannel - nil, // 233: lnrpc.WalletBalanceResponse.AccountBalanceEntry - nil, // 234: lnrpc.QueryRoutesRequest.DestCustomRecordsEntry - nil, // 235: lnrpc.Hop.CustomRecordsEntry - nil, // 236: lnrpc.LightningNode.FeaturesEntry - nil, // 237: lnrpc.LightningNode.CustomRecordsEntry - nil, // 238: lnrpc.RoutingPolicy.CustomRecordsEntry - nil, // 239: lnrpc.ChannelEdge.CustomRecordsEntry - nil, // 240: lnrpc.NodeMetricsResponse.BetweennessCentralityEntry - nil, // 241: lnrpc.NodeUpdate.FeaturesEntry - nil, // 242: lnrpc.Invoice.FeaturesEntry - nil, // 243: lnrpc.Invoice.AmpInvoiceStateEntry - nil, // 244: lnrpc.InvoiceHTLC.CustomRecordsEntry - nil, // 245: lnrpc.Payment.FirstHopCustomRecordsEntry - nil, // 246: lnrpc.PayReq.FeaturesEntry - nil, // 247: lnrpc.ListPermissionsResponse.MethodPermissionsEntry + (*CloseOutput)(nil), // 87: lnrpc.CloseOutput + (*ChannelCloseUpdate)(nil), // 88: lnrpc.ChannelCloseUpdate + (*CloseChannelRequest)(nil), // 89: lnrpc.CloseChannelRequest + (*CloseStatusUpdate)(nil), // 90: lnrpc.CloseStatusUpdate + (*PendingUpdate)(nil), // 91: lnrpc.PendingUpdate + (*InstantUpdate)(nil), // 92: lnrpc.InstantUpdate + (*ReadyForPsbtFunding)(nil), // 93: lnrpc.ReadyForPsbtFunding + (*BatchOpenChannelRequest)(nil), // 94: lnrpc.BatchOpenChannelRequest + (*BatchOpenChannel)(nil), // 95: lnrpc.BatchOpenChannel + (*BatchOpenChannelResponse)(nil), // 96: lnrpc.BatchOpenChannelResponse + (*OpenChannelRequest)(nil), // 97: lnrpc.OpenChannelRequest + (*OpenStatusUpdate)(nil), // 98: lnrpc.OpenStatusUpdate + (*KeyLocator)(nil), // 99: lnrpc.KeyLocator + (*KeyDescriptor)(nil), // 100: lnrpc.KeyDescriptor + (*ChanPointShim)(nil), // 101: lnrpc.ChanPointShim + (*PsbtShim)(nil), // 102: lnrpc.PsbtShim + (*FundingShim)(nil), // 103: lnrpc.FundingShim + (*FundingShimCancel)(nil), // 104: lnrpc.FundingShimCancel + (*FundingPsbtVerify)(nil), // 105: lnrpc.FundingPsbtVerify + (*FundingPsbtFinalize)(nil), // 106: lnrpc.FundingPsbtFinalize + (*FundingTransitionMsg)(nil), // 107: lnrpc.FundingTransitionMsg + (*FundingStateStepResp)(nil), // 108: lnrpc.FundingStateStepResp + (*PendingHTLC)(nil), // 109: lnrpc.PendingHTLC + (*PendingChannelsRequest)(nil), // 110: lnrpc.PendingChannelsRequest + (*PendingChannelsResponse)(nil), // 111: lnrpc.PendingChannelsResponse + (*ChannelEventSubscription)(nil), // 112: lnrpc.ChannelEventSubscription + (*ChannelEventUpdate)(nil), // 113: lnrpc.ChannelEventUpdate + (*WalletAccountBalance)(nil), // 114: lnrpc.WalletAccountBalance + (*WalletBalanceRequest)(nil), // 115: lnrpc.WalletBalanceRequest + (*WalletBalanceResponse)(nil), // 116: lnrpc.WalletBalanceResponse + (*Amount)(nil), // 117: lnrpc.Amount + (*ChannelBalanceRequest)(nil), // 118: lnrpc.ChannelBalanceRequest + (*ChannelBalanceResponse)(nil), // 119: lnrpc.ChannelBalanceResponse + (*QueryRoutesRequest)(nil), // 120: lnrpc.QueryRoutesRequest + (*NodePair)(nil), // 121: lnrpc.NodePair + (*EdgeLocator)(nil), // 122: lnrpc.EdgeLocator + (*QueryRoutesResponse)(nil), // 123: lnrpc.QueryRoutesResponse + (*Hop)(nil), // 124: lnrpc.Hop + (*MPPRecord)(nil), // 125: lnrpc.MPPRecord + (*AMPRecord)(nil), // 126: lnrpc.AMPRecord + (*Route)(nil), // 127: lnrpc.Route + (*NodeInfoRequest)(nil), // 128: lnrpc.NodeInfoRequest + (*NodeInfo)(nil), // 129: lnrpc.NodeInfo + (*LightningNode)(nil), // 130: lnrpc.LightningNode + (*NodeAddress)(nil), // 131: lnrpc.NodeAddress + (*RoutingPolicy)(nil), // 132: lnrpc.RoutingPolicy + (*ChannelEdge)(nil), // 133: lnrpc.ChannelEdge + (*ChannelGraphRequest)(nil), // 134: lnrpc.ChannelGraphRequest + (*ChannelGraph)(nil), // 135: lnrpc.ChannelGraph + (*NodeMetricsRequest)(nil), // 136: lnrpc.NodeMetricsRequest + (*NodeMetricsResponse)(nil), // 137: lnrpc.NodeMetricsResponse + (*FloatMetric)(nil), // 138: lnrpc.FloatMetric + (*ChanInfoRequest)(nil), // 139: lnrpc.ChanInfoRequest + (*NetworkInfoRequest)(nil), // 140: lnrpc.NetworkInfoRequest + (*NetworkInfo)(nil), // 141: lnrpc.NetworkInfo + (*StopRequest)(nil), // 142: lnrpc.StopRequest + (*StopResponse)(nil), // 143: lnrpc.StopResponse + (*GraphTopologySubscription)(nil), // 144: lnrpc.GraphTopologySubscription + (*GraphTopologyUpdate)(nil), // 145: lnrpc.GraphTopologyUpdate + (*NodeUpdate)(nil), // 146: lnrpc.NodeUpdate + (*ChannelEdgeUpdate)(nil), // 147: lnrpc.ChannelEdgeUpdate + (*ClosedChannelUpdate)(nil), // 148: lnrpc.ClosedChannelUpdate + (*HopHint)(nil), // 149: lnrpc.HopHint + (*SetID)(nil), // 150: lnrpc.SetID + (*RouteHint)(nil), // 151: lnrpc.RouteHint + (*BlindedPaymentPath)(nil), // 152: lnrpc.BlindedPaymentPath + (*BlindedPath)(nil), // 153: lnrpc.BlindedPath + (*BlindedHop)(nil), // 154: lnrpc.BlindedHop + (*AMPInvoiceState)(nil), // 155: lnrpc.AMPInvoiceState + (*Invoice)(nil), // 156: lnrpc.Invoice + (*BlindedPathConfig)(nil), // 157: lnrpc.BlindedPathConfig + (*InvoiceHTLC)(nil), // 158: lnrpc.InvoiceHTLC + (*AMP)(nil), // 159: lnrpc.AMP + (*AddInvoiceResponse)(nil), // 160: lnrpc.AddInvoiceResponse + (*PaymentHash)(nil), // 161: lnrpc.PaymentHash + (*ListInvoiceRequest)(nil), // 162: lnrpc.ListInvoiceRequest + (*ListInvoiceResponse)(nil), // 163: lnrpc.ListInvoiceResponse + (*InvoiceSubscription)(nil), // 164: lnrpc.InvoiceSubscription + (*Payment)(nil), // 165: lnrpc.Payment + (*HTLCAttempt)(nil), // 166: lnrpc.HTLCAttempt + (*ListPaymentsRequest)(nil), // 167: lnrpc.ListPaymentsRequest + (*ListPaymentsResponse)(nil), // 168: lnrpc.ListPaymentsResponse + (*DeletePaymentRequest)(nil), // 169: lnrpc.DeletePaymentRequest + (*DeleteAllPaymentsRequest)(nil), // 170: lnrpc.DeleteAllPaymentsRequest + (*DeletePaymentResponse)(nil), // 171: lnrpc.DeletePaymentResponse + (*DeleteAllPaymentsResponse)(nil), // 172: lnrpc.DeleteAllPaymentsResponse + (*AbandonChannelRequest)(nil), // 173: lnrpc.AbandonChannelRequest + (*AbandonChannelResponse)(nil), // 174: lnrpc.AbandonChannelResponse + (*DebugLevelRequest)(nil), // 175: lnrpc.DebugLevelRequest + (*DebugLevelResponse)(nil), // 176: lnrpc.DebugLevelResponse + (*PayReqString)(nil), // 177: lnrpc.PayReqString + (*PayReq)(nil), // 178: lnrpc.PayReq + (*Feature)(nil), // 179: lnrpc.Feature + (*FeeReportRequest)(nil), // 180: lnrpc.FeeReportRequest + (*ChannelFeeReport)(nil), // 181: lnrpc.ChannelFeeReport + (*FeeReportResponse)(nil), // 182: lnrpc.FeeReportResponse + (*InboundFee)(nil), // 183: lnrpc.InboundFee + (*PolicyUpdateRequest)(nil), // 184: lnrpc.PolicyUpdateRequest + (*FailedUpdate)(nil), // 185: lnrpc.FailedUpdate + (*PolicyUpdateResponse)(nil), // 186: lnrpc.PolicyUpdateResponse + (*ForwardingHistoryRequest)(nil), // 187: lnrpc.ForwardingHistoryRequest + (*ForwardingEvent)(nil), // 188: lnrpc.ForwardingEvent + (*ForwardingHistoryResponse)(nil), // 189: lnrpc.ForwardingHistoryResponse + (*ExportChannelBackupRequest)(nil), // 190: lnrpc.ExportChannelBackupRequest + (*ChannelBackup)(nil), // 191: lnrpc.ChannelBackup + (*MultiChanBackup)(nil), // 192: lnrpc.MultiChanBackup + (*ChanBackupExportRequest)(nil), // 193: lnrpc.ChanBackupExportRequest + (*ChanBackupSnapshot)(nil), // 194: lnrpc.ChanBackupSnapshot + (*ChannelBackups)(nil), // 195: lnrpc.ChannelBackups + (*RestoreChanBackupRequest)(nil), // 196: lnrpc.RestoreChanBackupRequest + (*RestoreBackupResponse)(nil), // 197: lnrpc.RestoreBackupResponse + (*ChannelBackupSubscription)(nil), // 198: lnrpc.ChannelBackupSubscription + (*VerifyChanBackupResponse)(nil), // 199: lnrpc.VerifyChanBackupResponse + (*MacaroonPermission)(nil), // 200: lnrpc.MacaroonPermission + (*BakeMacaroonRequest)(nil), // 201: lnrpc.BakeMacaroonRequest + (*BakeMacaroonResponse)(nil), // 202: lnrpc.BakeMacaroonResponse + (*ListMacaroonIDsRequest)(nil), // 203: lnrpc.ListMacaroonIDsRequest + (*ListMacaroonIDsResponse)(nil), // 204: lnrpc.ListMacaroonIDsResponse + (*DeleteMacaroonIDRequest)(nil), // 205: lnrpc.DeleteMacaroonIDRequest + (*DeleteMacaroonIDResponse)(nil), // 206: lnrpc.DeleteMacaroonIDResponse + (*MacaroonPermissionList)(nil), // 207: lnrpc.MacaroonPermissionList + (*ListPermissionsRequest)(nil), // 208: lnrpc.ListPermissionsRequest + (*ListPermissionsResponse)(nil), // 209: lnrpc.ListPermissionsResponse + (*Failure)(nil), // 210: lnrpc.Failure + (*ChannelUpdate)(nil), // 211: lnrpc.ChannelUpdate + (*MacaroonId)(nil), // 212: lnrpc.MacaroonId + (*Op)(nil), // 213: lnrpc.Op + (*CheckMacPermRequest)(nil), // 214: lnrpc.CheckMacPermRequest + (*CheckMacPermResponse)(nil), // 215: lnrpc.CheckMacPermResponse + (*RPCMiddlewareRequest)(nil), // 216: lnrpc.RPCMiddlewareRequest + (*StreamAuth)(nil), // 217: lnrpc.StreamAuth + (*RPCMessage)(nil), // 218: lnrpc.RPCMessage + (*RPCMiddlewareResponse)(nil), // 219: lnrpc.RPCMiddlewareResponse + (*MiddlewareRegistration)(nil), // 220: lnrpc.MiddlewareRegistration + (*InterceptFeedback)(nil), // 221: lnrpc.InterceptFeedback + nil, // 222: lnrpc.SendRequest.DestCustomRecordsEntry + nil, // 223: lnrpc.EstimateFeeRequest.AddrToAmountEntry + nil, // 224: lnrpc.SendManyRequest.AddrToAmountEntry + nil, // 225: lnrpc.Peer.FeaturesEntry + nil, // 226: lnrpc.GetInfoResponse.FeaturesEntry + nil, // 227: lnrpc.GetDebugInfoResponse.ConfigEntry + (*PendingChannelsResponse_PendingChannel)(nil), // 228: lnrpc.PendingChannelsResponse.PendingChannel + (*PendingChannelsResponse_PendingOpenChannel)(nil), // 229: lnrpc.PendingChannelsResponse.PendingOpenChannel + (*PendingChannelsResponse_WaitingCloseChannel)(nil), // 230: lnrpc.PendingChannelsResponse.WaitingCloseChannel + (*PendingChannelsResponse_Commitments)(nil), // 231: lnrpc.PendingChannelsResponse.Commitments + (*PendingChannelsResponse_ClosedChannel)(nil), // 232: lnrpc.PendingChannelsResponse.ClosedChannel + (*PendingChannelsResponse_ForceClosedChannel)(nil), // 233: lnrpc.PendingChannelsResponse.ForceClosedChannel + nil, // 234: lnrpc.WalletBalanceResponse.AccountBalanceEntry + nil, // 235: lnrpc.QueryRoutesRequest.DestCustomRecordsEntry + nil, // 236: lnrpc.Hop.CustomRecordsEntry + nil, // 237: lnrpc.LightningNode.FeaturesEntry + nil, // 238: lnrpc.LightningNode.CustomRecordsEntry + nil, // 239: lnrpc.RoutingPolicy.CustomRecordsEntry + nil, // 240: lnrpc.ChannelEdge.CustomRecordsEntry + nil, // 241: lnrpc.NodeMetricsResponse.BetweennessCentralityEntry + nil, // 242: lnrpc.NodeUpdate.FeaturesEntry + nil, // 243: lnrpc.Invoice.FeaturesEntry + nil, // 244: lnrpc.Invoice.AmpInvoiceStateEntry + nil, // 245: lnrpc.InvoiceHTLC.CustomRecordsEntry + nil, // 246: lnrpc.Payment.FirstHopCustomRecordsEntry + nil, // 247: lnrpc.PayReq.FeaturesEntry + nil, // 248: lnrpc.ListPermissionsResponse.MethodPermissionsEntry } var file_lightning_proto_depIdxs = []int32{ 2, // 0: lnrpc.Utxo.address_type:type_name -> lnrpc.AddressType @@ -21639,14 +21769,14 @@ var file_lightning_proto_depIdxs = []int32{ 40, // 4: lnrpc.Transaction.previous_outpoints:type_name -> lnrpc.PreviousOutPoint 29, // 5: lnrpc.TransactionDetails.transactions:type_name -> lnrpc.Transaction 32, // 6: lnrpc.SendRequest.fee_limit:type_name -> lnrpc.FeeLimit - 221, // 7: lnrpc.SendRequest.dest_custom_records:type_name -> lnrpc.SendRequest.DestCustomRecordsEntry + 222, // 7: lnrpc.SendRequest.dest_custom_records:type_name -> lnrpc.SendRequest.DestCustomRecordsEntry 10, // 8: lnrpc.SendRequest.dest_features:type_name -> lnrpc.FeatureBit - 126, // 9: lnrpc.SendResponse.payment_route:type_name -> lnrpc.Route - 126, // 10: lnrpc.SendToRouteRequest.route:type_name -> lnrpc.Route + 127, // 9: lnrpc.SendResponse.payment_route:type_name -> lnrpc.Route + 127, // 10: lnrpc.SendToRouteRequest.route:type_name -> lnrpc.Route 3, // 11: lnrpc.ChannelAcceptRequest.commitment_type:type_name -> lnrpc.CommitmentType - 222, // 12: lnrpc.EstimateFeeRequest.AddrToAmount:type_name -> lnrpc.EstimateFeeRequest.AddrToAmountEntry + 223, // 12: lnrpc.EstimateFeeRequest.AddrToAmount:type_name -> lnrpc.EstimateFeeRequest.AddrToAmountEntry 1, // 13: lnrpc.EstimateFeeRequest.coin_selection_strategy:type_name -> lnrpc.CoinSelectionStrategy - 223, // 14: lnrpc.SendManyRequest.AddrToAmount:type_name -> lnrpc.SendManyRequest.AddrToAmountEntry + 224, // 14: lnrpc.SendManyRequest.AddrToAmount:type_name -> lnrpc.SendManyRequest.AddrToAmountEntry 1, // 15: lnrpc.SendManyRequest.coin_selection_strategy:type_name -> lnrpc.CoinSelectionStrategy 1, // 16: lnrpc.SendCoinsRequest.coin_selection_strategy:type_name -> lnrpc.CoinSelectionStrategy 39, // 17: lnrpc.SendCoinsRequest.outpoints:type_name -> lnrpc.OutPoint @@ -21668,301 +21798,304 @@ var file_lightning_proto_depIdxs = []int32{ 39, // 33: lnrpc.Resolution.outpoint:type_name -> lnrpc.OutPoint 68, // 34: lnrpc.ClosedChannelsResponse.channels:type_name -> lnrpc.ChannelCloseSummary 13, // 35: lnrpc.Peer.sync_type:type_name -> lnrpc.Peer.SyncType - 224, // 36: lnrpc.Peer.features:type_name -> lnrpc.Peer.FeaturesEntry + 225, // 36: lnrpc.Peer.features:type_name -> lnrpc.Peer.FeaturesEntry 73, // 37: lnrpc.Peer.errors:type_name -> lnrpc.TimestampedError 72, // 38: lnrpc.ListPeersResponse.peers:type_name -> lnrpc.Peer 14, // 39: lnrpc.PeerEvent.type:type_name -> lnrpc.PeerEvent.EventType 84, // 40: lnrpc.GetInfoResponse.chains:type_name -> lnrpc.Chain - 225, // 41: lnrpc.GetInfoResponse.features:type_name -> lnrpc.GetInfoResponse.FeaturesEntry - 226, // 42: lnrpc.GetDebugInfoResponse.config:type_name -> lnrpc.GetDebugInfoResponse.ConfigEntry + 226, // 41: lnrpc.GetInfoResponse.features:type_name -> lnrpc.GetInfoResponse.FeaturesEntry + 227, // 42: lnrpc.GetDebugInfoResponse.config:type_name -> lnrpc.GetDebugInfoResponse.ConfigEntry 38, // 43: lnrpc.ChannelOpenUpdate.channel_point:type_name -> lnrpc.ChannelPoint - 38, // 44: lnrpc.CloseChannelRequest.channel_point:type_name -> lnrpc.ChannelPoint - 90, // 45: lnrpc.CloseStatusUpdate.close_pending:type_name -> lnrpc.PendingUpdate - 87, // 46: lnrpc.CloseStatusUpdate.chan_close:type_name -> lnrpc.ChannelCloseUpdate - 91, // 47: lnrpc.CloseStatusUpdate.close_instant:type_name -> lnrpc.InstantUpdate - 94, // 48: lnrpc.BatchOpenChannelRequest.channels:type_name -> lnrpc.BatchOpenChannel - 1, // 49: lnrpc.BatchOpenChannelRequest.coin_selection_strategy:type_name -> lnrpc.CoinSelectionStrategy - 3, // 50: lnrpc.BatchOpenChannel.commitment_type:type_name -> lnrpc.CommitmentType - 90, // 51: lnrpc.BatchOpenChannelResponse.pending_channels:type_name -> lnrpc.PendingUpdate - 102, // 52: lnrpc.OpenChannelRequest.funding_shim:type_name -> lnrpc.FundingShim - 3, // 53: lnrpc.OpenChannelRequest.commitment_type:type_name -> lnrpc.CommitmentType - 39, // 54: lnrpc.OpenChannelRequest.outpoints:type_name -> lnrpc.OutPoint - 90, // 55: lnrpc.OpenStatusUpdate.chan_pending:type_name -> lnrpc.PendingUpdate - 86, // 56: lnrpc.OpenStatusUpdate.chan_open:type_name -> lnrpc.ChannelOpenUpdate - 92, // 57: lnrpc.OpenStatusUpdate.psbt_fund:type_name -> lnrpc.ReadyForPsbtFunding - 98, // 58: lnrpc.KeyDescriptor.key_loc:type_name -> lnrpc.KeyLocator - 38, // 59: lnrpc.ChanPointShim.chan_point:type_name -> lnrpc.ChannelPoint - 99, // 60: lnrpc.ChanPointShim.local_key:type_name -> lnrpc.KeyDescriptor - 100, // 61: lnrpc.FundingShim.chan_point_shim:type_name -> lnrpc.ChanPointShim - 101, // 62: lnrpc.FundingShim.psbt_shim:type_name -> lnrpc.PsbtShim - 102, // 63: lnrpc.FundingTransitionMsg.shim_register:type_name -> lnrpc.FundingShim - 103, // 64: lnrpc.FundingTransitionMsg.shim_cancel:type_name -> lnrpc.FundingShimCancel - 104, // 65: lnrpc.FundingTransitionMsg.psbt_verify:type_name -> lnrpc.FundingPsbtVerify - 105, // 66: lnrpc.FundingTransitionMsg.psbt_finalize:type_name -> lnrpc.FundingPsbtFinalize - 228, // 67: lnrpc.PendingChannelsResponse.pending_open_channels:type_name -> lnrpc.PendingChannelsResponse.PendingOpenChannel - 231, // 68: lnrpc.PendingChannelsResponse.pending_closing_channels:type_name -> lnrpc.PendingChannelsResponse.ClosedChannel - 232, // 69: lnrpc.PendingChannelsResponse.pending_force_closing_channels:type_name -> lnrpc.PendingChannelsResponse.ForceClosedChannel - 229, // 70: lnrpc.PendingChannelsResponse.waiting_close_channels:type_name -> lnrpc.PendingChannelsResponse.WaitingCloseChannel - 62, // 71: lnrpc.ChannelEventUpdate.open_channel:type_name -> lnrpc.Channel - 68, // 72: lnrpc.ChannelEventUpdate.closed_channel:type_name -> lnrpc.ChannelCloseSummary - 38, // 73: lnrpc.ChannelEventUpdate.active_channel:type_name -> lnrpc.ChannelPoint - 38, // 74: lnrpc.ChannelEventUpdate.inactive_channel:type_name -> lnrpc.ChannelPoint - 90, // 75: lnrpc.ChannelEventUpdate.pending_open_channel:type_name -> lnrpc.PendingUpdate - 38, // 76: lnrpc.ChannelEventUpdate.fully_resolved_channel:type_name -> lnrpc.ChannelPoint - 16, // 77: lnrpc.ChannelEventUpdate.type:type_name -> lnrpc.ChannelEventUpdate.UpdateType - 233, // 78: lnrpc.WalletBalanceResponse.account_balance:type_name -> lnrpc.WalletBalanceResponse.AccountBalanceEntry - 116, // 79: lnrpc.ChannelBalanceResponse.local_balance:type_name -> lnrpc.Amount - 116, // 80: lnrpc.ChannelBalanceResponse.remote_balance:type_name -> lnrpc.Amount - 116, // 81: lnrpc.ChannelBalanceResponse.unsettled_local_balance:type_name -> lnrpc.Amount - 116, // 82: lnrpc.ChannelBalanceResponse.unsettled_remote_balance:type_name -> lnrpc.Amount - 116, // 83: lnrpc.ChannelBalanceResponse.pending_open_local_balance:type_name -> lnrpc.Amount - 116, // 84: lnrpc.ChannelBalanceResponse.pending_open_remote_balance:type_name -> lnrpc.Amount - 32, // 85: lnrpc.QueryRoutesRequest.fee_limit:type_name -> lnrpc.FeeLimit - 121, // 86: lnrpc.QueryRoutesRequest.ignored_edges:type_name -> lnrpc.EdgeLocator - 120, // 87: lnrpc.QueryRoutesRequest.ignored_pairs:type_name -> lnrpc.NodePair - 234, // 88: lnrpc.QueryRoutesRequest.dest_custom_records:type_name -> lnrpc.QueryRoutesRequest.DestCustomRecordsEntry - 150, // 89: lnrpc.QueryRoutesRequest.route_hints:type_name -> lnrpc.RouteHint - 151, // 90: lnrpc.QueryRoutesRequest.blinded_payment_paths:type_name -> lnrpc.BlindedPaymentPath - 10, // 91: lnrpc.QueryRoutesRequest.dest_features:type_name -> lnrpc.FeatureBit - 126, // 92: lnrpc.QueryRoutesResponse.routes:type_name -> lnrpc.Route - 124, // 93: lnrpc.Hop.mpp_record:type_name -> lnrpc.MPPRecord - 125, // 94: lnrpc.Hop.amp_record:type_name -> lnrpc.AMPRecord - 235, // 95: lnrpc.Hop.custom_records:type_name -> lnrpc.Hop.CustomRecordsEntry - 123, // 96: lnrpc.Route.hops:type_name -> lnrpc.Hop - 129, // 97: lnrpc.NodeInfo.node:type_name -> lnrpc.LightningNode - 132, // 98: lnrpc.NodeInfo.channels:type_name -> lnrpc.ChannelEdge - 130, // 99: lnrpc.LightningNode.addresses:type_name -> lnrpc.NodeAddress - 236, // 100: lnrpc.LightningNode.features:type_name -> lnrpc.LightningNode.FeaturesEntry - 237, // 101: lnrpc.LightningNode.custom_records:type_name -> lnrpc.LightningNode.CustomRecordsEntry - 238, // 102: lnrpc.RoutingPolicy.custom_records:type_name -> lnrpc.RoutingPolicy.CustomRecordsEntry - 131, // 103: lnrpc.ChannelEdge.node1_policy:type_name -> lnrpc.RoutingPolicy - 131, // 104: lnrpc.ChannelEdge.node2_policy:type_name -> lnrpc.RoutingPolicy - 239, // 105: lnrpc.ChannelEdge.custom_records:type_name -> lnrpc.ChannelEdge.CustomRecordsEntry - 129, // 106: lnrpc.ChannelGraph.nodes:type_name -> lnrpc.LightningNode - 132, // 107: lnrpc.ChannelGraph.edges:type_name -> lnrpc.ChannelEdge - 7, // 108: lnrpc.NodeMetricsRequest.types:type_name -> lnrpc.NodeMetricType - 240, // 109: lnrpc.NodeMetricsResponse.betweenness_centrality:type_name -> lnrpc.NodeMetricsResponse.BetweennessCentralityEntry - 145, // 110: lnrpc.GraphTopologyUpdate.node_updates:type_name -> lnrpc.NodeUpdate - 146, // 111: lnrpc.GraphTopologyUpdate.channel_updates:type_name -> lnrpc.ChannelEdgeUpdate - 147, // 112: lnrpc.GraphTopologyUpdate.closed_chans:type_name -> lnrpc.ClosedChannelUpdate - 130, // 113: lnrpc.NodeUpdate.node_addresses:type_name -> lnrpc.NodeAddress - 241, // 114: lnrpc.NodeUpdate.features:type_name -> lnrpc.NodeUpdate.FeaturesEntry - 38, // 115: lnrpc.ChannelEdgeUpdate.chan_point:type_name -> lnrpc.ChannelPoint - 131, // 116: lnrpc.ChannelEdgeUpdate.routing_policy:type_name -> lnrpc.RoutingPolicy - 38, // 117: lnrpc.ClosedChannelUpdate.chan_point:type_name -> lnrpc.ChannelPoint - 148, // 118: lnrpc.RouteHint.hop_hints:type_name -> lnrpc.HopHint - 152, // 119: lnrpc.BlindedPaymentPath.blinded_path:type_name -> lnrpc.BlindedPath - 10, // 120: lnrpc.BlindedPaymentPath.features:type_name -> lnrpc.FeatureBit - 153, // 121: lnrpc.BlindedPath.blinded_hops:type_name -> lnrpc.BlindedHop - 8, // 122: lnrpc.AMPInvoiceState.state:type_name -> lnrpc.InvoiceHTLCState - 150, // 123: lnrpc.Invoice.route_hints:type_name -> lnrpc.RouteHint - 17, // 124: lnrpc.Invoice.state:type_name -> lnrpc.Invoice.InvoiceState - 157, // 125: lnrpc.Invoice.htlcs:type_name -> lnrpc.InvoiceHTLC - 242, // 126: lnrpc.Invoice.features:type_name -> lnrpc.Invoice.FeaturesEntry - 243, // 127: lnrpc.Invoice.amp_invoice_state:type_name -> lnrpc.Invoice.AmpInvoiceStateEntry - 156, // 128: lnrpc.Invoice.blinded_path_config:type_name -> lnrpc.BlindedPathConfig - 8, // 129: lnrpc.InvoiceHTLC.state:type_name -> lnrpc.InvoiceHTLCState - 244, // 130: lnrpc.InvoiceHTLC.custom_records:type_name -> lnrpc.InvoiceHTLC.CustomRecordsEntry - 158, // 131: lnrpc.InvoiceHTLC.amp:type_name -> lnrpc.AMP - 155, // 132: lnrpc.ListInvoiceResponse.invoices:type_name -> lnrpc.Invoice - 18, // 133: lnrpc.Payment.status:type_name -> lnrpc.Payment.PaymentStatus - 165, // 134: lnrpc.Payment.htlcs:type_name -> lnrpc.HTLCAttempt - 9, // 135: lnrpc.Payment.failure_reason:type_name -> lnrpc.PaymentFailureReason - 245, // 136: lnrpc.Payment.first_hop_custom_records:type_name -> lnrpc.Payment.FirstHopCustomRecordsEntry - 19, // 137: lnrpc.HTLCAttempt.status:type_name -> lnrpc.HTLCAttempt.HTLCStatus - 126, // 138: lnrpc.HTLCAttempt.route:type_name -> lnrpc.Route - 209, // 139: lnrpc.HTLCAttempt.failure:type_name -> lnrpc.Failure - 164, // 140: lnrpc.ListPaymentsResponse.payments:type_name -> lnrpc.Payment - 38, // 141: lnrpc.AbandonChannelRequest.channel_point:type_name -> lnrpc.ChannelPoint - 150, // 142: lnrpc.PayReq.route_hints:type_name -> lnrpc.RouteHint - 246, // 143: lnrpc.PayReq.features:type_name -> lnrpc.PayReq.FeaturesEntry - 151, // 144: lnrpc.PayReq.blinded_paths:type_name -> lnrpc.BlindedPaymentPath - 180, // 145: lnrpc.FeeReportResponse.channel_fees:type_name -> lnrpc.ChannelFeeReport - 38, // 146: lnrpc.PolicyUpdateRequest.chan_point:type_name -> lnrpc.ChannelPoint - 182, // 147: lnrpc.PolicyUpdateRequest.inbound_fee:type_name -> lnrpc.InboundFee - 39, // 148: lnrpc.FailedUpdate.outpoint:type_name -> lnrpc.OutPoint - 11, // 149: lnrpc.FailedUpdate.reason:type_name -> lnrpc.UpdateFailure - 184, // 150: lnrpc.PolicyUpdateResponse.failed_updates:type_name -> lnrpc.FailedUpdate - 187, // 151: lnrpc.ForwardingHistoryResponse.forwarding_events:type_name -> lnrpc.ForwardingEvent - 38, // 152: lnrpc.ExportChannelBackupRequest.chan_point:type_name -> lnrpc.ChannelPoint - 38, // 153: lnrpc.ChannelBackup.chan_point:type_name -> lnrpc.ChannelPoint - 38, // 154: lnrpc.MultiChanBackup.chan_points:type_name -> lnrpc.ChannelPoint - 194, // 155: lnrpc.ChanBackupSnapshot.single_chan_backups:type_name -> lnrpc.ChannelBackups - 191, // 156: lnrpc.ChanBackupSnapshot.multi_chan_backup:type_name -> lnrpc.MultiChanBackup - 190, // 157: lnrpc.ChannelBackups.chan_backups:type_name -> lnrpc.ChannelBackup - 194, // 158: lnrpc.RestoreChanBackupRequest.chan_backups:type_name -> lnrpc.ChannelBackups - 199, // 159: lnrpc.BakeMacaroonRequest.permissions:type_name -> lnrpc.MacaroonPermission - 199, // 160: lnrpc.MacaroonPermissionList.permissions:type_name -> lnrpc.MacaroonPermission - 247, // 161: lnrpc.ListPermissionsResponse.method_permissions:type_name -> lnrpc.ListPermissionsResponse.MethodPermissionsEntry - 20, // 162: lnrpc.Failure.code:type_name -> lnrpc.Failure.FailureCode - 210, // 163: lnrpc.Failure.channel_update:type_name -> lnrpc.ChannelUpdate - 212, // 164: lnrpc.MacaroonId.ops:type_name -> lnrpc.Op - 199, // 165: lnrpc.CheckMacPermRequest.permissions:type_name -> lnrpc.MacaroonPermission - 216, // 166: lnrpc.RPCMiddlewareRequest.stream_auth:type_name -> lnrpc.StreamAuth - 217, // 167: lnrpc.RPCMiddlewareRequest.request:type_name -> lnrpc.RPCMessage - 217, // 168: lnrpc.RPCMiddlewareRequest.response:type_name -> lnrpc.RPCMessage - 219, // 169: lnrpc.RPCMiddlewareResponse.register:type_name -> lnrpc.MiddlewareRegistration - 220, // 170: lnrpc.RPCMiddlewareResponse.feedback:type_name -> lnrpc.InterceptFeedback - 178, // 171: lnrpc.Peer.FeaturesEntry.value:type_name -> lnrpc.Feature - 178, // 172: lnrpc.GetInfoResponse.FeaturesEntry.value:type_name -> lnrpc.Feature - 4, // 173: lnrpc.PendingChannelsResponse.PendingChannel.initiator:type_name -> lnrpc.Initiator - 3, // 174: lnrpc.PendingChannelsResponse.PendingChannel.commitment_type:type_name -> lnrpc.CommitmentType - 227, // 175: lnrpc.PendingChannelsResponse.PendingOpenChannel.channel:type_name -> lnrpc.PendingChannelsResponse.PendingChannel - 227, // 176: lnrpc.PendingChannelsResponse.WaitingCloseChannel.channel:type_name -> lnrpc.PendingChannelsResponse.PendingChannel - 230, // 177: lnrpc.PendingChannelsResponse.WaitingCloseChannel.commitments:type_name -> lnrpc.PendingChannelsResponse.Commitments - 227, // 178: lnrpc.PendingChannelsResponse.ClosedChannel.channel:type_name -> lnrpc.PendingChannelsResponse.PendingChannel - 227, // 179: lnrpc.PendingChannelsResponse.ForceClosedChannel.channel:type_name -> lnrpc.PendingChannelsResponse.PendingChannel - 108, // 180: lnrpc.PendingChannelsResponse.ForceClosedChannel.pending_htlcs:type_name -> lnrpc.PendingHTLC - 15, // 181: lnrpc.PendingChannelsResponse.ForceClosedChannel.anchor:type_name -> lnrpc.PendingChannelsResponse.ForceClosedChannel.AnchorState - 113, // 182: lnrpc.WalletBalanceResponse.AccountBalanceEntry.value:type_name -> lnrpc.WalletAccountBalance - 178, // 183: lnrpc.LightningNode.FeaturesEntry.value:type_name -> lnrpc.Feature - 137, // 184: lnrpc.NodeMetricsResponse.BetweennessCentralityEntry.value:type_name -> lnrpc.FloatMetric - 178, // 185: lnrpc.NodeUpdate.FeaturesEntry.value:type_name -> lnrpc.Feature - 178, // 186: lnrpc.Invoice.FeaturesEntry.value:type_name -> lnrpc.Feature - 154, // 187: lnrpc.Invoice.AmpInvoiceStateEntry.value:type_name -> lnrpc.AMPInvoiceState - 178, // 188: lnrpc.PayReq.FeaturesEntry.value:type_name -> lnrpc.Feature - 206, // 189: lnrpc.ListPermissionsResponse.MethodPermissionsEntry.value:type_name -> lnrpc.MacaroonPermissionList - 114, // 190: lnrpc.Lightning.WalletBalance:input_type -> lnrpc.WalletBalanceRequest - 117, // 191: lnrpc.Lightning.ChannelBalance:input_type -> lnrpc.ChannelBalanceRequest - 30, // 192: lnrpc.Lightning.GetTransactions:input_type -> lnrpc.GetTransactionsRequest - 42, // 193: lnrpc.Lightning.EstimateFee:input_type -> lnrpc.EstimateFeeRequest - 46, // 194: lnrpc.Lightning.SendCoins:input_type -> lnrpc.SendCoinsRequest - 48, // 195: lnrpc.Lightning.ListUnspent:input_type -> lnrpc.ListUnspentRequest - 30, // 196: lnrpc.Lightning.SubscribeTransactions:input_type -> lnrpc.GetTransactionsRequest - 44, // 197: lnrpc.Lightning.SendMany:input_type -> lnrpc.SendManyRequest - 50, // 198: lnrpc.Lightning.NewAddress:input_type -> lnrpc.NewAddressRequest - 52, // 199: lnrpc.Lightning.SignMessage:input_type -> lnrpc.SignMessageRequest - 54, // 200: lnrpc.Lightning.VerifyMessage:input_type -> lnrpc.VerifyMessageRequest - 56, // 201: lnrpc.Lightning.ConnectPeer:input_type -> lnrpc.ConnectPeerRequest - 58, // 202: lnrpc.Lightning.DisconnectPeer:input_type -> lnrpc.DisconnectPeerRequest - 74, // 203: lnrpc.Lightning.ListPeers:input_type -> lnrpc.ListPeersRequest - 76, // 204: lnrpc.Lightning.SubscribePeerEvents:input_type -> lnrpc.PeerEventSubscription - 78, // 205: lnrpc.Lightning.GetInfo:input_type -> lnrpc.GetInfoRequest - 80, // 206: lnrpc.Lightning.GetDebugInfo:input_type -> lnrpc.GetDebugInfoRequest - 82, // 207: lnrpc.Lightning.GetRecoveryInfo:input_type -> lnrpc.GetRecoveryInfoRequest - 109, // 208: lnrpc.Lightning.PendingChannels:input_type -> lnrpc.PendingChannelsRequest - 63, // 209: lnrpc.Lightning.ListChannels:input_type -> lnrpc.ListChannelsRequest - 111, // 210: lnrpc.Lightning.SubscribeChannelEvents:input_type -> lnrpc.ChannelEventSubscription - 70, // 211: lnrpc.Lightning.ClosedChannels:input_type -> lnrpc.ClosedChannelsRequest - 96, // 212: lnrpc.Lightning.OpenChannelSync:input_type -> lnrpc.OpenChannelRequest - 96, // 213: lnrpc.Lightning.OpenChannel:input_type -> lnrpc.OpenChannelRequest - 93, // 214: lnrpc.Lightning.BatchOpenChannel:input_type -> lnrpc.BatchOpenChannelRequest - 106, // 215: lnrpc.Lightning.FundingStateStep:input_type -> lnrpc.FundingTransitionMsg - 37, // 216: lnrpc.Lightning.ChannelAcceptor:input_type -> lnrpc.ChannelAcceptResponse - 88, // 217: lnrpc.Lightning.CloseChannel:input_type -> lnrpc.CloseChannelRequest - 172, // 218: lnrpc.Lightning.AbandonChannel:input_type -> lnrpc.AbandonChannelRequest - 33, // 219: lnrpc.Lightning.SendPayment:input_type -> lnrpc.SendRequest - 33, // 220: lnrpc.Lightning.SendPaymentSync:input_type -> lnrpc.SendRequest - 35, // 221: lnrpc.Lightning.SendToRoute:input_type -> lnrpc.SendToRouteRequest - 35, // 222: lnrpc.Lightning.SendToRouteSync:input_type -> lnrpc.SendToRouteRequest - 155, // 223: lnrpc.Lightning.AddInvoice:input_type -> lnrpc.Invoice - 161, // 224: lnrpc.Lightning.ListInvoices:input_type -> lnrpc.ListInvoiceRequest - 160, // 225: lnrpc.Lightning.LookupInvoice:input_type -> lnrpc.PaymentHash - 163, // 226: lnrpc.Lightning.SubscribeInvoices:input_type -> lnrpc.InvoiceSubscription - 176, // 227: lnrpc.Lightning.DecodePayReq:input_type -> lnrpc.PayReqString - 166, // 228: lnrpc.Lightning.ListPayments:input_type -> lnrpc.ListPaymentsRequest - 168, // 229: lnrpc.Lightning.DeletePayment:input_type -> lnrpc.DeletePaymentRequest - 169, // 230: lnrpc.Lightning.DeleteAllPayments:input_type -> lnrpc.DeleteAllPaymentsRequest - 133, // 231: lnrpc.Lightning.DescribeGraph:input_type -> lnrpc.ChannelGraphRequest - 135, // 232: lnrpc.Lightning.GetNodeMetrics:input_type -> lnrpc.NodeMetricsRequest - 138, // 233: lnrpc.Lightning.GetChanInfo:input_type -> lnrpc.ChanInfoRequest - 127, // 234: lnrpc.Lightning.GetNodeInfo:input_type -> lnrpc.NodeInfoRequest - 119, // 235: lnrpc.Lightning.QueryRoutes:input_type -> lnrpc.QueryRoutesRequest - 139, // 236: lnrpc.Lightning.GetNetworkInfo:input_type -> lnrpc.NetworkInfoRequest - 141, // 237: lnrpc.Lightning.StopDaemon:input_type -> lnrpc.StopRequest - 143, // 238: lnrpc.Lightning.SubscribeChannelGraph:input_type -> lnrpc.GraphTopologySubscription - 174, // 239: lnrpc.Lightning.DebugLevel:input_type -> lnrpc.DebugLevelRequest - 179, // 240: lnrpc.Lightning.FeeReport:input_type -> lnrpc.FeeReportRequest - 183, // 241: lnrpc.Lightning.UpdateChannelPolicy:input_type -> lnrpc.PolicyUpdateRequest - 186, // 242: lnrpc.Lightning.ForwardingHistory:input_type -> lnrpc.ForwardingHistoryRequest - 189, // 243: lnrpc.Lightning.ExportChannelBackup:input_type -> lnrpc.ExportChannelBackupRequest - 192, // 244: lnrpc.Lightning.ExportAllChannelBackups:input_type -> lnrpc.ChanBackupExportRequest - 193, // 245: lnrpc.Lightning.VerifyChanBackup:input_type -> lnrpc.ChanBackupSnapshot - 195, // 246: lnrpc.Lightning.RestoreChannelBackups:input_type -> lnrpc.RestoreChanBackupRequest - 197, // 247: lnrpc.Lightning.SubscribeChannelBackups:input_type -> lnrpc.ChannelBackupSubscription - 200, // 248: lnrpc.Lightning.BakeMacaroon:input_type -> lnrpc.BakeMacaroonRequest - 202, // 249: lnrpc.Lightning.ListMacaroonIDs:input_type -> lnrpc.ListMacaroonIDsRequest - 204, // 250: lnrpc.Lightning.DeleteMacaroonID:input_type -> lnrpc.DeleteMacaroonIDRequest - 207, // 251: lnrpc.Lightning.ListPermissions:input_type -> lnrpc.ListPermissionsRequest - 213, // 252: lnrpc.Lightning.CheckMacaroonPermissions:input_type -> lnrpc.CheckMacPermRequest - 218, // 253: lnrpc.Lightning.RegisterRPCMiddleware:input_type -> lnrpc.RPCMiddlewareResponse - 25, // 254: lnrpc.Lightning.SendCustomMessage:input_type -> lnrpc.SendCustomMessageRequest - 23, // 255: lnrpc.Lightning.SubscribeCustomMessages:input_type -> lnrpc.SubscribeCustomMessagesRequest - 66, // 256: lnrpc.Lightning.ListAliases:input_type -> lnrpc.ListAliasesRequest - 21, // 257: lnrpc.Lightning.LookupHtlcResolution:input_type -> lnrpc.LookupHtlcResolutionRequest - 115, // 258: lnrpc.Lightning.WalletBalance:output_type -> lnrpc.WalletBalanceResponse - 118, // 259: lnrpc.Lightning.ChannelBalance:output_type -> lnrpc.ChannelBalanceResponse - 31, // 260: lnrpc.Lightning.GetTransactions:output_type -> lnrpc.TransactionDetails - 43, // 261: lnrpc.Lightning.EstimateFee:output_type -> lnrpc.EstimateFeeResponse - 47, // 262: lnrpc.Lightning.SendCoins:output_type -> lnrpc.SendCoinsResponse - 49, // 263: lnrpc.Lightning.ListUnspent:output_type -> lnrpc.ListUnspentResponse - 29, // 264: lnrpc.Lightning.SubscribeTransactions:output_type -> lnrpc.Transaction - 45, // 265: lnrpc.Lightning.SendMany:output_type -> lnrpc.SendManyResponse - 51, // 266: lnrpc.Lightning.NewAddress:output_type -> lnrpc.NewAddressResponse - 53, // 267: lnrpc.Lightning.SignMessage:output_type -> lnrpc.SignMessageResponse - 55, // 268: lnrpc.Lightning.VerifyMessage:output_type -> lnrpc.VerifyMessageResponse - 57, // 269: lnrpc.Lightning.ConnectPeer:output_type -> lnrpc.ConnectPeerResponse - 59, // 270: lnrpc.Lightning.DisconnectPeer:output_type -> lnrpc.DisconnectPeerResponse - 75, // 271: lnrpc.Lightning.ListPeers:output_type -> lnrpc.ListPeersResponse - 77, // 272: lnrpc.Lightning.SubscribePeerEvents:output_type -> lnrpc.PeerEvent - 79, // 273: lnrpc.Lightning.GetInfo:output_type -> lnrpc.GetInfoResponse - 81, // 274: lnrpc.Lightning.GetDebugInfo:output_type -> lnrpc.GetDebugInfoResponse - 83, // 275: lnrpc.Lightning.GetRecoveryInfo:output_type -> lnrpc.GetRecoveryInfoResponse - 110, // 276: lnrpc.Lightning.PendingChannels:output_type -> lnrpc.PendingChannelsResponse - 64, // 277: lnrpc.Lightning.ListChannels:output_type -> lnrpc.ListChannelsResponse - 112, // 278: lnrpc.Lightning.SubscribeChannelEvents:output_type -> lnrpc.ChannelEventUpdate - 71, // 279: lnrpc.Lightning.ClosedChannels:output_type -> lnrpc.ClosedChannelsResponse - 38, // 280: lnrpc.Lightning.OpenChannelSync:output_type -> lnrpc.ChannelPoint - 97, // 281: lnrpc.Lightning.OpenChannel:output_type -> lnrpc.OpenStatusUpdate - 95, // 282: lnrpc.Lightning.BatchOpenChannel:output_type -> lnrpc.BatchOpenChannelResponse - 107, // 283: lnrpc.Lightning.FundingStateStep:output_type -> lnrpc.FundingStateStepResp - 36, // 284: lnrpc.Lightning.ChannelAcceptor:output_type -> lnrpc.ChannelAcceptRequest - 89, // 285: lnrpc.Lightning.CloseChannel:output_type -> lnrpc.CloseStatusUpdate - 173, // 286: lnrpc.Lightning.AbandonChannel:output_type -> lnrpc.AbandonChannelResponse - 34, // 287: lnrpc.Lightning.SendPayment:output_type -> lnrpc.SendResponse - 34, // 288: lnrpc.Lightning.SendPaymentSync:output_type -> lnrpc.SendResponse - 34, // 289: lnrpc.Lightning.SendToRoute:output_type -> lnrpc.SendResponse - 34, // 290: lnrpc.Lightning.SendToRouteSync:output_type -> lnrpc.SendResponse - 159, // 291: lnrpc.Lightning.AddInvoice:output_type -> lnrpc.AddInvoiceResponse - 162, // 292: lnrpc.Lightning.ListInvoices:output_type -> lnrpc.ListInvoiceResponse - 155, // 293: lnrpc.Lightning.LookupInvoice:output_type -> lnrpc.Invoice - 155, // 294: lnrpc.Lightning.SubscribeInvoices:output_type -> lnrpc.Invoice - 177, // 295: lnrpc.Lightning.DecodePayReq:output_type -> lnrpc.PayReq - 167, // 296: lnrpc.Lightning.ListPayments:output_type -> lnrpc.ListPaymentsResponse - 170, // 297: lnrpc.Lightning.DeletePayment:output_type -> lnrpc.DeletePaymentResponse - 171, // 298: lnrpc.Lightning.DeleteAllPayments:output_type -> lnrpc.DeleteAllPaymentsResponse - 134, // 299: lnrpc.Lightning.DescribeGraph:output_type -> lnrpc.ChannelGraph - 136, // 300: lnrpc.Lightning.GetNodeMetrics:output_type -> lnrpc.NodeMetricsResponse - 132, // 301: lnrpc.Lightning.GetChanInfo:output_type -> lnrpc.ChannelEdge - 128, // 302: lnrpc.Lightning.GetNodeInfo:output_type -> lnrpc.NodeInfo - 122, // 303: lnrpc.Lightning.QueryRoutes:output_type -> lnrpc.QueryRoutesResponse - 140, // 304: lnrpc.Lightning.GetNetworkInfo:output_type -> lnrpc.NetworkInfo - 142, // 305: lnrpc.Lightning.StopDaemon:output_type -> lnrpc.StopResponse - 144, // 306: lnrpc.Lightning.SubscribeChannelGraph:output_type -> lnrpc.GraphTopologyUpdate - 175, // 307: lnrpc.Lightning.DebugLevel:output_type -> lnrpc.DebugLevelResponse - 181, // 308: lnrpc.Lightning.FeeReport:output_type -> lnrpc.FeeReportResponse - 185, // 309: lnrpc.Lightning.UpdateChannelPolicy:output_type -> lnrpc.PolicyUpdateResponse - 188, // 310: lnrpc.Lightning.ForwardingHistory:output_type -> lnrpc.ForwardingHistoryResponse - 190, // 311: lnrpc.Lightning.ExportChannelBackup:output_type -> lnrpc.ChannelBackup - 193, // 312: lnrpc.Lightning.ExportAllChannelBackups:output_type -> lnrpc.ChanBackupSnapshot - 198, // 313: lnrpc.Lightning.VerifyChanBackup:output_type -> lnrpc.VerifyChanBackupResponse - 196, // 314: lnrpc.Lightning.RestoreChannelBackups:output_type -> lnrpc.RestoreBackupResponse - 193, // 315: lnrpc.Lightning.SubscribeChannelBackups:output_type -> lnrpc.ChanBackupSnapshot - 201, // 316: lnrpc.Lightning.BakeMacaroon:output_type -> lnrpc.BakeMacaroonResponse - 203, // 317: lnrpc.Lightning.ListMacaroonIDs:output_type -> lnrpc.ListMacaroonIDsResponse - 205, // 318: lnrpc.Lightning.DeleteMacaroonID:output_type -> lnrpc.DeleteMacaroonIDResponse - 208, // 319: lnrpc.Lightning.ListPermissions:output_type -> lnrpc.ListPermissionsResponse - 214, // 320: lnrpc.Lightning.CheckMacaroonPermissions:output_type -> lnrpc.CheckMacPermResponse - 215, // 321: lnrpc.Lightning.RegisterRPCMiddleware:output_type -> lnrpc.RPCMiddlewareRequest - 26, // 322: lnrpc.Lightning.SendCustomMessage:output_type -> lnrpc.SendCustomMessageResponse - 24, // 323: lnrpc.Lightning.SubscribeCustomMessages:output_type -> lnrpc.CustomMessage - 67, // 324: lnrpc.Lightning.ListAliases:output_type -> lnrpc.ListAliasesResponse - 22, // 325: lnrpc.Lightning.LookupHtlcResolution:output_type -> lnrpc.LookupHtlcResolutionResponse - 258, // [258:326] is the sub-list for method output_type - 190, // [190:258] is the sub-list for method input_type - 190, // [190:190] is the sub-list for extension type_name - 190, // [190:190] is the sub-list for extension extendee - 0, // [0:190] is the sub-list for field type_name + 87, // 44: lnrpc.ChannelCloseUpdate.local_close_output:type_name -> lnrpc.CloseOutput + 87, // 45: lnrpc.ChannelCloseUpdate.remote_close_output:type_name -> lnrpc.CloseOutput + 87, // 46: lnrpc.ChannelCloseUpdate.additional_outputs:type_name -> lnrpc.CloseOutput + 38, // 47: lnrpc.CloseChannelRequest.channel_point:type_name -> lnrpc.ChannelPoint + 91, // 48: lnrpc.CloseStatusUpdate.close_pending:type_name -> lnrpc.PendingUpdate + 88, // 49: lnrpc.CloseStatusUpdate.chan_close:type_name -> lnrpc.ChannelCloseUpdate + 92, // 50: lnrpc.CloseStatusUpdate.close_instant:type_name -> lnrpc.InstantUpdate + 95, // 51: lnrpc.BatchOpenChannelRequest.channels:type_name -> lnrpc.BatchOpenChannel + 1, // 52: lnrpc.BatchOpenChannelRequest.coin_selection_strategy:type_name -> lnrpc.CoinSelectionStrategy + 3, // 53: lnrpc.BatchOpenChannel.commitment_type:type_name -> lnrpc.CommitmentType + 91, // 54: lnrpc.BatchOpenChannelResponse.pending_channels:type_name -> lnrpc.PendingUpdate + 103, // 55: lnrpc.OpenChannelRequest.funding_shim:type_name -> lnrpc.FundingShim + 3, // 56: lnrpc.OpenChannelRequest.commitment_type:type_name -> lnrpc.CommitmentType + 39, // 57: lnrpc.OpenChannelRequest.outpoints:type_name -> lnrpc.OutPoint + 91, // 58: lnrpc.OpenStatusUpdate.chan_pending:type_name -> lnrpc.PendingUpdate + 86, // 59: lnrpc.OpenStatusUpdate.chan_open:type_name -> lnrpc.ChannelOpenUpdate + 93, // 60: lnrpc.OpenStatusUpdate.psbt_fund:type_name -> lnrpc.ReadyForPsbtFunding + 99, // 61: lnrpc.KeyDescriptor.key_loc:type_name -> lnrpc.KeyLocator + 38, // 62: lnrpc.ChanPointShim.chan_point:type_name -> lnrpc.ChannelPoint + 100, // 63: lnrpc.ChanPointShim.local_key:type_name -> lnrpc.KeyDescriptor + 101, // 64: lnrpc.FundingShim.chan_point_shim:type_name -> lnrpc.ChanPointShim + 102, // 65: lnrpc.FundingShim.psbt_shim:type_name -> lnrpc.PsbtShim + 103, // 66: lnrpc.FundingTransitionMsg.shim_register:type_name -> lnrpc.FundingShim + 104, // 67: lnrpc.FundingTransitionMsg.shim_cancel:type_name -> lnrpc.FundingShimCancel + 105, // 68: lnrpc.FundingTransitionMsg.psbt_verify:type_name -> lnrpc.FundingPsbtVerify + 106, // 69: lnrpc.FundingTransitionMsg.psbt_finalize:type_name -> lnrpc.FundingPsbtFinalize + 229, // 70: lnrpc.PendingChannelsResponse.pending_open_channels:type_name -> lnrpc.PendingChannelsResponse.PendingOpenChannel + 232, // 71: lnrpc.PendingChannelsResponse.pending_closing_channels:type_name -> lnrpc.PendingChannelsResponse.ClosedChannel + 233, // 72: lnrpc.PendingChannelsResponse.pending_force_closing_channels:type_name -> lnrpc.PendingChannelsResponse.ForceClosedChannel + 230, // 73: lnrpc.PendingChannelsResponse.waiting_close_channels:type_name -> lnrpc.PendingChannelsResponse.WaitingCloseChannel + 62, // 74: lnrpc.ChannelEventUpdate.open_channel:type_name -> lnrpc.Channel + 68, // 75: lnrpc.ChannelEventUpdate.closed_channel:type_name -> lnrpc.ChannelCloseSummary + 38, // 76: lnrpc.ChannelEventUpdate.active_channel:type_name -> lnrpc.ChannelPoint + 38, // 77: lnrpc.ChannelEventUpdate.inactive_channel:type_name -> lnrpc.ChannelPoint + 91, // 78: lnrpc.ChannelEventUpdate.pending_open_channel:type_name -> lnrpc.PendingUpdate + 38, // 79: lnrpc.ChannelEventUpdate.fully_resolved_channel:type_name -> lnrpc.ChannelPoint + 16, // 80: lnrpc.ChannelEventUpdate.type:type_name -> lnrpc.ChannelEventUpdate.UpdateType + 234, // 81: lnrpc.WalletBalanceResponse.account_balance:type_name -> lnrpc.WalletBalanceResponse.AccountBalanceEntry + 117, // 82: lnrpc.ChannelBalanceResponse.local_balance:type_name -> lnrpc.Amount + 117, // 83: lnrpc.ChannelBalanceResponse.remote_balance:type_name -> lnrpc.Amount + 117, // 84: lnrpc.ChannelBalanceResponse.unsettled_local_balance:type_name -> lnrpc.Amount + 117, // 85: lnrpc.ChannelBalanceResponse.unsettled_remote_balance:type_name -> lnrpc.Amount + 117, // 86: lnrpc.ChannelBalanceResponse.pending_open_local_balance:type_name -> lnrpc.Amount + 117, // 87: lnrpc.ChannelBalanceResponse.pending_open_remote_balance:type_name -> lnrpc.Amount + 32, // 88: lnrpc.QueryRoutesRequest.fee_limit:type_name -> lnrpc.FeeLimit + 122, // 89: lnrpc.QueryRoutesRequest.ignored_edges:type_name -> lnrpc.EdgeLocator + 121, // 90: lnrpc.QueryRoutesRequest.ignored_pairs:type_name -> lnrpc.NodePair + 235, // 91: lnrpc.QueryRoutesRequest.dest_custom_records:type_name -> lnrpc.QueryRoutesRequest.DestCustomRecordsEntry + 151, // 92: lnrpc.QueryRoutesRequest.route_hints:type_name -> lnrpc.RouteHint + 152, // 93: lnrpc.QueryRoutesRequest.blinded_payment_paths:type_name -> lnrpc.BlindedPaymentPath + 10, // 94: lnrpc.QueryRoutesRequest.dest_features:type_name -> lnrpc.FeatureBit + 127, // 95: lnrpc.QueryRoutesResponse.routes:type_name -> lnrpc.Route + 125, // 96: lnrpc.Hop.mpp_record:type_name -> lnrpc.MPPRecord + 126, // 97: lnrpc.Hop.amp_record:type_name -> lnrpc.AMPRecord + 236, // 98: lnrpc.Hop.custom_records:type_name -> lnrpc.Hop.CustomRecordsEntry + 124, // 99: lnrpc.Route.hops:type_name -> lnrpc.Hop + 130, // 100: lnrpc.NodeInfo.node:type_name -> lnrpc.LightningNode + 133, // 101: lnrpc.NodeInfo.channels:type_name -> lnrpc.ChannelEdge + 131, // 102: lnrpc.LightningNode.addresses:type_name -> lnrpc.NodeAddress + 237, // 103: lnrpc.LightningNode.features:type_name -> lnrpc.LightningNode.FeaturesEntry + 238, // 104: lnrpc.LightningNode.custom_records:type_name -> lnrpc.LightningNode.CustomRecordsEntry + 239, // 105: lnrpc.RoutingPolicy.custom_records:type_name -> lnrpc.RoutingPolicy.CustomRecordsEntry + 132, // 106: lnrpc.ChannelEdge.node1_policy:type_name -> lnrpc.RoutingPolicy + 132, // 107: lnrpc.ChannelEdge.node2_policy:type_name -> lnrpc.RoutingPolicy + 240, // 108: lnrpc.ChannelEdge.custom_records:type_name -> lnrpc.ChannelEdge.CustomRecordsEntry + 130, // 109: lnrpc.ChannelGraph.nodes:type_name -> lnrpc.LightningNode + 133, // 110: lnrpc.ChannelGraph.edges:type_name -> lnrpc.ChannelEdge + 7, // 111: lnrpc.NodeMetricsRequest.types:type_name -> lnrpc.NodeMetricType + 241, // 112: lnrpc.NodeMetricsResponse.betweenness_centrality:type_name -> lnrpc.NodeMetricsResponse.BetweennessCentralityEntry + 146, // 113: lnrpc.GraphTopologyUpdate.node_updates:type_name -> lnrpc.NodeUpdate + 147, // 114: lnrpc.GraphTopologyUpdate.channel_updates:type_name -> lnrpc.ChannelEdgeUpdate + 148, // 115: lnrpc.GraphTopologyUpdate.closed_chans:type_name -> lnrpc.ClosedChannelUpdate + 131, // 116: lnrpc.NodeUpdate.node_addresses:type_name -> lnrpc.NodeAddress + 242, // 117: lnrpc.NodeUpdate.features:type_name -> lnrpc.NodeUpdate.FeaturesEntry + 38, // 118: lnrpc.ChannelEdgeUpdate.chan_point:type_name -> lnrpc.ChannelPoint + 132, // 119: lnrpc.ChannelEdgeUpdate.routing_policy:type_name -> lnrpc.RoutingPolicy + 38, // 120: lnrpc.ClosedChannelUpdate.chan_point:type_name -> lnrpc.ChannelPoint + 149, // 121: lnrpc.RouteHint.hop_hints:type_name -> lnrpc.HopHint + 153, // 122: lnrpc.BlindedPaymentPath.blinded_path:type_name -> lnrpc.BlindedPath + 10, // 123: lnrpc.BlindedPaymentPath.features:type_name -> lnrpc.FeatureBit + 154, // 124: lnrpc.BlindedPath.blinded_hops:type_name -> lnrpc.BlindedHop + 8, // 125: lnrpc.AMPInvoiceState.state:type_name -> lnrpc.InvoiceHTLCState + 151, // 126: lnrpc.Invoice.route_hints:type_name -> lnrpc.RouteHint + 17, // 127: lnrpc.Invoice.state:type_name -> lnrpc.Invoice.InvoiceState + 158, // 128: lnrpc.Invoice.htlcs:type_name -> lnrpc.InvoiceHTLC + 243, // 129: lnrpc.Invoice.features:type_name -> lnrpc.Invoice.FeaturesEntry + 244, // 130: lnrpc.Invoice.amp_invoice_state:type_name -> lnrpc.Invoice.AmpInvoiceStateEntry + 157, // 131: lnrpc.Invoice.blinded_path_config:type_name -> lnrpc.BlindedPathConfig + 8, // 132: lnrpc.InvoiceHTLC.state:type_name -> lnrpc.InvoiceHTLCState + 245, // 133: lnrpc.InvoiceHTLC.custom_records:type_name -> lnrpc.InvoiceHTLC.CustomRecordsEntry + 159, // 134: lnrpc.InvoiceHTLC.amp:type_name -> lnrpc.AMP + 156, // 135: lnrpc.ListInvoiceResponse.invoices:type_name -> lnrpc.Invoice + 18, // 136: lnrpc.Payment.status:type_name -> lnrpc.Payment.PaymentStatus + 166, // 137: lnrpc.Payment.htlcs:type_name -> lnrpc.HTLCAttempt + 9, // 138: lnrpc.Payment.failure_reason:type_name -> lnrpc.PaymentFailureReason + 246, // 139: lnrpc.Payment.first_hop_custom_records:type_name -> lnrpc.Payment.FirstHopCustomRecordsEntry + 19, // 140: lnrpc.HTLCAttempt.status:type_name -> lnrpc.HTLCAttempt.HTLCStatus + 127, // 141: lnrpc.HTLCAttempt.route:type_name -> lnrpc.Route + 210, // 142: lnrpc.HTLCAttempt.failure:type_name -> lnrpc.Failure + 165, // 143: lnrpc.ListPaymentsResponse.payments:type_name -> lnrpc.Payment + 38, // 144: lnrpc.AbandonChannelRequest.channel_point:type_name -> lnrpc.ChannelPoint + 151, // 145: lnrpc.PayReq.route_hints:type_name -> lnrpc.RouteHint + 247, // 146: lnrpc.PayReq.features:type_name -> lnrpc.PayReq.FeaturesEntry + 152, // 147: lnrpc.PayReq.blinded_paths:type_name -> lnrpc.BlindedPaymentPath + 181, // 148: lnrpc.FeeReportResponse.channel_fees:type_name -> lnrpc.ChannelFeeReport + 38, // 149: lnrpc.PolicyUpdateRequest.chan_point:type_name -> lnrpc.ChannelPoint + 183, // 150: lnrpc.PolicyUpdateRequest.inbound_fee:type_name -> lnrpc.InboundFee + 39, // 151: lnrpc.FailedUpdate.outpoint:type_name -> lnrpc.OutPoint + 11, // 152: lnrpc.FailedUpdate.reason:type_name -> lnrpc.UpdateFailure + 185, // 153: lnrpc.PolicyUpdateResponse.failed_updates:type_name -> lnrpc.FailedUpdate + 188, // 154: lnrpc.ForwardingHistoryResponse.forwarding_events:type_name -> lnrpc.ForwardingEvent + 38, // 155: lnrpc.ExportChannelBackupRequest.chan_point:type_name -> lnrpc.ChannelPoint + 38, // 156: lnrpc.ChannelBackup.chan_point:type_name -> lnrpc.ChannelPoint + 38, // 157: lnrpc.MultiChanBackup.chan_points:type_name -> lnrpc.ChannelPoint + 195, // 158: lnrpc.ChanBackupSnapshot.single_chan_backups:type_name -> lnrpc.ChannelBackups + 192, // 159: lnrpc.ChanBackupSnapshot.multi_chan_backup:type_name -> lnrpc.MultiChanBackup + 191, // 160: lnrpc.ChannelBackups.chan_backups:type_name -> lnrpc.ChannelBackup + 195, // 161: lnrpc.RestoreChanBackupRequest.chan_backups:type_name -> lnrpc.ChannelBackups + 200, // 162: lnrpc.BakeMacaroonRequest.permissions:type_name -> lnrpc.MacaroonPermission + 200, // 163: lnrpc.MacaroonPermissionList.permissions:type_name -> lnrpc.MacaroonPermission + 248, // 164: lnrpc.ListPermissionsResponse.method_permissions:type_name -> lnrpc.ListPermissionsResponse.MethodPermissionsEntry + 20, // 165: lnrpc.Failure.code:type_name -> lnrpc.Failure.FailureCode + 211, // 166: lnrpc.Failure.channel_update:type_name -> lnrpc.ChannelUpdate + 213, // 167: lnrpc.MacaroonId.ops:type_name -> lnrpc.Op + 200, // 168: lnrpc.CheckMacPermRequest.permissions:type_name -> lnrpc.MacaroonPermission + 217, // 169: lnrpc.RPCMiddlewareRequest.stream_auth:type_name -> lnrpc.StreamAuth + 218, // 170: lnrpc.RPCMiddlewareRequest.request:type_name -> lnrpc.RPCMessage + 218, // 171: lnrpc.RPCMiddlewareRequest.response:type_name -> lnrpc.RPCMessage + 220, // 172: lnrpc.RPCMiddlewareResponse.register:type_name -> lnrpc.MiddlewareRegistration + 221, // 173: lnrpc.RPCMiddlewareResponse.feedback:type_name -> lnrpc.InterceptFeedback + 179, // 174: lnrpc.Peer.FeaturesEntry.value:type_name -> lnrpc.Feature + 179, // 175: lnrpc.GetInfoResponse.FeaturesEntry.value:type_name -> lnrpc.Feature + 4, // 176: lnrpc.PendingChannelsResponse.PendingChannel.initiator:type_name -> lnrpc.Initiator + 3, // 177: lnrpc.PendingChannelsResponse.PendingChannel.commitment_type:type_name -> lnrpc.CommitmentType + 228, // 178: lnrpc.PendingChannelsResponse.PendingOpenChannel.channel:type_name -> lnrpc.PendingChannelsResponse.PendingChannel + 228, // 179: lnrpc.PendingChannelsResponse.WaitingCloseChannel.channel:type_name -> lnrpc.PendingChannelsResponse.PendingChannel + 231, // 180: lnrpc.PendingChannelsResponse.WaitingCloseChannel.commitments:type_name -> lnrpc.PendingChannelsResponse.Commitments + 228, // 181: lnrpc.PendingChannelsResponse.ClosedChannel.channel:type_name -> lnrpc.PendingChannelsResponse.PendingChannel + 228, // 182: lnrpc.PendingChannelsResponse.ForceClosedChannel.channel:type_name -> lnrpc.PendingChannelsResponse.PendingChannel + 109, // 183: lnrpc.PendingChannelsResponse.ForceClosedChannel.pending_htlcs:type_name -> lnrpc.PendingHTLC + 15, // 184: lnrpc.PendingChannelsResponse.ForceClosedChannel.anchor:type_name -> lnrpc.PendingChannelsResponse.ForceClosedChannel.AnchorState + 114, // 185: lnrpc.WalletBalanceResponse.AccountBalanceEntry.value:type_name -> lnrpc.WalletAccountBalance + 179, // 186: lnrpc.LightningNode.FeaturesEntry.value:type_name -> lnrpc.Feature + 138, // 187: lnrpc.NodeMetricsResponse.BetweennessCentralityEntry.value:type_name -> lnrpc.FloatMetric + 179, // 188: lnrpc.NodeUpdate.FeaturesEntry.value:type_name -> lnrpc.Feature + 179, // 189: lnrpc.Invoice.FeaturesEntry.value:type_name -> lnrpc.Feature + 155, // 190: lnrpc.Invoice.AmpInvoiceStateEntry.value:type_name -> lnrpc.AMPInvoiceState + 179, // 191: lnrpc.PayReq.FeaturesEntry.value:type_name -> lnrpc.Feature + 207, // 192: lnrpc.ListPermissionsResponse.MethodPermissionsEntry.value:type_name -> lnrpc.MacaroonPermissionList + 115, // 193: lnrpc.Lightning.WalletBalance:input_type -> lnrpc.WalletBalanceRequest + 118, // 194: lnrpc.Lightning.ChannelBalance:input_type -> lnrpc.ChannelBalanceRequest + 30, // 195: lnrpc.Lightning.GetTransactions:input_type -> lnrpc.GetTransactionsRequest + 42, // 196: lnrpc.Lightning.EstimateFee:input_type -> lnrpc.EstimateFeeRequest + 46, // 197: lnrpc.Lightning.SendCoins:input_type -> lnrpc.SendCoinsRequest + 48, // 198: lnrpc.Lightning.ListUnspent:input_type -> lnrpc.ListUnspentRequest + 30, // 199: lnrpc.Lightning.SubscribeTransactions:input_type -> lnrpc.GetTransactionsRequest + 44, // 200: lnrpc.Lightning.SendMany:input_type -> lnrpc.SendManyRequest + 50, // 201: lnrpc.Lightning.NewAddress:input_type -> lnrpc.NewAddressRequest + 52, // 202: lnrpc.Lightning.SignMessage:input_type -> lnrpc.SignMessageRequest + 54, // 203: lnrpc.Lightning.VerifyMessage:input_type -> lnrpc.VerifyMessageRequest + 56, // 204: lnrpc.Lightning.ConnectPeer:input_type -> lnrpc.ConnectPeerRequest + 58, // 205: lnrpc.Lightning.DisconnectPeer:input_type -> lnrpc.DisconnectPeerRequest + 74, // 206: lnrpc.Lightning.ListPeers:input_type -> lnrpc.ListPeersRequest + 76, // 207: lnrpc.Lightning.SubscribePeerEvents:input_type -> lnrpc.PeerEventSubscription + 78, // 208: lnrpc.Lightning.GetInfo:input_type -> lnrpc.GetInfoRequest + 80, // 209: lnrpc.Lightning.GetDebugInfo:input_type -> lnrpc.GetDebugInfoRequest + 82, // 210: lnrpc.Lightning.GetRecoveryInfo:input_type -> lnrpc.GetRecoveryInfoRequest + 110, // 211: lnrpc.Lightning.PendingChannels:input_type -> lnrpc.PendingChannelsRequest + 63, // 212: lnrpc.Lightning.ListChannels:input_type -> lnrpc.ListChannelsRequest + 112, // 213: lnrpc.Lightning.SubscribeChannelEvents:input_type -> lnrpc.ChannelEventSubscription + 70, // 214: lnrpc.Lightning.ClosedChannels:input_type -> lnrpc.ClosedChannelsRequest + 97, // 215: lnrpc.Lightning.OpenChannelSync:input_type -> lnrpc.OpenChannelRequest + 97, // 216: lnrpc.Lightning.OpenChannel:input_type -> lnrpc.OpenChannelRequest + 94, // 217: lnrpc.Lightning.BatchOpenChannel:input_type -> lnrpc.BatchOpenChannelRequest + 107, // 218: lnrpc.Lightning.FundingStateStep:input_type -> lnrpc.FundingTransitionMsg + 37, // 219: lnrpc.Lightning.ChannelAcceptor:input_type -> lnrpc.ChannelAcceptResponse + 89, // 220: lnrpc.Lightning.CloseChannel:input_type -> lnrpc.CloseChannelRequest + 173, // 221: lnrpc.Lightning.AbandonChannel:input_type -> lnrpc.AbandonChannelRequest + 33, // 222: lnrpc.Lightning.SendPayment:input_type -> lnrpc.SendRequest + 33, // 223: lnrpc.Lightning.SendPaymentSync:input_type -> lnrpc.SendRequest + 35, // 224: lnrpc.Lightning.SendToRoute:input_type -> lnrpc.SendToRouteRequest + 35, // 225: lnrpc.Lightning.SendToRouteSync:input_type -> lnrpc.SendToRouteRequest + 156, // 226: lnrpc.Lightning.AddInvoice:input_type -> lnrpc.Invoice + 162, // 227: lnrpc.Lightning.ListInvoices:input_type -> lnrpc.ListInvoiceRequest + 161, // 228: lnrpc.Lightning.LookupInvoice:input_type -> lnrpc.PaymentHash + 164, // 229: lnrpc.Lightning.SubscribeInvoices:input_type -> lnrpc.InvoiceSubscription + 177, // 230: lnrpc.Lightning.DecodePayReq:input_type -> lnrpc.PayReqString + 167, // 231: lnrpc.Lightning.ListPayments:input_type -> lnrpc.ListPaymentsRequest + 169, // 232: lnrpc.Lightning.DeletePayment:input_type -> lnrpc.DeletePaymentRequest + 170, // 233: lnrpc.Lightning.DeleteAllPayments:input_type -> lnrpc.DeleteAllPaymentsRequest + 134, // 234: lnrpc.Lightning.DescribeGraph:input_type -> lnrpc.ChannelGraphRequest + 136, // 235: lnrpc.Lightning.GetNodeMetrics:input_type -> lnrpc.NodeMetricsRequest + 139, // 236: lnrpc.Lightning.GetChanInfo:input_type -> lnrpc.ChanInfoRequest + 128, // 237: lnrpc.Lightning.GetNodeInfo:input_type -> lnrpc.NodeInfoRequest + 120, // 238: lnrpc.Lightning.QueryRoutes:input_type -> lnrpc.QueryRoutesRequest + 140, // 239: lnrpc.Lightning.GetNetworkInfo:input_type -> lnrpc.NetworkInfoRequest + 142, // 240: lnrpc.Lightning.StopDaemon:input_type -> lnrpc.StopRequest + 144, // 241: lnrpc.Lightning.SubscribeChannelGraph:input_type -> lnrpc.GraphTopologySubscription + 175, // 242: lnrpc.Lightning.DebugLevel:input_type -> lnrpc.DebugLevelRequest + 180, // 243: lnrpc.Lightning.FeeReport:input_type -> lnrpc.FeeReportRequest + 184, // 244: lnrpc.Lightning.UpdateChannelPolicy:input_type -> lnrpc.PolicyUpdateRequest + 187, // 245: lnrpc.Lightning.ForwardingHistory:input_type -> lnrpc.ForwardingHistoryRequest + 190, // 246: lnrpc.Lightning.ExportChannelBackup:input_type -> lnrpc.ExportChannelBackupRequest + 193, // 247: lnrpc.Lightning.ExportAllChannelBackups:input_type -> lnrpc.ChanBackupExportRequest + 194, // 248: lnrpc.Lightning.VerifyChanBackup:input_type -> lnrpc.ChanBackupSnapshot + 196, // 249: lnrpc.Lightning.RestoreChannelBackups:input_type -> lnrpc.RestoreChanBackupRequest + 198, // 250: lnrpc.Lightning.SubscribeChannelBackups:input_type -> lnrpc.ChannelBackupSubscription + 201, // 251: lnrpc.Lightning.BakeMacaroon:input_type -> lnrpc.BakeMacaroonRequest + 203, // 252: lnrpc.Lightning.ListMacaroonIDs:input_type -> lnrpc.ListMacaroonIDsRequest + 205, // 253: lnrpc.Lightning.DeleteMacaroonID:input_type -> lnrpc.DeleteMacaroonIDRequest + 208, // 254: lnrpc.Lightning.ListPermissions:input_type -> lnrpc.ListPermissionsRequest + 214, // 255: lnrpc.Lightning.CheckMacaroonPermissions:input_type -> lnrpc.CheckMacPermRequest + 219, // 256: lnrpc.Lightning.RegisterRPCMiddleware:input_type -> lnrpc.RPCMiddlewareResponse + 25, // 257: lnrpc.Lightning.SendCustomMessage:input_type -> lnrpc.SendCustomMessageRequest + 23, // 258: lnrpc.Lightning.SubscribeCustomMessages:input_type -> lnrpc.SubscribeCustomMessagesRequest + 66, // 259: lnrpc.Lightning.ListAliases:input_type -> lnrpc.ListAliasesRequest + 21, // 260: lnrpc.Lightning.LookupHtlcResolution:input_type -> lnrpc.LookupHtlcResolutionRequest + 116, // 261: lnrpc.Lightning.WalletBalance:output_type -> lnrpc.WalletBalanceResponse + 119, // 262: lnrpc.Lightning.ChannelBalance:output_type -> lnrpc.ChannelBalanceResponse + 31, // 263: lnrpc.Lightning.GetTransactions:output_type -> lnrpc.TransactionDetails + 43, // 264: lnrpc.Lightning.EstimateFee:output_type -> lnrpc.EstimateFeeResponse + 47, // 265: lnrpc.Lightning.SendCoins:output_type -> lnrpc.SendCoinsResponse + 49, // 266: lnrpc.Lightning.ListUnspent:output_type -> lnrpc.ListUnspentResponse + 29, // 267: lnrpc.Lightning.SubscribeTransactions:output_type -> lnrpc.Transaction + 45, // 268: lnrpc.Lightning.SendMany:output_type -> lnrpc.SendManyResponse + 51, // 269: lnrpc.Lightning.NewAddress:output_type -> lnrpc.NewAddressResponse + 53, // 270: lnrpc.Lightning.SignMessage:output_type -> lnrpc.SignMessageResponse + 55, // 271: lnrpc.Lightning.VerifyMessage:output_type -> lnrpc.VerifyMessageResponse + 57, // 272: lnrpc.Lightning.ConnectPeer:output_type -> lnrpc.ConnectPeerResponse + 59, // 273: lnrpc.Lightning.DisconnectPeer:output_type -> lnrpc.DisconnectPeerResponse + 75, // 274: lnrpc.Lightning.ListPeers:output_type -> lnrpc.ListPeersResponse + 77, // 275: lnrpc.Lightning.SubscribePeerEvents:output_type -> lnrpc.PeerEvent + 79, // 276: lnrpc.Lightning.GetInfo:output_type -> lnrpc.GetInfoResponse + 81, // 277: lnrpc.Lightning.GetDebugInfo:output_type -> lnrpc.GetDebugInfoResponse + 83, // 278: lnrpc.Lightning.GetRecoveryInfo:output_type -> lnrpc.GetRecoveryInfoResponse + 111, // 279: lnrpc.Lightning.PendingChannels:output_type -> lnrpc.PendingChannelsResponse + 64, // 280: lnrpc.Lightning.ListChannels:output_type -> lnrpc.ListChannelsResponse + 113, // 281: lnrpc.Lightning.SubscribeChannelEvents:output_type -> lnrpc.ChannelEventUpdate + 71, // 282: lnrpc.Lightning.ClosedChannels:output_type -> lnrpc.ClosedChannelsResponse + 38, // 283: lnrpc.Lightning.OpenChannelSync:output_type -> lnrpc.ChannelPoint + 98, // 284: lnrpc.Lightning.OpenChannel:output_type -> lnrpc.OpenStatusUpdate + 96, // 285: lnrpc.Lightning.BatchOpenChannel:output_type -> lnrpc.BatchOpenChannelResponse + 108, // 286: lnrpc.Lightning.FundingStateStep:output_type -> lnrpc.FundingStateStepResp + 36, // 287: lnrpc.Lightning.ChannelAcceptor:output_type -> lnrpc.ChannelAcceptRequest + 90, // 288: lnrpc.Lightning.CloseChannel:output_type -> lnrpc.CloseStatusUpdate + 174, // 289: lnrpc.Lightning.AbandonChannel:output_type -> lnrpc.AbandonChannelResponse + 34, // 290: lnrpc.Lightning.SendPayment:output_type -> lnrpc.SendResponse + 34, // 291: lnrpc.Lightning.SendPaymentSync:output_type -> lnrpc.SendResponse + 34, // 292: lnrpc.Lightning.SendToRoute:output_type -> lnrpc.SendResponse + 34, // 293: lnrpc.Lightning.SendToRouteSync:output_type -> lnrpc.SendResponse + 160, // 294: lnrpc.Lightning.AddInvoice:output_type -> lnrpc.AddInvoiceResponse + 163, // 295: lnrpc.Lightning.ListInvoices:output_type -> lnrpc.ListInvoiceResponse + 156, // 296: lnrpc.Lightning.LookupInvoice:output_type -> lnrpc.Invoice + 156, // 297: lnrpc.Lightning.SubscribeInvoices:output_type -> lnrpc.Invoice + 178, // 298: lnrpc.Lightning.DecodePayReq:output_type -> lnrpc.PayReq + 168, // 299: lnrpc.Lightning.ListPayments:output_type -> lnrpc.ListPaymentsResponse + 171, // 300: lnrpc.Lightning.DeletePayment:output_type -> lnrpc.DeletePaymentResponse + 172, // 301: lnrpc.Lightning.DeleteAllPayments:output_type -> lnrpc.DeleteAllPaymentsResponse + 135, // 302: lnrpc.Lightning.DescribeGraph:output_type -> lnrpc.ChannelGraph + 137, // 303: lnrpc.Lightning.GetNodeMetrics:output_type -> lnrpc.NodeMetricsResponse + 133, // 304: lnrpc.Lightning.GetChanInfo:output_type -> lnrpc.ChannelEdge + 129, // 305: lnrpc.Lightning.GetNodeInfo:output_type -> lnrpc.NodeInfo + 123, // 306: lnrpc.Lightning.QueryRoutes:output_type -> lnrpc.QueryRoutesResponse + 141, // 307: lnrpc.Lightning.GetNetworkInfo:output_type -> lnrpc.NetworkInfo + 143, // 308: lnrpc.Lightning.StopDaemon:output_type -> lnrpc.StopResponse + 145, // 309: lnrpc.Lightning.SubscribeChannelGraph:output_type -> lnrpc.GraphTopologyUpdate + 176, // 310: lnrpc.Lightning.DebugLevel:output_type -> lnrpc.DebugLevelResponse + 182, // 311: lnrpc.Lightning.FeeReport:output_type -> lnrpc.FeeReportResponse + 186, // 312: lnrpc.Lightning.UpdateChannelPolicy:output_type -> lnrpc.PolicyUpdateResponse + 189, // 313: lnrpc.Lightning.ForwardingHistory:output_type -> lnrpc.ForwardingHistoryResponse + 191, // 314: lnrpc.Lightning.ExportChannelBackup:output_type -> lnrpc.ChannelBackup + 194, // 315: lnrpc.Lightning.ExportAllChannelBackups:output_type -> lnrpc.ChanBackupSnapshot + 199, // 316: lnrpc.Lightning.VerifyChanBackup:output_type -> lnrpc.VerifyChanBackupResponse + 197, // 317: lnrpc.Lightning.RestoreChannelBackups:output_type -> lnrpc.RestoreBackupResponse + 194, // 318: lnrpc.Lightning.SubscribeChannelBackups:output_type -> lnrpc.ChanBackupSnapshot + 202, // 319: lnrpc.Lightning.BakeMacaroon:output_type -> lnrpc.BakeMacaroonResponse + 204, // 320: lnrpc.Lightning.ListMacaroonIDs:output_type -> lnrpc.ListMacaroonIDsResponse + 206, // 321: lnrpc.Lightning.DeleteMacaroonID:output_type -> lnrpc.DeleteMacaroonIDResponse + 209, // 322: lnrpc.Lightning.ListPermissions:output_type -> lnrpc.ListPermissionsResponse + 215, // 323: lnrpc.Lightning.CheckMacaroonPermissions:output_type -> lnrpc.CheckMacPermResponse + 216, // 324: lnrpc.Lightning.RegisterRPCMiddleware:output_type -> lnrpc.RPCMiddlewareRequest + 26, // 325: lnrpc.Lightning.SendCustomMessage:output_type -> lnrpc.SendCustomMessageResponse + 24, // 326: lnrpc.Lightning.SubscribeCustomMessages:output_type -> lnrpc.CustomMessage + 67, // 327: lnrpc.Lightning.ListAliases:output_type -> lnrpc.ListAliasesResponse + 22, // 328: lnrpc.Lightning.LookupHtlcResolution:output_type -> lnrpc.LookupHtlcResolutionResponse + 261, // [261:329] is the sub-list for method output_type + 193, // [193:261] is the sub-list for method input_type + 193, // [193:193] is the sub-list for extension type_name + 193, // [193:193] is the sub-list for extension extendee + 0, // [0:193] is the sub-list for field type_name } func init() { file_lightning_proto_init() } @@ -22764,7 +22897,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[66].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ChannelCloseUpdate); i { + switch v := v.(*CloseOutput); i { case 0: return &v.state case 1: @@ -22776,7 +22909,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[67].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*CloseChannelRequest); i { + switch v := v.(*ChannelCloseUpdate); i { case 0: return &v.state case 1: @@ -22788,7 +22921,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[68].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*CloseStatusUpdate); i { + switch v := v.(*CloseChannelRequest); i { case 0: return &v.state case 1: @@ -22800,7 +22933,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[69].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*PendingUpdate); i { + switch v := v.(*CloseStatusUpdate); i { case 0: return &v.state case 1: @@ -22812,7 +22945,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[70].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*InstantUpdate); i { + switch v := v.(*PendingUpdate); i { case 0: return &v.state case 1: @@ -22824,7 +22957,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[71].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ReadyForPsbtFunding); i { + switch v := v.(*InstantUpdate); i { case 0: return &v.state case 1: @@ -22836,7 +22969,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[72].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*BatchOpenChannelRequest); i { + switch v := v.(*ReadyForPsbtFunding); i { case 0: return &v.state case 1: @@ -22848,7 +22981,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[73].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*BatchOpenChannel); i { + switch v := v.(*BatchOpenChannelRequest); i { case 0: return &v.state case 1: @@ -22860,7 +22993,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[74].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*BatchOpenChannelResponse); i { + switch v := v.(*BatchOpenChannel); i { case 0: return &v.state case 1: @@ -22872,7 +23005,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[75].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*OpenChannelRequest); i { + switch v := v.(*BatchOpenChannelResponse); i { case 0: return &v.state case 1: @@ -22884,7 +23017,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[76].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*OpenStatusUpdate); i { + switch v := v.(*OpenChannelRequest); i { case 0: return &v.state case 1: @@ -22896,7 +23029,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[77].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*KeyLocator); i { + switch v := v.(*OpenStatusUpdate); i { case 0: return &v.state case 1: @@ -22908,7 +23041,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[78].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*KeyDescriptor); i { + switch v := v.(*KeyLocator); i { case 0: return &v.state case 1: @@ -22920,7 +23053,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[79].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ChanPointShim); i { + switch v := v.(*KeyDescriptor); i { case 0: return &v.state case 1: @@ -22932,7 +23065,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[80].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*PsbtShim); i { + switch v := v.(*ChanPointShim); i { case 0: return &v.state case 1: @@ -22944,7 +23077,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[81].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*FundingShim); i { + switch v := v.(*PsbtShim); i { case 0: return &v.state case 1: @@ -22956,7 +23089,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[82].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*FundingShimCancel); i { + switch v := v.(*FundingShim); i { case 0: return &v.state case 1: @@ -22968,7 +23101,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[83].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*FundingPsbtVerify); i { + switch v := v.(*FundingShimCancel); i { case 0: return &v.state case 1: @@ -22980,7 +23113,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[84].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*FundingPsbtFinalize); i { + switch v := v.(*FundingPsbtVerify); i { case 0: return &v.state case 1: @@ -22992,7 +23125,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[85].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*FundingTransitionMsg); i { + switch v := v.(*FundingPsbtFinalize); i { case 0: return &v.state case 1: @@ -23004,7 +23137,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[86].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*FundingStateStepResp); i { + switch v := v.(*FundingTransitionMsg); i { case 0: return &v.state case 1: @@ -23016,7 +23149,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[87].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*PendingHTLC); i { + switch v := v.(*FundingStateStepResp); i { case 0: return &v.state case 1: @@ -23028,7 +23161,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[88].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*PendingChannelsRequest); i { + switch v := v.(*PendingHTLC); i { case 0: return &v.state case 1: @@ -23040,7 +23173,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[89].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*PendingChannelsResponse); i { + switch v := v.(*PendingChannelsRequest); i { case 0: return &v.state case 1: @@ -23052,7 +23185,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[90].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ChannelEventSubscription); i { + switch v := v.(*PendingChannelsResponse); i { case 0: return &v.state case 1: @@ -23064,7 +23197,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[91].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ChannelEventUpdate); i { + switch v := v.(*ChannelEventSubscription); i { case 0: return &v.state case 1: @@ -23076,7 +23209,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[92].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*WalletAccountBalance); i { + switch v := v.(*ChannelEventUpdate); i { case 0: return &v.state case 1: @@ -23088,7 +23221,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[93].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*WalletBalanceRequest); i { + switch v := v.(*WalletAccountBalance); i { case 0: return &v.state case 1: @@ -23100,7 +23233,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[94].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*WalletBalanceResponse); i { + switch v := v.(*WalletBalanceRequest); i { case 0: return &v.state case 1: @@ -23112,7 +23245,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[95].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Amount); i { + switch v := v.(*WalletBalanceResponse); i { case 0: return &v.state case 1: @@ -23124,7 +23257,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[96].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ChannelBalanceRequest); i { + switch v := v.(*Amount); i { case 0: return &v.state case 1: @@ -23136,7 +23269,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[97].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ChannelBalanceResponse); i { + switch v := v.(*ChannelBalanceRequest); i { case 0: return &v.state case 1: @@ -23148,7 +23281,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[98].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*QueryRoutesRequest); i { + switch v := v.(*ChannelBalanceResponse); i { case 0: return &v.state case 1: @@ -23160,7 +23293,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[99].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*NodePair); i { + switch v := v.(*QueryRoutesRequest); i { case 0: return &v.state case 1: @@ -23172,7 +23305,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[100].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*EdgeLocator); i { + switch v := v.(*NodePair); i { case 0: return &v.state case 1: @@ -23184,7 +23317,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[101].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*QueryRoutesResponse); i { + switch v := v.(*EdgeLocator); i { case 0: return &v.state case 1: @@ -23196,7 +23329,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[102].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Hop); i { + switch v := v.(*QueryRoutesResponse); i { case 0: return &v.state case 1: @@ -23208,7 +23341,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[103].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*MPPRecord); i { + switch v := v.(*Hop); i { case 0: return &v.state case 1: @@ -23220,7 +23353,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[104].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*AMPRecord); i { + switch v := v.(*MPPRecord); i { case 0: return &v.state case 1: @@ -23232,7 +23365,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[105].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Route); i { + switch v := v.(*AMPRecord); i { case 0: return &v.state case 1: @@ -23244,7 +23377,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[106].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*NodeInfoRequest); i { + switch v := v.(*Route); i { case 0: return &v.state case 1: @@ -23256,7 +23389,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[107].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*NodeInfo); i { + switch v := v.(*NodeInfoRequest); i { case 0: return &v.state case 1: @@ -23268,7 +23401,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[108].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*LightningNode); i { + switch v := v.(*NodeInfo); i { case 0: return &v.state case 1: @@ -23280,7 +23413,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[109].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*NodeAddress); i { + switch v := v.(*LightningNode); i { case 0: return &v.state case 1: @@ -23292,7 +23425,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[110].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*RoutingPolicy); i { + switch v := v.(*NodeAddress); i { case 0: return &v.state case 1: @@ -23304,7 +23437,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[111].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ChannelEdge); i { + switch v := v.(*RoutingPolicy); i { case 0: return &v.state case 1: @@ -23316,7 +23449,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[112].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ChannelGraphRequest); i { + switch v := v.(*ChannelEdge); i { case 0: return &v.state case 1: @@ -23328,7 +23461,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[113].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ChannelGraph); i { + switch v := v.(*ChannelGraphRequest); i { case 0: return &v.state case 1: @@ -23340,7 +23473,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[114].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*NodeMetricsRequest); i { + switch v := v.(*ChannelGraph); i { case 0: return &v.state case 1: @@ -23352,7 +23485,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[115].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*NodeMetricsResponse); i { + switch v := v.(*NodeMetricsRequest); i { case 0: return &v.state case 1: @@ -23364,7 +23497,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[116].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*FloatMetric); i { + switch v := v.(*NodeMetricsResponse); i { case 0: return &v.state case 1: @@ -23376,7 +23509,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[117].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ChanInfoRequest); i { + switch v := v.(*FloatMetric); i { case 0: return &v.state case 1: @@ -23388,7 +23521,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[118].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*NetworkInfoRequest); i { + switch v := v.(*ChanInfoRequest); i { case 0: return &v.state case 1: @@ -23400,7 +23533,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[119].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*NetworkInfo); i { + switch v := v.(*NetworkInfoRequest); i { case 0: return &v.state case 1: @@ -23412,7 +23545,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[120].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*StopRequest); i { + switch v := v.(*NetworkInfo); i { case 0: return &v.state case 1: @@ -23424,7 +23557,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[121].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*StopResponse); i { + switch v := v.(*StopRequest); i { case 0: return &v.state case 1: @@ -23436,7 +23569,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[122].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GraphTopologySubscription); i { + switch v := v.(*StopResponse); i { case 0: return &v.state case 1: @@ -23448,7 +23581,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[123].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GraphTopologyUpdate); i { + switch v := v.(*GraphTopologySubscription); i { case 0: return &v.state case 1: @@ -23460,7 +23593,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[124].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*NodeUpdate); i { + switch v := v.(*GraphTopologyUpdate); i { case 0: return &v.state case 1: @@ -23472,7 +23605,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[125].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ChannelEdgeUpdate); i { + switch v := v.(*NodeUpdate); i { case 0: return &v.state case 1: @@ -23484,7 +23617,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[126].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ClosedChannelUpdate); i { + switch v := v.(*ChannelEdgeUpdate); i { case 0: return &v.state case 1: @@ -23496,7 +23629,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[127].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*HopHint); i { + switch v := v.(*ClosedChannelUpdate); i { case 0: return &v.state case 1: @@ -23508,7 +23641,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[128].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SetID); i { + switch v := v.(*HopHint); i { case 0: return &v.state case 1: @@ -23520,7 +23653,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[129].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*RouteHint); i { + switch v := v.(*SetID); i { case 0: return &v.state case 1: @@ -23532,7 +23665,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[130].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*BlindedPaymentPath); i { + switch v := v.(*RouteHint); i { case 0: return &v.state case 1: @@ -23544,7 +23677,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[131].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*BlindedPath); i { + switch v := v.(*BlindedPaymentPath); i { case 0: return &v.state case 1: @@ -23556,7 +23689,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[132].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*BlindedHop); i { + switch v := v.(*BlindedPath); i { case 0: return &v.state case 1: @@ -23568,7 +23701,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[133].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*AMPInvoiceState); i { + switch v := v.(*BlindedHop); i { case 0: return &v.state case 1: @@ -23580,7 +23713,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[134].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Invoice); i { + switch v := v.(*AMPInvoiceState); i { case 0: return &v.state case 1: @@ -23592,7 +23725,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[135].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*BlindedPathConfig); i { + switch v := v.(*Invoice); i { case 0: return &v.state case 1: @@ -23604,7 +23737,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[136].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*InvoiceHTLC); i { + switch v := v.(*BlindedPathConfig); i { case 0: return &v.state case 1: @@ -23616,7 +23749,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[137].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*AMP); i { + switch v := v.(*InvoiceHTLC); i { case 0: return &v.state case 1: @@ -23628,7 +23761,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[138].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*AddInvoiceResponse); i { + switch v := v.(*AMP); i { case 0: return &v.state case 1: @@ -23640,7 +23773,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[139].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*PaymentHash); i { + switch v := v.(*AddInvoiceResponse); i { case 0: return &v.state case 1: @@ -23652,7 +23785,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[140].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ListInvoiceRequest); i { + switch v := v.(*PaymentHash); i { case 0: return &v.state case 1: @@ -23664,7 +23797,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[141].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ListInvoiceResponse); i { + switch v := v.(*ListInvoiceRequest); i { case 0: return &v.state case 1: @@ -23676,7 +23809,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[142].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*InvoiceSubscription); i { + switch v := v.(*ListInvoiceResponse); i { case 0: return &v.state case 1: @@ -23688,7 +23821,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[143].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Payment); i { + switch v := v.(*InvoiceSubscription); i { case 0: return &v.state case 1: @@ -23700,7 +23833,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[144].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*HTLCAttempt); i { + switch v := v.(*Payment); i { case 0: return &v.state case 1: @@ -23712,7 +23845,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[145].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ListPaymentsRequest); i { + switch v := v.(*HTLCAttempt); i { case 0: return &v.state case 1: @@ -23724,7 +23857,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[146].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ListPaymentsResponse); i { + switch v := v.(*ListPaymentsRequest); i { case 0: return &v.state case 1: @@ -23736,7 +23869,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[147].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*DeletePaymentRequest); i { + switch v := v.(*ListPaymentsResponse); i { case 0: return &v.state case 1: @@ -23748,7 +23881,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[148].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*DeleteAllPaymentsRequest); i { + switch v := v.(*DeletePaymentRequest); i { case 0: return &v.state case 1: @@ -23760,7 +23893,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[149].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*DeletePaymentResponse); i { + switch v := v.(*DeleteAllPaymentsRequest); i { case 0: return &v.state case 1: @@ -23772,7 +23905,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[150].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*DeleteAllPaymentsResponse); i { + switch v := v.(*DeletePaymentResponse); i { case 0: return &v.state case 1: @@ -23784,7 +23917,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[151].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*AbandonChannelRequest); i { + switch v := v.(*DeleteAllPaymentsResponse); i { case 0: return &v.state case 1: @@ -23796,7 +23929,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[152].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*AbandonChannelResponse); i { + switch v := v.(*AbandonChannelRequest); i { case 0: return &v.state case 1: @@ -23808,7 +23941,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[153].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*DebugLevelRequest); i { + switch v := v.(*AbandonChannelResponse); i { case 0: return &v.state case 1: @@ -23820,7 +23953,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[154].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*DebugLevelResponse); i { + switch v := v.(*DebugLevelRequest); i { case 0: return &v.state case 1: @@ -23832,7 +23965,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[155].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*PayReqString); i { + switch v := v.(*DebugLevelResponse); i { case 0: return &v.state case 1: @@ -23844,7 +23977,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[156].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*PayReq); i { + switch v := v.(*PayReqString); i { case 0: return &v.state case 1: @@ -23856,7 +23989,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[157].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Feature); i { + switch v := v.(*PayReq); i { case 0: return &v.state case 1: @@ -23868,7 +24001,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[158].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*FeeReportRequest); i { + switch v := v.(*Feature); i { case 0: return &v.state case 1: @@ -23880,7 +24013,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[159].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ChannelFeeReport); i { + switch v := v.(*FeeReportRequest); i { case 0: return &v.state case 1: @@ -23892,7 +24025,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[160].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*FeeReportResponse); i { + switch v := v.(*ChannelFeeReport); i { case 0: return &v.state case 1: @@ -23904,7 +24037,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[161].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*InboundFee); i { + switch v := v.(*FeeReportResponse); i { case 0: return &v.state case 1: @@ -23916,7 +24049,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[162].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*PolicyUpdateRequest); i { + switch v := v.(*InboundFee); i { case 0: return &v.state case 1: @@ -23928,7 +24061,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[163].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*FailedUpdate); i { + switch v := v.(*PolicyUpdateRequest); i { case 0: return &v.state case 1: @@ -23940,7 +24073,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[164].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*PolicyUpdateResponse); i { + switch v := v.(*FailedUpdate); i { case 0: return &v.state case 1: @@ -23952,7 +24085,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[165].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ForwardingHistoryRequest); i { + switch v := v.(*PolicyUpdateResponse); i { case 0: return &v.state case 1: @@ -23964,7 +24097,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[166].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ForwardingEvent); i { + switch v := v.(*ForwardingHistoryRequest); i { case 0: return &v.state case 1: @@ -23976,7 +24109,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[167].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ForwardingHistoryResponse); i { + switch v := v.(*ForwardingEvent); i { case 0: return &v.state case 1: @@ -23988,7 +24121,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[168].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ExportChannelBackupRequest); i { + switch v := v.(*ForwardingHistoryResponse); i { case 0: return &v.state case 1: @@ -24000,7 +24133,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[169].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ChannelBackup); i { + switch v := v.(*ExportChannelBackupRequest); i { case 0: return &v.state case 1: @@ -24012,7 +24145,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[170].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*MultiChanBackup); i { + switch v := v.(*ChannelBackup); i { case 0: return &v.state case 1: @@ -24024,7 +24157,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[171].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ChanBackupExportRequest); i { + switch v := v.(*MultiChanBackup); i { case 0: return &v.state case 1: @@ -24036,7 +24169,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[172].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ChanBackupSnapshot); i { + switch v := v.(*ChanBackupExportRequest); i { case 0: return &v.state case 1: @@ -24048,7 +24181,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[173].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ChannelBackups); i { + switch v := v.(*ChanBackupSnapshot); i { case 0: return &v.state case 1: @@ -24060,7 +24193,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[174].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*RestoreChanBackupRequest); i { + switch v := v.(*ChannelBackups); i { case 0: return &v.state case 1: @@ -24072,7 +24205,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[175].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*RestoreBackupResponse); i { + switch v := v.(*RestoreChanBackupRequest); i { case 0: return &v.state case 1: @@ -24084,7 +24217,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[176].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ChannelBackupSubscription); i { + switch v := v.(*RestoreBackupResponse); i { case 0: return &v.state case 1: @@ -24096,7 +24229,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[177].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*VerifyChanBackupResponse); i { + switch v := v.(*ChannelBackupSubscription); i { case 0: return &v.state case 1: @@ -24108,7 +24241,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[178].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*MacaroonPermission); i { + switch v := v.(*VerifyChanBackupResponse); i { case 0: return &v.state case 1: @@ -24120,7 +24253,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[179].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*BakeMacaroonRequest); i { + switch v := v.(*MacaroonPermission); i { case 0: return &v.state case 1: @@ -24132,7 +24265,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[180].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*BakeMacaroonResponse); i { + switch v := v.(*BakeMacaroonRequest); i { case 0: return &v.state case 1: @@ -24144,7 +24277,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[181].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ListMacaroonIDsRequest); i { + switch v := v.(*BakeMacaroonResponse); i { case 0: return &v.state case 1: @@ -24156,7 +24289,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[182].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ListMacaroonIDsResponse); i { + switch v := v.(*ListMacaroonIDsRequest); i { case 0: return &v.state case 1: @@ -24168,7 +24301,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[183].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*DeleteMacaroonIDRequest); i { + switch v := v.(*ListMacaroonIDsResponse); i { case 0: return &v.state case 1: @@ -24180,7 +24313,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[184].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*DeleteMacaroonIDResponse); i { + switch v := v.(*DeleteMacaroonIDRequest); i { case 0: return &v.state case 1: @@ -24192,7 +24325,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[185].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*MacaroonPermissionList); i { + switch v := v.(*DeleteMacaroonIDResponse); i { case 0: return &v.state case 1: @@ -24204,7 +24337,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[186].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ListPermissionsRequest); i { + switch v := v.(*MacaroonPermissionList); i { case 0: return &v.state case 1: @@ -24216,7 +24349,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[187].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ListPermissionsResponse); i { + switch v := v.(*ListPermissionsRequest); i { case 0: return &v.state case 1: @@ -24228,7 +24361,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[188].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Failure); i { + switch v := v.(*ListPermissionsResponse); i { case 0: return &v.state case 1: @@ -24240,7 +24373,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[189].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ChannelUpdate); i { + switch v := v.(*Failure); i { case 0: return &v.state case 1: @@ -24252,7 +24385,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[190].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*MacaroonId); i { + switch v := v.(*ChannelUpdate); i { case 0: return &v.state case 1: @@ -24264,7 +24397,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[191].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Op); i { + switch v := v.(*MacaroonId); i { case 0: return &v.state case 1: @@ -24276,7 +24409,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[192].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*CheckMacPermRequest); i { + switch v := v.(*Op); i { case 0: return &v.state case 1: @@ -24288,7 +24421,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[193].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*CheckMacPermResponse); i { + switch v := v.(*CheckMacPermRequest); i { case 0: return &v.state case 1: @@ -24300,7 +24433,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[194].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*RPCMiddlewareRequest); i { + switch v := v.(*CheckMacPermResponse); i { case 0: return &v.state case 1: @@ -24312,7 +24445,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[195].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*StreamAuth); i { + switch v := v.(*RPCMiddlewareRequest); i { case 0: return &v.state case 1: @@ -24324,7 +24457,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[196].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*RPCMessage); i { + switch v := v.(*StreamAuth); i { case 0: return &v.state case 1: @@ -24336,7 +24469,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[197].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*RPCMiddlewareResponse); i { + switch v := v.(*RPCMessage); i { case 0: return &v.state case 1: @@ -24348,7 +24481,7 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[198].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*MiddlewareRegistration); i { + switch v := v.(*RPCMiddlewareResponse); i { case 0: return &v.state case 1: @@ -24360,6 +24493,18 @@ func file_lightning_proto_init() { } } file_lightning_proto_msgTypes[199].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*MiddlewareRegistration); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_lightning_proto_msgTypes[200].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*InterceptFeedback); i { case 0: return &v.state @@ -24371,7 +24516,7 @@ func file_lightning_proto_init() { return nil } } - file_lightning_proto_msgTypes[206].Exporter = func(v interface{}, i int) interface{} { + file_lightning_proto_msgTypes[207].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*PendingChannelsResponse_PendingChannel); i { case 0: return &v.state @@ -24383,7 +24528,7 @@ func file_lightning_proto_init() { return nil } } - file_lightning_proto_msgTypes[207].Exporter = func(v interface{}, i int) interface{} { + file_lightning_proto_msgTypes[208].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*PendingChannelsResponse_PendingOpenChannel); i { case 0: return &v.state @@ -24395,7 +24540,7 @@ func file_lightning_proto_init() { return nil } } - file_lightning_proto_msgTypes[208].Exporter = func(v interface{}, i int) interface{} { + file_lightning_proto_msgTypes[209].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*PendingChannelsResponse_WaitingCloseChannel); i { case 0: return &v.state @@ -24407,7 +24552,7 @@ func file_lightning_proto_init() { return nil } } - file_lightning_proto_msgTypes[209].Exporter = func(v interface{}, i int) interface{} { + file_lightning_proto_msgTypes[210].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*PendingChannelsResponse_Commitments); i { case 0: return &v.state @@ -24419,7 +24564,7 @@ func file_lightning_proto_init() { return nil } } - file_lightning_proto_msgTypes[210].Exporter = func(v interface{}, i int) interface{} { + file_lightning_proto_msgTypes[211].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*PendingChannelsResponse_ClosedChannel); i { case 0: return &v.state @@ -24431,7 +24576,7 @@ func file_lightning_proto_init() { return nil } } - file_lightning_proto_msgTypes[211].Exporter = func(v interface{}, i int) interface{} { + file_lightning_proto_msgTypes[212].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*PendingChannelsResponse_ForceClosedChannel); i { case 0: return &v.state @@ -24453,27 +24598,27 @@ func file_lightning_proto_init() { (*ChannelPoint_FundingTxidBytes)(nil), (*ChannelPoint_FundingTxidStr)(nil), } - file_lightning_proto_msgTypes[68].OneofWrappers = []interface{}{ + file_lightning_proto_msgTypes[69].OneofWrappers = []interface{}{ (*CloseStatusUpdate_ClosePending)(nil), (*CloseStatusUpdate_ChanClose)(nil), (*CloseStatusUpdate_CloseInstant)(nil), } - file_lightning_proto_msgTypes[76].OneofWrappers = []interface{}{ + file_lightning_proto_msgTypes[77].OneofWrappers = []interface{}{ (*OpenStatusUpdate_ChanPending)(nil), (*OpenStatusUpdate_ChanOpen)(nil), (*OpenStatusUpdate_PsbtFund)(nil), } - file_lightning_proto_msgTypes[81].OneofWrappers = []interface{}{ + file_lightning_proto_msgTypes[82].OneofWrappers = []interface{}{ (*FundingShim_ChanPointShim)(nil), (*FundingShim_PsbtShim)(nil), } - file_lightning_proto_msgTypes[85].OneofWrappers = []interface{}{ + file_lightning_proto_msgTypes[86].OneofWrappers = []interface{}{ (*FundingTransitionMsg_ShimRegister)(nil), (*FundingTransitionMsg_ShimCancel)(nil), (*FundingTransitionMsg_PsbtVerify)(nil), (*FundingTransitionMsg_PsbtFinalize)(nil), } - file_lightning_proto_msgTypes[91].OneofWrappers = []interface{}{ + file_lightning_proto_msgTypes[92].OneofWrappers = []interface{}{ (*ChannelEventUpdate_OpenChannel)(nil), (*ChannelEventUpdate_ClosedChannel)(nil), (*ChannelEventUpdate_ActiveChannel)(nil), @@ -24481,22 +24626,22 @@ func file_lightning_proto_init() { (*ChannelEventUpdate_PendingOpenChannel)(nil), (*ChannelEventUpdate_FullyResolvedChannel)(nil), } - file_lightning_proto_msgTypes[135].OneofWrappers = []interface{}{} - file_lightning_proto_msgTypes[162].OneofWrappers = []interface{}{ + file_lightning_proto_msgTypes[136].OneofWrappers = []interface{}{} + file_lightning_proto_msgTypes[163].OneofWrappers = []interface{}{ (*PolicyUpdateRequest_Global)(nil), (*PolicyUpdateRequest_ChanPoint)(nil), } - file_lightning_proto_msgTypes[174].OneofWrappers = []interface{}{ + file_lightning_proto_msgTypes[175].OneofWrappers = []interface{}{ (*RestoreChanBackupRequest_ChanBackups)(nil), (*RestoreChanBackupRequest_MultiChanBackup)(nil), } - file_lightning_proto_msgTypes[194].OneofWrappers = []interface{}{ + file_lightning_proto_msgTypes[195].OneofWrappers = []interface{}{ (*RPCMiddlewareRequest_StreamAuth)(nil), (*RPCMiddlewareRequest_Request)(nil), (*RPCMiddlewareRequest_Response)(nil), (*RPCMiddlewareRequest_RegComplete)(nil), } - file_lightning_proto_msgTypes[197].OneofWrappers = []interface{}{ + file_lightning_proto_msgTypes[198].OneofWrappers = []interface{}{ (*RPCMiddlewareResponse_Register)(nil), (*RPCMiddlewareResponse_Feedback)(nil), } @@ -24506,7 +24651,7 @@ func file_lightning_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_lightning_proto_rawDesc, NumEnums: 21, - NumMessages: 227, + NumMessages: 228, NumExtensions: 0, NumServices: 1, }, diff --git a/lnrpc/lightning.proto b/lnrpc/lightning.proto index f8cfcaca62..1d481650e5 100644 --- a/lnrpc/lightning.proto +++ b/lnrpc/lightning.proto @@ -2033,10 +2033,38 @@ message ChannelOpenUpdate { ChannelPoint channel_point = 1; } +message CloseOutput { + // The amount in satoshi of this close output. This amount is the final + // commitment balance of the channel and the actual amount paid out on chain + // might be smaller due to subtracted fees. + int64 amount_sat = 1; + + // The pkScript of the close output. + bytes pk_script = 2; + + // Whether this output is for the local or remote node. + bool is_local = 3; + + // The TLV encoded custom channel data records for this output, which might + // be set for custom channels. + bytes custom_channel_data = 4; +} + message ChannelCloseUpdate { bytes closing_txid = 1; bool success = 2; + + // The local channel close output. If the local channel balance was dust to + // begin with, this output will not be set. + CloseOutput local_close_output = 3; + + // The remote channel close output. If the remote channel balance was dust + // to begin with, this output will not be set. + CloseOutput remote_close_output = 4; + + // Any additional outputs that might be added for custom channel types. + repeated CloseOutput additional_outputs = 5; } message CloseChannelRequest { diff --git a/lnrpc/lightning.swagger.json b/lnrpc/lightning.swagger.json index 9e2d2d25be..db3ec2cb36 100644 --- a/lnrpc/lightning.swagger.json +++ b/lnrpc/lightning.swagger.json @@ -4156,6 +4156,21 @@ }, "success": { "type": "boolean" + }, + "local_close_output": { + "$ref": "#/definitions/lnrpcCloseOutput", + "description": "The local channel close output. If the local channel balance was dust to\nbegin with, this output will not be set." + }, + "remote_close_output": { + "$ref": "#/definitions/lnrpcCloseOutput", + "description": "The remote channel close output. If the remote channel balance was dust\nto begin with, this output will not be set." + }, + "additional_outputs": { + "type": "array", + "items": { + "$ref": "#/definitions/lnrpcCloseOutput" + }, + "description": "Any additional outputs that might be added for custom channel types." } } }, @@ -4465,6 +4480,30 @@ } } }, + "lnrpcCloseOutput": { + "type": "object", + "properties": { + "amount_sat": { + "type": "string", + "format": "int64", + "description": "The amount in satoshi of this close output. This amount is the final\ncommitment balance of the channel and the actual amount paid out on chain\nmight be smaller due to subtracted fees." + }, + "pk_script": { + "type": "string", + "format": "byte", + "description": "The pkScript of the close output." + }, + "is_local": { + "type": "boolean", + "description": "Whether this output is for the local or remote node." + }, + "custom_channel_data": { + "type": "string", + "format": "byte", + "description": "The TLV encoded custom channel data records for this output, which might\nbe set for custom channels." + } + } + }, "lnrpcCloseStatusUpdate": { "type": "object", "properties": { diff --git a/peer/brontide.go b/peer/brontide.go index b7d3a4087b..2676740c6d 100644 --- a/peer/brontide.go +++ b/peer/brontide.go @@ -145,6 +145,21 @@ type PendingUpdate struct { type ChannelCloseUpdate struct { ClosingTxid []byte Success bool + + // LocalCloseOutput is an optional, additional output on the closing + // transaction that the local party should be paid to. This will only be + // populated if the local balance isn't dust. + LocalCloseOutput fn.Option[chancloser.CloseOutput] + + // RemoteCloseOutput is an optional, additional output on the closing + // transaction that the remote party should be paid to. This will only + // be populated if the remote balance isn't dust. + RemoteCloseOutput fn.Option[chancloser.CloseOutput] + + // AuxOutputs is an optional set of additional outputs that might be + // included in the closing transaction. These are used for custom + // channel types. + AuxOutputs fn.Option[chancloser.AuxCloseOutputs] } // TimestampedError is a timestamped error that is used to store the most recent @@ -3567,17 +3582,25 @@ func (p *Brontide) finalizeChanClosure(chanCloser *chancloser.ChanCloser) { } } - go WaitForChanToClose(chanCloser.NegotiationHeight(), notifier, errChan, + localOut := chanCloser.LocalCloseOutput() + remoteOut := chanCloser.RemoteCloseOutput() + auxOut := chanCloser.AuxOutputs() + go WaitForChanToClose( + chanCloser.NegotiationHeight(), notifier, errChan, &chanPoint, &closingTxid, closingTx.TxOut[0].PkScript, func() { // Respond to the local subsystem which requested the // channel closure. if closeReq != nil { closeReq.Updates <- &ChannelCloseUpdate{ - ClosingTxid: closingTxid[:], - Success: true, + ClosingTxid: closingTxid[:], + Success: true, + LocalCloseOutput: localOut, + RemoteCloseOutput: remoteOut, + AuxOutputs: auxOut, } } - }) + }, + ) } // WaitForChanToClose uses the passed notifier to wait until the channel has diff --git a/rpcserver.go b/rpcserver.go index 6404845f62..ea75ff6beb 100644 --- a/rpcserver.go +++ b/rpcserver.go @@ -2685,7 +2685,6 @@ func (r *rpcServer) CloseChannel(in *lnrpc.CloseChannelRequest, // transaction here rather than going to the switch as we don't require // interaction from the peer. if force { - // As we're force closing this channel, as a precaution, we'll // ensure that the switch doesn't continue to see this channel // as eligible for forwarding HTLC's. If the peer is online, @@ -2725,15 +2724,19 @@ func (r *rpcServer) CloseChannel(in *lnrpc.CloseChannelRequest, errChan = make(chan error, 1) notifier := r.server.cc.ChainNotifier - go peer.WaitForChanToClose(uint32(bestHeight), notifier, errChan, chanPoint, + go peer.WaitForChanToClose( + uint32(bestHeight), notifier, errChan, chanPoint, &closingTxid, closingTx.TxOut[0].PkScript, func() { // Respond to the local subsystem which // requested the channel closure. updateChan <- &peer.ChannelCloseUpdate{ ClosingTxid: closingTxid[:], Success: true, + // Force closure transactions don't have + // additional local/remote outputs. } - }) + }, + ) } else { // If this is a frozen channel, then we only allow the co-op // close to proceed if we were the responder to this channel if @@ -2857,6 +2860,19 @@ out: return err } + err = fn.MapOptionZ( + r.server.implCfg.AuxDataParser, + func(parser AuxDataParser) error { + return parser.InlineParseCustomData( + rpcClosingUpdate, + ) + }, + ) + if err != nil { + return fmt.Errorf("error parsing custom data: "+ + "%w", err) + } + rpcsLog.Tracef("[closechannel] sending update: %v", rpcClosingUpdate) @@ -2882,19 +2898,84 @@ out: return nil } -func createRPCCloseUpdate(update interface{}) ( - *lnrpc.CloseStatusUpdate, error) { +func createRPCCloseUpdate( + update interface{}) (*lnrpc.CloseStatusUpdate, error) { switch u := update.(type) { case *peer.ChannelCloseUpdate: + ccu := &lnrpc.ChannelCloseUpdate{ + ClosingTxid: u.ClosingTxid, + Success: u.Success, + } + + err := fn.MapOptionZ( + u.LocalCloseOutput, + func(closeOut chancloser.CloseOutput) error { + cr, err := closeOut.ShutdownRecords.Serialize() + if err != nil { + return fmt.Errorf("error serializing "+ + "local close out custom "+ + "records: %w", err) + } + + rpcCloseOut := &lnrpc.CloseOutput{ + AmountSat: int64(closeOut.Amt), + PkScript: closeOut.PkScript, + IsLocal: true, + CustomChannelData: cr, + } + ccu.LocalCloseOutput = rpcCloseOut + + return nil + }, + ) + if err != nil { + return nil, err + } + + err = fn.MapOptionZ( + u.RemoteCloseOutput, + func(closeOut chancloser.CloseOutput) error { + cr, err := closeOut.ShutdownRecords.Serialize() + if err != nil { + return fmt.Errorf("error serializing "+ + "remote close out custom "+ + "records: %w", err) + } + + rpcCloseOut := &lnrpc.CloseOutput{ + AmountSat: int64(closeOut.Amt), + PkScript: closeOut.PkScript, + CustomChannelData: cr, + } + ccu.RemoteCloseOutput = rpcCloseOut + + return nil + }, + ) + if err != nil { + return nil, err + } + + u.AuxOutputs.WhenSome(func(outs chancloser.AuxCloseOutputs) { + for _, out := range outs.ExtraCloseOutputs { + ccu.AdditionalOutputs = append( + ccu.AdditionalOutputs, + &lnrpc.CloseOutput{ + AmountSat: out.Value, + PkScript: out.PkScript, + IsLocal: out.IsLocal, + }, + ) + } + }) + return &lnrpc.CloseStatusUpdate{ Update: &lnrpc.CloseStatusUpdate_ChanClose{ - ChanClose: &lnrpc.ChannelCloseUpdate{ - ClosingTxid: u.ClosingTxid, - Success: u.Success, - }, + ChanClose: ccu, }, }, nil + case *peer.PendingUpdate: return &lnrpc.CloseStatusUpdate{ Update: &lnrpc.CloseStatusUpdate_ClosePending{ From d2c745e610eaede7212088f7e38c2233f47c610e Mon Sep 17 00:00:00 2001 From: Elle Mouton Date: Tue, 24 Sep 2024 12:36:15 +0900 Subject: [PATCH 138/218] multi: update PaymentAddr to use fn.Option Since it is allowed to not be set and so can lead to nil deref panics if it is a pointer. --- docs/release-notes/release-notes-0.18.4.md | 56 ++++++++++++++++++++++ docs/release-notes/release-notes-0.19.0.md | 0 lnrpc/routerrpc/router_backend.go | 7 +-- lnrpc/routerrpc/router_server.go | 11 +++-- routing/integrated_routing_context_test.go | 2 +- routing/pathfind.go | 23 ++++----- routing/pathfind_test.go | 22 ++++----- routing/payment_session.go | 2 +- routing/router.go | 11 +++-- routing/router_test.go | 18 +++---- rpcserver.go | 14 +++--- zpay32/decode.go | 11 ++++- zpay32/encode.go | 15 +++--- zpay32/invoice.go | 5 +- zpay32/invoice_test.go | 11 +++-- 15 files changed, 138 insertions(+), 70 deletions(-) create mode 100644 docs/release-notes/release-notes-0.18.4.md create mode 100644 docs/release-notes/release-notes-0.19.0.md diff --git a/docs/release-notes/release-notes-0.18.4.md b/docs/release-notes/release-notes-0.18.4.md new file mode 100644 index 0000000000..74d0198281 --- /dev/null +++ b/docs/release-notes/release-notes-0.18.4.md @@ -0,0 +1,56 @@ +# Release Notes +- [Bug Fixes](#bug-fixes) +- [New Features](#new-features) + - [Functional Enhancements](#functional-enhancements) + - [RPC Additions](#rpc-additions) + - [lncli Additions](#lncli-additions) +- [Improvements](#improvements) + - [Functional Updates](#functional-updates) + - [RPC Updates](#rpc-updates) + - [lncli Updates](#lncli-updates) + - [Breaking Changes](#breaking-changes) + - [Performance Improvements](#performance-improvements) +- [Technical and Architectural Updates](#technical-and-architectural-updates) + - [BOLT Spec Updates](#bolt-spec-updates) + - [Testing](#testing) + - [Database](#database) + - [Code Health](#code-health) + - [Tooling and Documentation](#tooling-and-documentation) + +# Bug Fixes + +* [Fix a bug](https://github.com/lightningnetwork/lnd/pull/9134) that would + cause a nil pointer dereference during the probing of a payment request that + does not contain a payment address. + +# New Features +## Functional Enhancements +## RPC Additions + +## lncli Additions + +# Improvements +## Functional Updates + +## RPC Updates + +## lncli Updates + +## Code Health + +## Breaking Changes +## Performance Improvements + +# Technical and Architectural Updates +## BOLT Spec Updates + +## Testing +## Database + +## Code Health + +## Tooling and Documentation + +# Contributors (Alphabetical Order) + +* Elle Mouton diff --git a/docs/release-notes/release-notes-0.19.0.md b/docs/release-notes/release-notes-0.19.0.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/lnrpc/routerrpc/router_backend.go b/lnrpc/routerrpc/router_backend.go index 4ef60a71da..7aa7e5c098 100644 --- a/lnrpc/routerrpc/router_backend.go +++ b/lnrpc/routerrpc/router_backend.go @@ -16,6 +16,7 @@ import ( sphinx "github.com/lightningnetwork/lightning-onion" "github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/feature" + "github.com/lightningnetwork/lnd/fn" "github.com/lightningnetwork/lnd/htlcswitch" "github.com/lightningnetwork/lnd/lnrpc" "github.com/lightningnetwork/lnd/lntypes" @@ -1011,7 +1012,7 @@ func (r *RouterBackend) extractIntentFromSendRequest( if len(rpcPayReq.PaymentAddr) > 0 { var addr [32]byte copy(addr[:], rpcPayReq.PaymentAddr) - payAddr = &addr + payAddr = fn.Some(addr) } } else { err = payIntent.SetPaymentHash(*payReq.PaymentHash) @@ -1128,7 +1129,7 @@ func (r *RouterBackend) extractIntentFromSendRequest( } else { copy(payAddr[:], rpcPayReq.PaymentAddr) } - payIntent.PaymentAddr = &payAddr + payIntent.PaymentAddr = fn.Some(payAddr) // Generate random SetID and root share. var setID [32]byte @@ -1167,7 +1168,7 @@ func (r *RouterBackend) extractIntentFromSendRequest( var payAddr [32]byte copy(payAddr[:], rpcPayReq.PaymentAddr) - payIntent.PaymentAddr = &payAddr + payIntent.PaymentAddr = fn.Some(payAddr) } } diff --git a/lnrpc/routerrpc/router_server.go b/lnrpc/routerrpc/router_server.go index 8091998a93..a4112ba646 100644 --- a/lnrpc/routerrpc/router_server.go +++ b/lnrpc/routerrpc/router_server.go @@ -528,11 +528,16 @@ func (s *Server) probePaymentRequest(ctx context.Context, paymentRequest string, AmtMsat: amtMsat, PaymentHash: paymentHash[:], FeeLimitSat: routeFeeLimitSat, - PaymentAddr: payReq.PaymentAddr[:], FinalCltvDelta: int32(payReq.MinFinalCLTVExpiry()), DestFeatures: MarshalFeatures(payReq.Features), } + // If the payment addresses is specified, then we'll also populate that + // now as well. + payReq.PaymentAddr.WhenSome(func(addr [32]byte) { + copy(probeRequest.PaymentAddr, addr[:]) + }) + hints := payReq.RouteHints // If the hints don't indicate an LSP then chances are that our probe @@ -1453,12 +1458,12 @@ func (s *Server) BuildRoute(_ context.Context, outgoingChan = &req.OutgoingChanId } - var payAddr *[32]byte + var payAddr fn.Option[[32]byte] if len(req.PaymentAddr) != 0 { var backingPayAddr [32]byte copy(backingPayAddr[:], req.PaymentAddr) - payAddr = &backingPayAddr + payAddr = fn.Some(backingPayAddr) } if req.FinalCltvDelta == 0 { diff --git a/routing/integrated_routing_context_test.go b/routing/integrated_routing_context_test.go index 785fa1a50a..061dcfc3aa 100644 --- a/routing/integrated_routing_context_test.go +++ b/routing/integrated_routing_context_test.go @@ -187,7 +187,7 @@ func (c *integratedRoutingContext) testPayment(maxParts uint32, FinalCLTVDelta: uint16(c.finalExpiry), FeeLimit: lnwire.MaxMilliSatoshi, Target: c.target.pubkey, - PaymentAddr: &paymentAddr, + PaymentAddr: fn.Some(paymentAddr), DestFeatures: lnwire.NewFeatureVector( baseFeatureBits, lnwire.Features, ), diff --git a/routing/pathfind.go b/routing/pathfind.go index 6f394ea581..43eae71036 100644 --- a/routing/pathfind.go +++ b/routing/pathfind.go @@ -111,7 +111,7 @@ type finalHopParams struct { cltvDelta uint16 records record.CustomSet - paymentAddr *[32]byte + paymentAddr fn.Option[[32]byte] // metadata is additional data that is sent along with the payment to // the payee. @@ -226,7 +226,7 @@ func newRoute(sourceVertex route.Vertex, // If we're attaching a payment addr but the receiver // doesn't support both TLV and payment addrs, fail. payAddr := supports(lnwire.PaymentAddrOptional) - if !payAddr && finalHop.paymentAddr != nil { + if !payAddr && finalHop.paymentAddr.IsSome() { return nil, errors.New("cannot attach " + "payment addr") } @@ -234,12 +234,9 @@ func newRoute(sourceVertex route.Vertex, // Otherwise attach the mpp record if it exists. // TODO(halseth): move this to payment life cycle, // where AMP options are set. - if finalHop.paymentAddr != nil { - mpp = record.NewMPP( - finalHop.totalAmt, - *finalHop.paymentAddr, - ) - } + finalHop.paymentAddr.WhenSome(func(addr [32]byte) { + mpp = record.NewMPP(finalHop.totalAmt, addr) + }) metadata = finalHop.metadata @@ -452,7 +449,7 @@ type RestrictParams struct { // PaymentAddr is a random 32-byte value generated by the receiver to // mitigate probing vectors and payment sniping attacks on overpaid // invoices. - PaymentAddr *[32]byte + PaymentAddr fn.Option[[32]byte] // Amp signals to the pathfinder that this payment is an AMP payment // and therefore it needs to account for additional AMP data in the @@ -608,7 +605,7 @@ func findPath(g *graphParams, r *RestrictParams, cfg *PathFindingConfig, // checking that it supports the features we need. If the caller has a // payment address to attach, check that our destination feature vector // supports them. - if r.PaymentAddr != nil && + if r.PaymentAddr.IsSome() && !features.HasFeature(lnwire.PaymentAddrOptional) { return nil, 0, errNoPaymentAddr @@ -1435,9 +1432,9 @@ func lastHopPayloadSize(r *RestrictParams, finalHtlcExpiry int32, } var mpp *record.MPP - if r.PaymentAddr != nil { - mpp = record.NewMPP(amount, *r.PaymentAddr) - } + r.PaymentAddr.WhenSome(func(addr [32]byte) { + mpp = record.NewMPP(amount, addr) + }) var amp *record.AMP if r.Amp != nil { diff --git a/routing/pathfind_test.go b/routing/pathfind_test.go index 38942a31d2..72f71600dd 100644 --- a/routing/pathfind_test.go +++ b/routing/pathfind_test.go @@ -1367,7 +1367,7 @@ func TestNewRoute(t *testing.T) { // overwrite the final hop's feature vector in the graph. destFeatures *lnwire.FeatureVector - paymentAddr *[32]byte + paymentAddr fn.Option[[32]byte] // metadata is the payment metadata to attach to the route. metadata []byte @@ -1446,7 +1446,7 @@ func TestNewRoute(t *testing.T) { // a fee to receive the payment. name: "two hop single shot mpp", destFeatures: tlvPayAddrFeatures, - paymentAddr: &testPaymentAddr, + paymentAddr: fn.Some(testPaymentAddr), paymentAmount: 100000, hops: []*models.CachedEdgePolicy{ createHop(0, 1000, 1000000, 10), @@ -1911,7 +1911,7 @@ func runDestPaymentAddr(t *testing.T, useCache bool) { luoji := ctx.keyFromAlias("luoji") // Add payment address w/o any invoice features. - ctx.restrictParams.PaymentAddr = &[32]byte{1} + ctx.restrictParams.PaymentAddr = fn.Some([32]byte{1}) // Add empty destination features. This should cause us to fail, since // this overrides anything in the graph. @@ -2955,7 +2955,7 @@ func runInboundFees(t *testing.T, useCache bool) { ctx := newPathFindingTestContext(t, useCache, testChannels, "a") payAddr := [32]byte{1} - ctx.restrictParams.PaymentAddr = &payAddr + ctx.restrictParams.PaymentAddr = fn.Some(payAddr) ctx.restrictParams.DestFeatures = tlvPayAddrFeatures const ( @@ -2974,7 +2974,7 @@ func runInboundFees(t *testing.T, useCache bool) { amt: paymentAmt, cltvDelta: finalHopCLTV, records: nil, - paymentAddr: &payAddr, + paymentAddr: fn.Some(payAddr), totalAmt: paymentAmt, }, nil, @@ -3469,7 +3469,7 @@ func TestLastHopPayloadSize(t *testing.T) { { name: "Non blinded final hop", restrictions: &RestrictParams{ - PaymentAddr: paymentAddr, + PaymentAddr: fn.Some(*paymentAddr), DestCustomRecords: customRecords, Metadata: metadata, Amp: ampOptions, @@ -3501,12 +3501,10 @@ func TestLastHopPayloadSize(t *testing.T) { t.Run(tc.name, func(t *testing.T) { t.Parallel() - var mpp *record.MPP - if tc.restrictions.PaymentAddr != nil { - mpp = record.NewMPP( - tc.amount, *tc.restrictions.PaymentAddr, - ) - } + mpp := fn.MapOptionZ(tc.restrictions.PaymentAddr, + func(addr [32]byte) *record.MPP { + return record.NewMPP(tc.amount, addr) + }) // In case it's an AMP payment we use the max AMP record // size to estimate the final hop size. diff --git a/routing/payment_session.go b/routing/payment_session.go index 5e6a468fe1..3bd718e9d8 100644 --- a/routing/payment_session.go +++ b/routing/payment_session.go @@ -344,7 +344,7 @@ func (p *paymentSession) RequestRoute(maxAmt, feeLimit lnwire.MilliSatoshi, // record. If it has a blinded path though, then we // can split. Split payments to blinded paths won't have // MPP records. - if p.payment.PaymentAddr == nil && + if p.payment.PaymentAddr.IsNone() && p.payment.BlindedPathSet == nil { p.log.Debugf("not splitting because payment " + diff --git a/routing/router.go b/routing/router.go index db3fbc8573..04096fa67f 100644 --- a/routing/router.go +++ b/routing/router.go @@ -862,7 +862,7 @@ type LightningPayment struct { // PaymentAddr is the payment address specified by the receiver. This // field should be a random 32-byte nonce presented in the receiver's // invoice to prevent probing of the destination. - PaymentAddr *[32]byte + PaymentAddr fn.Option[[32]byte] // PaymentRequest is an optional payment request that this payment is // attempting to complete. @@ -1063,9 +1063,10 @@ func (r *ChannelRouter) PreparePayment(payment *LightningPayment) ( switch { // If this is an AMP payment, we'll use the AMP shard tracker. case payment.amp != nil: + addr := payment.PaymentAddr.UnwrapOr([32]byte{}) shardTracker = amp.NewShardTracker( - payment.amp.RootShare, payment.amp.SetID, - *payment.PaymentAddr, payment.Amount, + payment.amp.RootShare, payment.amp.SetID, addr, + payment.Amount, ) // Otherwise we'll use the simple tracker that will map each attempt to @@ -1367,8 +1368,8 @@ func (e ErrNoChannel) Error() string { // outgoing channel, use the outgoingChan parameter. func (r *ChannelRouter) BuildRoute(amt fn.Option[lnwire.MilliSatoshi], hops []route.Vertex, outgoingChan *uint64, finalCltvDelta int32, - payAddr *[32]byte, firstHopBlob fn.Option[[]byte]) (*route.Route, - error) { + payAddr fn.Option[[32]byte], firstHopBlob fn.Option[[]byte]) ( + *route.Route, error) { log.Tracef("BuildRoute called: hopsCount=%v, amt=%v", len(hops), amt) diff --git a/routing/router_test.go b/routing/router_test.go index 7726091fcd..53c49f1cfc 100644 --- a/routing/router_test.go +++ b/routing/router_test.go @@ -1653,14 +1653,14 @@ func TestBuildRoute(t *testing.T) { // Test that we can't build a route when no hops are given. hops = []route.Vertex{} _, err = ctx.router.BuildRoute( - noAmt, hops, nil, 40, nil, fn.None[[]byte](), + noAmt, hops, nil, 40, fn.None[[32]byte](), fn.None[[]byte](), ) require.Error(t, err) // Create hop list for an unknown destination. hops := []route.Vertex{ctx.aliases["b"], ctx.aliases["y"]} _, err = ctx.router.BuildRoute( - noAmt, hops, nil, 40, &payAddr, fn.None[[]byte](), + noAmt, hops, nil, 40, fn.Some(payAddr), fn.None[[]byte](), ) noChanErr := ErrNoChannel{} require.ErrorAs(t, err, &noChanErr) @@ -1672,7 +1672,8 @@ func TestBuildRoute(t *testing.T) { // Build the route for the given amount. rt, err := ctx.router.BuildRoute( - fn.Some(amt), hops, nil, 40, &payAddr, fn.None[[]byte](), + fn.Some(amt), hops, nil, 40, fn.Some(payAddr), + fn.None[[]byte](), ) require.NoError(t, err) @@ -1684,7 +1685,7 @@ func TestBuildRoute(t *testing.T) { // Build the route for the minimum amount. rt, err = ctx.router.BuildRoute( - noAmt, hops, nil, 40, &payAddr, fn.None[[]byte](), + noAmt, hops, nil, 40, fn.Some(payAddr), fn.None[[]byte](), ) require.NoError(t, err) @@ -1702,7 +1703,7 @@ func TestBuildRoute(t *testing.T) { // There is no amount that can pass through both channel 5 and 4. hops = []route.Vertex{ctx.aliases["e"], ctx.aliases["c"]} _, err = ctx.router.BuildRoute( - noAmt, hops, nil, 40, nil, fn.None[[]byte](), + noAmt, hops, nil, 40, fn.None[[32]byte](), fn.None[[]byte](), ) require.Error(t, err) noChanErr = ErrNoChannel{} @@ -1722,7 +1723,7 @@ func TestBuildRoute(t *testing.T) { // policy of channel 3. hops = []route.Vertex{ctx.aliases["b"], ctx.aliases["z"]} rt, err = ctx.router.BuildRoute( - noAmt, hops, nil, 40, &payAddr, fn.None[[]byte](), + noAmt, hops, nil, 40, fn.Some(payAddr), fn.None[[]byte](), ) require.NoError(t, err) checkHops(rt, []uint64{1, 8}, payAddr) @@ -1736,7 +1737,8 @@ func TestBuildRoute(t *testing.T) { hops = []route.Vertex{ctx.aliases["d"], ctx.aliases["f"]} amt = lnwire.NewMSatFromSatoshis(100) rt, err = ctx.router.BuildRoute( - fn.Some(amt), hops, nil, 40, &payAddr, fn.None[[]byte](), + fn.Some(amt), hops, nil, 40, fn.Some(payAddr), + fn.None[[]byte](), ) require.NoError(t, err) checkHops(rt, []uint64{9, 10}, payAddr) @@ -1752,7 +1754,7 @@ func TestBuildRoute(t *testing.T) { // is a third pass through newRoute in which this gets corrected to end hops = []route.Vertex{ctx.aliases["d"], ctx.aliases["f"]} rt, err = ctx.router.BuildRoute( - noAmt, hops, nil, 40, &payAddr, fn.None[[]byte](), + noAmt, hops, nil, 40, fn.Some(payAddr), fn.None[[]byte](), ) require.NoError(t, err) checkHops(rt, []uint64{9, 10}, payAddr) diff --git a/rpcserver.go b/rpcserver.go index ea75ff6beb..ea60380eff 100644 --- a/rpcserver.go +++ b/rpcserver.go @@ -5328,7 +5328,7 @@ type rpcPaymentIntent struct { outgoingChannelIDs []uint64 lastHop *route.Vertex destFeatures *lnwire.FeatureVector - paymentAddr *[32]byte + paymentAddr fn.Option[[32]byte] payReq []byte metadata []byte blindedPathSet *routing.BlindedPaymentPathSet @@ -5537,8 +5537,9 @@ func (r *rpcServer) extractPaymentIntent(rpcPayReq *rpcPaymentRequest) (rpcPayme // Note that the payment address for the payIntent should be nil if none // was provided with the rpcPaymentRequest. if len(rpcPayReq.PaymentAddr) != 0 { - payIntent.paymentAddr = &[32]byte{} - copy(payIntent.paymentAddr[:], rpcPayReq.PaymentAddr) + var addr [32]byte + copy(addr[:], rpcPayReq.PaymentAddr) + payIntent.paymentAddr = fn.Some(addr) } // Otherwise, If the payment request field was not specified @@ -7384,10 +7385,7 @@ func (r *rpcServer) DecodePayReq(ctx context.Context, } // Extract the payment address from the payment request, if present. - var paymentAddr []byte - if payReq.PaymentAddr != nil { - paymentAddr = payReq.PaymentAddr[:] - } + paymentAddr := payReq.PaymentAddr.UnwrapOr([32]byte{}) dest := payReq.Destination.SerializeCompressed() return &lnrpc.PayReq{ @@ -7403,7 +7401,7 @@ func (r *rpcServer) DecodePayReq(ctx context.Context, CltvExpiry: int64(payReq.MinFinalCLTVExpiry()), RouteHints: routeHints, BlindedPaths: blindedPaymentPaths, - PaymentAddr: paymentAddr, + PaymentAddr: paymentAddr[:], Features: invoicesrpc.CreateRPCFeatures(payReq.Features), }, nil } diff --git a/zpay32/decode.go b/zpay32/decode.go index fcfc7edfef..61099cf2f0 100644 --- a/zpay32/decode.go +++ b/zpay32/decode.go @@ -14,6 +14,7 @@ import ( "github.com/btcsuite/btcd/btcutil/bech32" "github.com/btcsuite/btcd/chaincfg" "github.com/btcsuite/btcd/chaincfg/chainhash" + "github.com/lightningnetwork/lnd/fn" "github.com/lightningnetwork/lnd/lnwire" ) @@ -278,13 +279,19 @@ func parseTaggedFields(invoice *Invoice, fields []byte, net *chaincfg.Params) er invoice.PaymentHash, err = parse32Bytes(base32Data) case fieldTypeS: - if invoice.PaymentAddr != nil { + if invoice.PaymentAddr.IsSome() { // We skip the field if we have already seen a // supported one. continue } - invoice.PaymentAddr, err = parse32Bytes(base32Data) + addr, err := parse32Bytes(base32Data) + if err != nil { + return err + } + if addr != nil { + invoice.PaymentAddr = fn.Some(*addr) + } case fieldTypeD: if invoice.Description != nil { diff --git a/zpay32/encode.go b/zpay32/encode.go index 130abdcfd5..3e2d799776 100644 --- a/zpay32/encode.go +++ b/zpay32/encode.go @@ -9,6 +9,7 @@ import ( "github.com/btcsuite/btcd/btcutil/bech32" "github.com/btcsuite/btcd/chaincfg" "github.com/btcsuite/btcd/chaincfg/chainhash" + "github.com/lightningnetwork/lnd/fn" "github.com/lightningnetwork/lnd/lnwire" ) @@ -301,14 +302,14 @@ func writeTaggedFields(bufferBase32 *bytes.Buffer, invoice *Invoice) error { return err } } - if invoice.PaymentAddr != nil { - err := writeBytes32( - bufferBase32, fieldTypeS, *invoice.PaymentAddr, - ) - if err != nil { - return err - } + + err := fn.MapOptionZ(invoice.PaymentAddr, func(addr [32]byte) error { + return writeBytes32(bufferBase32, fieldTypeS, addr) + }) + if err != nil { + return err } + if invoice.Features.SerializeSize32() > 0 { var b bytes.Buffer err := invoice.Features.RawFeatureVector.EncodeBase32(&b) diff --git a/zpay32/invoice.go b/zpay32/invoice.go index 8b1457ab43..7c18253eb0 100644 --- a/zpay32/invoice.go +++ b/zpay32/invoice.go @@ -8,6 +8,7 @@ import ( "github.com/btcsuite/btcd/btcec/v2" "github.com/btcsuite/btcd/btcutil" "github.com/btcsuite/btcd/chaincfg" + "github.com/lightningnetwork/lnd/fn" "github.com/lightningnetwork/lnd/lnwire" ) @@ -136,7 +137,7 @@ type Invoice struct { // PaymentAddr is the payment address to be used by payments to prevent // probing of the destination. - PaymentAddr *[32]byte + PaymentAddr fn.Option[[32]byte] // Destination is the public key of the target node. This will always // be set after decoding, and can optionally be set before encoding to @@ -301,7 +302,7 @@ func Features(features *lnwire.FeatureVector) func(*Invoice) { // the desired payment address that is advertised on the invoice. func PaymentAddr(addr [32]byte) func(*Invoice) { return func(i *Invoice) { - i.PaymentAddr = &addr + i.PaymentAddr = fn.Some(addr) } } diff --git a/zpay32/invoice_test.go b/zpay32/invoice_test.go index 6a726daf37..d982a14a2b 100644 --- a/zpay32/invoice_test.go +++ b/zpay32/invoice_test.go @@ -17,6 +17,7 @@ import ( "github.com/btcsuite/btcd/chaincfg" "github.com/btcsuite/btcd/chaincfg/chainhash" sphinx "github.com/lightningnetwork/lightning-onion" + "github.com/lightningnetwork/lnd/fn" "github.com/lightningnetwork/lnd/lnwire" "github.com/stretchr/testify/require" ) @@ -562,7 +563,7 @@ func TestDecodeEncode(t *testing.T) { MilliSat: &testMillisat25mBTC, Timestamp: time.Unix(1496314658, 0), PaymentHash: &testPaymentHash, - PaymentAddr: &specPaymentAddr, + PaymentAddr: fn.Some(specPaymentAddr), Description: &testCoffeeBeans, Destination: testPubKey, Features: lnwire.NewFeatureVector( @@ -589,7 +590,7 @@ func TestDecodeEncode(t *testing.T) { MilliSat: &testMillisat25mBTC, Timestamp: time.Unix(1496314658, 0), PaymentHash: &testPaymentHash, - PaymentAddr: &specPaymentAddr, + PaymentAddr: fn.Some(specPaymentAddr), Description: &testCoffeeBeans, Destination: testPubKey, Features: lnwire.NewFeatureVector( @@ -718,7 +719,7 @@ func TestDecodeEncode(t *testing.T) { PaymentHash: &testPaymentHash, Description: &testPaymentMetadata, Destination: testPubKey, - PaymentAddr: &specPaymentAddr, + PaymentAddr: fn.Some(specPaymentAddr), Features: lnwire.NewFeatureVector( lnwire.NewRawFeatureVector(8, 14, 48), lnwire.Features, @@ -772,7 +773,7 @@ func TestDecodeEncode(t *testing.T) { MilliSat: &testMillisat25mBTC, Timestamp: time.Unix(1496314658, 0), PaymentHash: &testPaymentHash, - PaymentAddr: &specPaymentAddr, + PaymentAddr: fn.Some(specPaymentAddr), Description: &testCoffeeBeans, Destination: testPubKey, Features: lnwire.NewFeatureVector( @@ -803,7 +804,7 @@ func TestDecodeEncode(t *testing.T) { MilliSat: &testMillisat25mBTC, Timestamp: time.Unix(1496314658, 0), PaymentHash: &testPaymentHash, - PaymentAddr: &specPaymentAddr, + PaymentAddr: fn.Some(specPaymentAddr), Description: &testCoffeeBeans, Destination: testPubKey, Features: lnwire.NewFeatureVector( From 575d699917f8dc588b7a56f32458681cf9fc482a Mon Sep 17 00:00:00 2001 From: Elle Mouton Date: Tue, 24 Sep 2024 14:44:09 +0900 Subject: [PATCH 139/218] invoices: init quit channel of modifier Also add atomic start and stop vars to prevent close of a closed channel. --- invoices/modification_interceptor.go | 12 ++++++++++++ server.go | 4 ++++ 2 files changed, 16 insertions(+) diff --git a/invoices/modification_interceptor.go b/invoices/modification_interceptor.go index e2d9a90513..47f0de80ba 100644 --- a/invoices/modification_interceptor.go +++ b/invoices/modification_interceptor.go @@ -66,6 +66,9 @@ func (s *safeCallback) Exec(req HtlcModifyRequest) (*HtlcModifyResponse, // settle an invoice, enabling a subscribed client to modify certain aspects of // those HTLCs. type HtlcModificationInterceptor struct { + started atomic.Bool + stopped atomic.Bool + // callback is the wrapped client callback function that is called when // an invoice is intercepted. This function gives the client the ability // to determine how the invoice should be settled. @@ -79,6 +82,7 @@ type HtlcModificationInterceptor struct { func NewHtlcModificationInterceptor() *HtlcModificationInterceptor { return &HtlcModificationInterceptor{ callback: &safeCallback{}, + quit: make(chan struct{}), } } @@ -163,11 +167,19 @@ func (s *HtlcModificationInterceptor) RegisterInterceptor( // Start starts the service. func (s *HtlcModificationInterceptor) Start() error { + if !s.started.CompareAndSwap(false, true) { + return nil + } + return nil } // Stop stops the service. func (s *HtlcModificationInterceptor) Stop() error { + if !s.stopped.CompareAndSwap(false, true) { + return nil + } + close(s.quit) return nil diff --git a/server.go b/server.go index 20506c059a..3eceaa9890 100644 --- a/server.go +++ b/server.go @@ -2380,6 +2380,10 @@ func (s *server) Stop() error { if err := s.invoices.Stop(); err != nil { srvrLog.Warnf("failed to stop invoices: %v", err) } + if err := s.invoiceHtlcModifier.Stop(); err != nil { + srvrLog.Warnf("failed to stop htlc invoices "+ + "modifier: %v", err) + } if err := s.chanRouter.Stop(); err != nil { srvrLog.Warnf("failed to stop chanRouter: %v", err) } From 6df4f279292b803d73beec480566c30bc92adcb8 Mon Sep 17 00:00:00 2001 From: Elle Mouton Date: Tue, 24 Sep 2024 14:54:59 +0900 Subject: [PATCH 140/218] server: stop interceptable switch in Stop --- server.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/server.go b/server.go index 3eceaa9890..8fe89bbe2f 100644 --- a/server.go +++ b/server.go @@ -2380,6 +2380,10 @@ func (s *server) Stop() error { if err := s.invoices.Stop(); err != nil { srvrLog.Warnf("failed to stop invoices: %v", err) } + if err := s.interceptableSwitch.Stop(); err != nil { + srvrLog.Warnf("failed to stop interceptable "+ + "switch: %v", err) + } if err := s.invoiceHtlcModifier.Stop(); err != nil { srvrLog.Warnf("failed to stop htlc invoices "+ "modifier: %v", err) From 663f4c251e65f7dbf67483b2ed1db35f32eb25e2 Mon Sep 17 00:00:00 2001 From: Elle Mouton Date: Wed, 25 Sep 2024 11:38:42 +0900 Subject: [PATCH 141/218] feature: remove b11 feature bit from default invoice set --- feature/default_sets.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/feature/default_sets.go b/feature/default_sets.go index 447870b277..cc802fe859 100644 --- a/feature/default_sets.go +++ b/feature/default_sets.go @@ -92,7 +92,4 @@ var defaultSetDesc = setDesc{ SetInit: {}, // I SetNodeAnn: {}, // N }, - lnwire.Bolt11BlindedPathsOptional: { - SetInvoice: {}, // I - }, } From 9d50e273a37d8d317f2e46e5bf696ed0aa4d9e4c Mon Sep 17 00:00:00 2001 From: George Tsagkarelis Date: Thu, 12 Sep 2024 14:39:51 +0200 Subject: [PATCH 142/218] routing: add htlcAmt to PaymentBandwidth method of TlvTrafficShaper This commit was added to the 0-19-staging branch recently and therefore didn't make it into a previous part yet. So it's unrelated to the changes in this part but is required for the whole custom channel saga. --- routing/bandwidth.go | 4 +++- routing/bandwidth_test.go | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/routing/bandwidth.go b/routing/bandwidth.go index a193c654ad..3b80dadc7c 100644 --- a/routing/bandwidth.go +++ b/routing/bandwidth.go @@ -47,7 +47,8 @@ type TlvTrafficShaper interface { // is a custom channel that should be handled by the traffic shaper, the // HandleTraffic method should be called first. PaymentBandwidth(htlcBlob, commitmentBlob fn.Option[tlv.Blob], - linkBandwidth lnwire.MilliSatoshi) (lnwire.MilliSatoshi, error) + linkBandwidth, + htlcAmt lnwire.MilliSatoshi) (lnwire.MilliSatoshi, error) } // AuxHtlcModifier is an interface that allows the sender to modify the outgoing @@ -191,6 +192,7 @@ func (b *bandwidthManager) getBandwidth(cid lnwire.ShortChannelID, commitmentBlob := link.CommitmentCustomBlob() auxBandwidth, err := ts.PaymentBandwidth( b.firstHopBlob, commitmentBlob, linkBandwidth, + amount, ) if err != nil { return bandwidthErr(fmt.Errorf("failed to get "+ diff --git a/routing/bandwidth_test.go b/routing/bandwidth_test.go index 4876f09c70..4872b5a7ec 100644 --- a/routing/bandwidth_test.go +++ b/routing/bandwidth_test.go @@ -148,7 +148,7 @@ func (*mockTrafficShaper) ShouldHandleTraffic(_ lnwire.ShortChannelID, // is a custom channel that should be handled by the traffic shaper, the // HandleTraffic method should be called first. func (*mockTrafficShaper) PaymentBandwidth(_, _ fn.Option[tlv.Blob], - linkBandwidth lnwire.MilliSatoshi) (lnwire.MilliSatoshi, error) { + linkBandwidth, _ lnwire.MilliSatoshi) (lnwire.MilliSatoshi, error) { return linkBandwidth, nil } From 035126b28016071fe88f7495c07f8af06d887979 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Thu, 29 Aug 2024 20:51:09 -0500 Subject: [PATCH 143/218] lnwire: modify TestLightningWireProtocol to use sub-tests This way, it's possible to run induvidual tests to target failures. --- lnwire/lnwire_test.go | 34 ++++++++++++++++++++-------------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/lnwire/lnwire_test.go b/lnwire/lnwire_test.go index 828ddec67a..0502129dc2 100644 --- a/lnwire/lnwire_test.go +++ b/lnwire/lnwire_test.go @@ -1700,22 +1700,28 @@ func TestLightningWireProtocol(t *testing.T) { }, } for _, test := range tests { - var config *quick.Config - - // If the type defined is within the custom type gen map above, - // then we'll modify the default config to use this Value - // function that knows how to generate the proper types. - if valueGen, ok := customTypeGen[test.msgType]; ok { - config = &quick.Config{ - Values: valueGen, + t.Run(test.msgType.String(), func(t *testing.T) { + var config *quick.Config + + // If the type defined is within the custom type gen + // map above, then we'll modify the default config to + // use this Value function that knows how to generate + // the proper types. + if valueGen, ok := customTypeGen[test.msgType]; ok { + config = &quick.Config{ + Values: valueGen, + } } - } - t.Logf("Running fuzz tests for msgType=%v", test.msgType) - if err := quick.Check(test.scenario, config); err != nil { - t.Fatalf("fuzz checks for msg=%v failed: %v", - test.msgType, err) - } + t.Logf("Running fuzz tests for msgType=%v", + test.msgType) + + err := quick.Check(test.scenario, config) + if err != nil { + t.Fatalf("fuzz checks for msg=%v failed: %v", + test.msgType, err) + } + }) } } From 900a0a114a53703722d5442fb71a5c0c83e32397 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Mon, 3 Jun 2024 22:55:50 -0700 Subject: [PATCH 144/218] contractcourt: convert taprootBriefcase to use new tlv record type This commit doesn't yet go all the way to modify all the other records quite yet. --- contractcourt/breach_arbitrator.go | 17 ++-- contractcourt/briefcase.go | 32 +++--- contractcourt/taproot_briefcase.go | 126 +++++++++++------------- contractcourt/taproot_briefcase_test.go | 9 +- 4 files changed, 88 insertions(+), 96 deletions(-) diff --git a/contractcourt/breach_arbitrator.go b/contractcourt/breach_arbitrator.go index e3dd85ea6f..9617c1ebee 100644 --- a/contractcourt/breach_arbitrator.go +++ b/contractcourt/breach_arbitrator.go @@ -1622,13 +1622,13 @@ func taprootBriefcaseFromRetInfo(retInfo *retributionInfo) *taprootBriefcase { // commitment, we'll need to stash the control block. case input.TaprootRemoteCommitSpend: //nolint:lll - tapCase.CtrlBlocks.CommitSweepCtrlBlock = bo.signDesc.ControlBlock + tapCase.CtrlBlocks.Val.CommitSweepCtrlBlock = bo.signDesc.ControlBlock // To spend the revoked output again, we'll store the same // control block value as above, but in a different place. case input.TaprootCommitmentRevoke: //nolint:lll - tapCase.CtrlBlocks.RevokeSweepCtrlBlock = bo.signDesc.ControlBlock + tapCase.CtrlBlocks.Val.RevokeSweepCtrlBlock = bo.signDesc.ControlBlock // For spending the HTLC outputs, we'll store the first and // second level tweak values. @@ -1642,10 +1642,10 @@ func taprootBriefcaseFromRetInfo(retInfo *retributionInfo) *taprootBriefcase { secondLevelTweak := bo.secondLevelTapTweak //nolint:lll - tapCase.TapTweaks.BreachedHtlcTweaks[resID] = firstLevelTweak + tapCase.TapTweaks.Val.BreachedHtlcTweaks[resID] = firstLevelTweak //nolint:lll - tapCase.TapTweaks.BreachedSecondLevelHltcTweaks[resID] = secondLevelTweak + tapCase.TapTweaks.Val.BreachedSecondLevelHltcTweaks[resID] = secondLevelTweak } } @@ -1665,13 +1665,13 @@ func applyTaprootRetInfo(tapCase *taprootBriefcase, // commitment, we'll apply the control block. case input.TaprootRemoteCommitSpend: //nolint:lll - bo.signDesc.ControlBlock = tapCase.CtrlBlocks.CommitSweepCtrlBlock + bo.signDesc.ControlBlock = tapCase.CtrlBlocks.Val.CommitSweepCtrlBlock // To spend the revoked output again, we'll apply the same // control block value as above, but to a different place. case input.TaprootCommitmentRevoke: //nolint:lll - bo.signDesc.ControlBlock = tapCase.CtrlBlocks.RevokeSweepCtrlBlock + bo.signDesc.ControlBlock = tapCase.CtrlBlocks.Val.RevokeSweepCtrlBlock // For spending the HTLC outputs, we'll apply the first and // second level tweak values. @@ -1680,7 +1680,8 @@ func applyTaprootRetInfo(tapCase *taprootBriefcase, case input.TaprootHtlcOfferedRevoke: resID := newResolverID(bo.OutPoint()) - tap1, ok := tapCase.TapTweaks.BreachedHtlcTweaks[resID] + //nolint:lll + tap1, ok := tapCase.TapTweaks.Val.BreachedHtlcTweaks[resID] if !ok { return fmt.Errorf("unable to find taproot "+ "tweak for: %v", bo.OutPoint()) @@ -1688,7 +1689,7 @@ func applyTaprootRetInfo(tapCase *taprootBriefcase, bo.signDesc.TapTweak = tap1[:] //nolint:lll - tap2, ok := tapCase.TapTweaks.BreachedSecondLevelHltcTweaks[resID] + tap2, ok := tapCase.TapTweaks.Val.BreachedSecondLevelHltcTweaks[resID] if !ok { return fmt.Errorf("unable to find taproot "+ "tweak for: %v", bo.OutPoint()) diff --git a/contractcourt/briefcase.go b/contractcourt/briefcase.go index 95f6a933d7..2132da6deb 100644 --- a/contractcourt/briefcase.go +++ b/contractcourt/briefcase.go @@ -1553,7 +1553,7 @@ func encodeTaprootAuxData(w io.Writer, c *ContractResolutions) error { commitResolution := c.CommitResolution commitSignDesc := commitResolution.SelfOutputSignDesc //nolint:lll - tapCase.CtrlBlocks.CommitSweepCtrlBlock = commitSignDesc.ControlBlock + tapCase.CtrlBlocks.Val.CommitSweepCtrlBlock = commitSignDesc.ControlBlock } for _, htlc := range c.HtlcResolutions.IncomingHTLCs { @@ -1571,7 +1571,7 @@ func encodeTaprootAuxData(w io.Writer, c *ContractResolutions) error { htlc.SignedSuccessTx.TxIn[0].PreviousOutPoint, ) //nolint:lll - tapCase.CtrlBlocks.SecondLevelCtrlBlocks[resID] = ctrlBlock + tapCase.CtrlBlocks.Val.SecondLevelCtrlBlocks[resID] = ctrlBlock // For HTLCs we need to go to the second level for, we // also need to store the control block needed to @@ -1580,12 +1580,12 @@ func encodeTaprootAuxData(w io.Writer, c *ContractResolutions) error { //nolint:lll bridgeCtrlBlock := htlc.SignDetails.SignDesc.ControlBlock //nolint:lll - tapCase.CtrlBlocks.IncomingHtlcCtrlBlocks[resID] = bridgeCtrlBlock + tapCase.CtrlBlocks.Val.IncomingHtlcCtrlBlocks[resID] = bridgeCtrlBlock } } else { resID := newResolverID(htlc.ClaimOutpoint) //nolint:lll - tapCase.CtrlBlocks.IncomingHtlcCtrlBlocks[resID] = ctrlBlock + tapCase.CtrlBlocks.Val.IncomingHtlcCtrlBlocks[resID] = ctrlBlock } } for _, htlc := range c.HtlcResolutions.OutgoingHTLCs { @@ -1603,7 +1603,7 @@ func encodeTaprootAuxData(w io.Writer, c *ContractResolutions) error { htlc.SignedTimeoutTx.TxIn[0].PreviousOutPoint, ) //nolint:lll - tapCase.CtrlBlocks.SecondLevelCtrlBlocks[resID] = ctrlBlock + tapCase.CtrlBlocks.Val.SecondLevelCtrlBlocks[resID] = ctrlBlock // For HTLCs we need to go to the second level for, we // also need to store the control block needed to @@ -1614,18 +1614,18 @@ func encodeTaprootAuxData(w io.Writer, c *ContractResolutions) error { //nolint:lll bridgeCtrlBlock := htlc.SignDetails.SignDesc.ControlBlock //nolint:lll - tapCase.CtrlBlocks.OutgoingHtlcCtrlBlocks[resID] = bridgeCtrlBlock + tapCase.CtrlBlocks.Val.OutgoingHtlcCtrlBlocks[resID] = bridgeCtrlBlock } } else { resID := newResolverID(htlc.ClaimOutpoint) //nolint:lll - tapCase.CtrlBlocks.OutgoingHtlcCtrlBlocks[resID] = ctrlBlock + tapCase.CtrlBlocks.Val.OutgoingHtlcCtrlBlocks[resID] = ctrlBlock } } if c.AnchorResolution != nil { anchorSignDesc := c.AnchorResolution.AnchorSignDescriptor - tapCase.TapTweaks.AnchorTweak = anchorSignDesc.TapTweak + tapCase.TapTweaks.Val.AnchorTweak = anchorSignDesc.TapTweak } return tapCase.Encode(w) @@ -1639,7 +1639,7 @@ func decodeTapRootAuxData(r io.Reader, c *ContractResolutions) error { if c.CommitResolution != nil { c.CommitResolution.SelfOutputSignDesc.ControlBlock = - tapCase.CtrlBlocks.CommitSweepCtrlBlock + tapCase.CtrlBlocks.Val.CommitSweepCtrlBlock } for i := range c.HtlcResolutions.IncomingHTLCs { @@ -1652,19 +1652,19 @@ func decodeTapRootAuxData(r io.Reader, c *ContractResolutions) error { ) //nolint:lll - ctrlBlock := tapCase.CtrlBlocks.SecondLevelCtrlBlocks[resID] + ctrlBlock := tapCase.CtrlBlocks.Val.SecondLevelCtrlBlocks[resID] htlc.SweepSignDesc.ControlBlock = ctrlBlock //nolint:lll if htlc.SignDetails != nil { - bridgeCtrlBlock := tapCase.CtrlBlocks.IncomingHtlcCtrlBlocks[resID] + bridgeCtrlBlock := tapCase.CtrlBlocks.Val.IncomingHtlcCtrlBlocks[resID] htlc.SignDetails.SignDesc.ControlBlock = bridgeCtrlBlock } } else { resID = newResolverID(htlc.ClaimOutpoint) //nolint:lll - ctrlBlock := tapCase.CtrlBlocks.IncomingHtlcCtrlBlocks[resID] + ctrlBlock := tapCase.CtrlBlocks.Val.IncomingHtlcCtrlBlocks[resID] htlc.SweepSignDesc.ControlBlock = ctrlBlock } @@ -1680,19 +1680,19 @@ func decodeTapRootAuxData(r io.Reader, c *ContractResolutions) error { ) //nolint:lll - ctrlBlock := tapCase.CtrlBlocks.SecondLevelCtrlBlocks[resID] + ctrlBlock := tapCase.CtrlBlocks.Val.SecondLevelCtrlBlocks[resID] htlc.SweepSignDesc.ControlBlock = ctrlBlock //nolint:lll if htlc.SignDetails != nil { - bridgeCtrlBlock := tapCase.CtrlBlocks.OutgoingHtlcCtrlBlocks[resID] + bridgeCtrlBlock := tapCase.CtrlBlocks.Val.OutgoingHtlcCtrlBlocks[resID] htlc.SignDetails.SignDesc.ControlBlock = bridgeCtrlBlock } } else { resID = newResolverID(htlc.ClaimOutpoint) //nolint:lll - ctrlBlock := tapCase.CtrlBlocks.OutgoingHtlcCtrlBlocks[resID] + ctrlBlock := tapCase.CtrlBlocks.Val.OutgoingHtlcCtrlBlocks[resID] htlc.SweepSignDesc.ControlBlock = ctrlBlock } @@ -1701,7 +1701,7 @@ func decodeTapRootAuxData(r io.Reader, c *ContractResolutions) error { if c.AnchorResolution != nil { c.AnchorResolution.AnchorSignDescriptor.TapTweak = - tapCase.TapTweaks.AnchorTweak + tapCase.TapTweaks.Val.AnchorTweak } return nil diff --git a/contractcourt/taproot_briefcase.go b/contractcourt/taproot_briefcase.go index 5931a4556f..8544582c08 100644 --- a/contractcourt/taproot_briefcase.go +++ b/contractcourt/taproot_briefcase.go @@ -8,9 +8,6 @@ import ( ) const ( - taprootCtrlBlockType tlv.Type = 0 - taprootTapTweakType tlv.Type = 1 - commitCtrlBlockType tlv.Type = 0 revokeCtrlBlockType tlv.Type = 1 outgoingHtlcCtrlBlockType tlv.Type = 2 @@ -26,36 +23,39 @@ const ( // information we need to sweep taproot outputs. type taprootBriefcase struct { // CtrlBlock is the set of control block for the taproot outputs. - CtrlBlocks *ctrlBlocks + CtrlBlocks tlv.RecordT[tlv.TlvType0, ctrlBlocks] // TapTweaks is the set of taproot tweaks for the taproot outputs that // are to be spent via a keyspend path. This includes anchors, and any // revocation paths. - TapTweaks *tapTweaks + TapTweaks tlv.RecordT[tlv.TlvType1, tapTweaks] } +// TODO(roasbeef): morph into new tlv record + // newTaprootBriefcase returns a new instance of the taproot specific briefcase // variant. func newTaprootBriefcase() *taprootBriefcase { return &taprootBriefcase{ - CtrlBlocks: newCtrlBlocks(), - TapTweaks: newTapTweaks(), + CtrlBlocks: tlv.NewRecordT[tlv.TlvType0](newCtrlBlocks()), + TapTweaks: tlv.NewRecordT[tlv.TlvType1](newTapTweaks()), } } // EncodeRecords returns a slice of TLV records that should be encoded. func (t *taprootBriefcase) EncodeRecords() []tlv.Record { - return []tlv.Record{ - newCtrlBlocksRecord(&t.CtrlBlocks), - newTapTweaksRecord(&t.TapTweaks), + records := []tlv.Record{ + t.CtrlBlocks.Record(), + t.TapTweaks.Record(), } + return records } // DecodeRecords returns a slice of TLV records that should be decoded. func (t *taprootBriefcase) DecodeRecords() []tlv.Record { return []tlv.Record{ - newCtrlBlocksRecord(&t.CtrlBlocks), - newTapTweaksRecord(&t.TapTweaks), + t.CtrlBlocks.Record(), + t.TapTweaks.Record(), } } @@ -76,7 +76,12 @@ func (t *taprootBriefcase) Decode(r io.Reader) error { return err } - return stream.Decode(r) + _, err = stream.DecodeWithParsedTypes(r) + if err != nil { + return err + } + + return nil } // resolverCtrlBlocks is a map of resolver IDs to their corresponding control @@ -216,8 +221,8 @@ type ctrlBlocks struct { } // newCtrlBlocks returns a new instance of the ctrlBlocks struct. -func newCtrlBlocks() *ctrlBlocks { - return &ctrlBlocks{ +func newCtrlBlocks() ctrlBlocks { + return ctrlBlocks{ OutgoingHtlcCtrlBlocks: newResolverCtrlBlocks(), IncomingHtlcCtrlBlocks: newResolverCtrlBlocks(), SecondLevelCtrlBlocks: newResolverCtrlBlocks(), @@ -260,7 +265,7 @@ func varBytesDecoder(r io.Reader, val any, buf *[8]byte, l uint64) error { // ctrlBlockEncoder is a custom TLV encoder for the ctrlBlocks struct. func ctrlBlockEncoder(w io.Writer, val any, _ *[8]byte) error { - if t, ok := val.(**ctrlBlocks); ok { + if t, ok := val.(*ctrlBlocks); ok { return (*t).Encode(w) } @@ -269,7 +274,7 @@ func ctrlBlockEncoder(w io.Writer, val any, _ *[8]byte) error { // ctrlBlockDecoder is a custom TLV decoder for the ctrlBlocks struct. func ctrlBlockDecoder(r io.Reader, val any, _ *[8]byte, l uint64) error { - if typ, ok := val.(**ctrlBlocks); ok { + if typ, ok := val.(*ctrlBlocks); ok { ctrlReader := io.LimitReader(r, int64(l)) var ctrlBlocks ctrlBlocks @@ -278,7 +283,7 @@ func ctrlBlockDecoder(r io.Reader, val any, _ *[8]byte, l uint64) error { return err } - *typ = &ctrlBlocks + *typ = ctrlBlocks return nil } @@ -286,28 +291,6 @@ func ctrlBlockDecoder(r io.Reader, val any, _ *[8]byte, l uint64) error { return tlv.NewTypeForDecodingErr(val, "ctrlBlocks", l, l) } -// newCtrlBlocksRecord returns a new TLV record that can be used to -// encode/decode the set of cotrol blocks for the taproot outputs for a -// channel. -func newCtrlBlocksRecord(blks **ctrlBlocks) tlv.Record { - recordSize := func() uint64 { - var ( - b bytes.Buffer - buf [8]byte - ) - if err := ctrlBlockEncoder(&b, blks, &buf); err != nil { - panic(err) - } - - return uint64(len(b.Bytes())) - } - - return tlv.MakeDynamicRecord( - taprootCtrlBlockType, blks, recordSize, ctrlBlockEncoder, - ctrlBlockDecoder, - ) -} - // EncodeRecords returns the set of TLV records that encode the control block // for the commitment transaction. func (c *ctrlBlocks) EncodeRecords() []tlv.Record { @@ -382,7 +365,21 @@ func (c *ctrlBlocks) DecodeRecords() []tlv.Record { // Record returns a TLV record that can be used to encode/decode the control // blocks. type from a given TLV stream. func (c *ctrlBlocks) Record() tlv.Record { - return tlv.MakePrimitiveRecord(commitCtrlBlockType, c) + recordSize := func() uint64 { + var ( + b bytes.Buffer + buf [8]byte + ) + if err := ctrlBlockEncoder(&b, c, &buf); err != nil { + panic(err) + } + + return uint64(len(b.Bytes())) + } + + return tlv.MakeDynamicRecord( + 0, c, recordSize, ctrlBlockEncoder, ctrlBlockDecoder, + ) } // Encode encodes the set of control blocks. @@ -530,8 +527,8 @@ type tapTweaks struct { } // newTapTweaks returns a new tapTweaks struct. -func newTapTweaks() *tapTweaks { - return &tapTweaks{ +func newTapTweaks() tapTweaks { + return tapTweaks{ BreachedHtlcTweaks: make(htlcTapTweaks), BreachedSecondLevelHltcTweaks: make(htlcTapTweaks), } @@ -539,7 +536,7 @@ func newTapTweaks() *tapTweaks { // tapTweaksEncoder is a custom TLV encoder for the tapTweaks struct. func tapTweaksEncoder(w io.Writer, val any, _ *[8]byte) error { - if t, ok := val.(**tapTweaks); ok { + if t, ok := val.(*tapTweaks); ok { return (*t).Encode(w) } @@ -548,7 +545,7 @@ func tapTweaksEncoder(w io.Writer, val any, _ *[8]byte) error { // tapTweaksDecoder is a custom TLV decoder for the tapTweaks struct. func tapTweaksDecoder(r io.Reader, val any, _ *[8]byte, l uint64) error { - if typ, ok := val.(**tapTweaks); ok { + if typ, ok := val.(*tapTweaks); ok { tweakReader := io.LimitReader(r, int64(l)) var tapTweaks tapTweaks @@ -557,7 +554,7 @@ func tapTweaksDecoder(r io.Reader, val any, _ *[8]byte, l uint64) error { return err } - *typ = &tapTweaks + *typ = tapTweaks return nil } @@ -565,27 +562,6 @@ func tapTweaksDecoder(r io.Reader, val any, _ *[8]byte, l uint64) error { return tlv.NewTypeForDecodingErr(val, "tapTweaks", l, l) } -// newTapTweaksRecord returns a new TLV record that can be used to -// encode/decode the tap tweak structs. -func newTapTweaksRecord(tweaks **tapTweaks) tlv.Record { - recordSize := func() uint64 { - var ( - b bytes.Buffer - buf [8]byte - ) - if err := tapTweaksEncoder(&b, tweaks, &buf); err != nil { - panic(err) - } - - return uint64(len(b.Bytes())) - } - - return tlv.MakeDynamicRecord( - taprootTapTweakType, tweaks, recordSize, tapTweaksEncoder, - tapTweaksDecoder, - ) -} - // EncodeRecords returns the set of TLV records that encode the tweaks. func (t *tapTweaks) EncodeRecords() []tlv.Record { var records []tlv.Record @@ -637,7 +613,21 @@ func (t *tapTweaks) DecodeRecords() []tlv.Record { // Record returns a TLV record that can be used to encode/decode the tap // tweaks. func (t *tapTweaks) Record() tlv.Record { - return tlv.MakePrimitiveRecord(taprootTapTweakType, t) + recordSize := func() uint64 { + var ( + b bytes.Buffer + buf [8]byte + ) + if err := tapTweaksEncoder(&b, t, &buf); err != nil { + panic(err) + } + + return uint64(len(b.Bytes())) + } + + return tlv.MakeDynamicRecord( + 0, t, recordSize, tapTweaksEncoder, tapTweaksDecoder, + ) } // Encode encodes the set of tap tweaks. diff --git a/contractcourt/taproot_briefcase_test.go b/contractcourt/taproot_briefcase_test.go index 38471ed744..1b0d0b399b 100644 --- a/contractcourt/taproot_briefcase_test.go +++ b/contractcourt/taproot_briefcase_test.go @@ -5,6 +5,7 @@ import ( "math/rand" "testing" + "github.com/lightningnetwork/lnd/tlv" "github.com/stretchr/testify/require" ) @@ -70,18 +71,18 @@ func TestTaprootBriefcase(t *testing.T) { require.NoError(t, err) testCase := &taprootBriefcase{ - CtrlBlocks: &ctrlBlocks{ + CtrlBlocks: tlv.NewRecordT[tlv.TlvType0](ctrlBlocks{ CommitSweepCtrlBlock: sweepCtrlBlock[:], RevokeSweepCtrlBlock: revokeCtrlBlock[:], OutgoingHtlcCtrlBlocks: randResolverCtrlBlocks(t), IncomingHtlcCtrlBlocks: randResolverCtrlBlocks(t), SecondLevelCtrlBlocks: randResolverCtrlBlocks(t), - }, - TapTweaks: &tapTweaks{ + }), + TapTweaks: tlv.NewRecordT[tlv.TlvType1](tapTweaks{ AnchorTweak: anchorTweak[:], BreachedHtlcTweaks: randHtlcTweaks(t), BreachedSecondLevelHltcTweaks: randHtlcTweaks(t), - }, + }), } var b bytes.Buffer From f4710ca63946698089dcfe0e6d7146db86dce5b6 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Mon, 3 Jun 2024 22:58:57 -0700 Subject: [PATCH 145/218] lnwallet: add new aux resolver interface This will be used by external callers to modify the way we resolve contracts on chain. For a given contract, we'll store an extra "blob", that will later be presented during the sweeping phase. --- config_builder.go | 4 + contractcourt/breach_arbitrator_test.go | 6 + contractcourt/chain_watcher.go | 11 +- lnwallet/aux_resolutions.go | 89 +++++++++++ lnwallet/channel.go | 194 +++++++++++++++++++++--- lnwallet/channel_test.go | 10 ++ lnwallet/mock.go | 11 ++ server.go | 1 + 8 files changed, 303 insertions(+), 23 deletions(-) create mode 100644 lnwallet/aux_resolutions.go diff --git a/config_builder.go b/config_builder.go index 4b585cb585..7d6235f397 100644 --- a/config_builder.go +++ b/config_builder.go @@ -187,6 +187,10 @@ type AuxComponents struct { // AuxChanCloser is an optional channel closer that can be used to // modify the way a coop-close transaction is constructed. AuxChanCloser fn.Option[chancloser.AuxChanCloser] + + // AuxContractResolver is an optional interface that can be used to + // modify the way contracts are resolved. + AuxContractResolver fn.Option[lnwallet.AuxContractResolver] } // DefaultWalletImpl is the default implementation of our normal, btcwallet diff --git a/contractcourt/breach_arbitrator_test.go b/contractcourt/breach_arbitrator_test.go index cf575f1534..f3a56e8aac 100644 --- a/contractcourt/breach_arbitrator_test.go +++ b/contractcourt/breach_arbitrator_test.go @@ -1592,6 +1592,9 @@ func testBreachSpends(t *testing.T, test breachTest) { retribution, err := lnwallet.NewBreachRetribution( alice.State(), height, 1, forceCloseTx, fn.Some[lnwallet.AuxLeafStore](&lnwallet.MockAuxLeafStore{}), + fn.Some[lnwallet.AuxContractResolver]( + &lnwallet.MockAuxContractResolver{}, + ), ) require.NoError(t, err, "unable to create breach retribution") @@ -1802,6 +1805,9 @@ func TestBreachDelayedJusticeConfirmation(t *testing.T) { retribution, err := lnwallet.NewBreachRetribution( alice.State(), height, uint32(blockHeight), forceCloseTx, fn.Some[lnwallet.AuxLeafStore](&lnwallet.MockAuxLeafStore{}), + fn.Some[lnwallet.AuxContractResolver]( + &lnwallet.MockAuxContractResolver{}, + ), ) require.NoError(t, err, "unable to create breach retribution") diff --git a/contractcourt/chain_watcher.go b/contractcourt/chain_watcher.go index b1e3fc1c28..ef4a4e2008 100644 --- a/contractcourt/chain_watcher.go +++ b/contractcourt/chain_watcher.go @@ -196,6 +196,9 @@ type chainWatcherConfig struct { // auxLeafStore can be used to fetch information for custom channels. auxLeafStore fn.Option[lnwallet.AuxLeafStore] + + // auxResolver is used to supplement contract resolution. + auxResolver fn.Option[lnwallet.AuxContractResolver] } // chainWatcher is a system that's assigned to every active channel. The duty @@ -896,7 +899,7 @@ func (c *chainWatcher) handlePossibleBreach(commitSpend *chainntnfs.SpendDetail, spendHeight := uint32(commitSpend.SpendingHeight) retribution, err := lnwallet.NewBreachRetribution( c.cfg.chanState, broadcastStateNum, spendHeight, - commitSpend.SpendingTx, c.cfg.auxLeafStore, + commitSpend.SpendingTx, c.cfg.auxLeafStore, c.cfg.auxResolver, ) switch { @@ -1147,7 +1150,7 @@ func (c *chainWatcher) dispatchLocalForceClose( forceClose, err := lnwallet.NewLocalForceCloseSummary( c.cfg.chanState, c.cfg.signer, commitSpend.SpendingTx, stateNum, - c.cfg.auxLeafStore, + c.cfg.auxLeafStore, c.cfg.auxResolver, ) if err != nil { return err @@ -1239,8 +1242,8 @@ func (c *chainWatcher) dispatchRemoteForceClose( // materials required to let each subscriber sweep the funds in the // channel on-chain. uniClose, err := lnwallet.NewUnilateralCloseSummary( - c.cfg.chanState, c.cfg.signer, commitSpend, - remoteCommit, commitPoint, c.cfg.auxLeafStore, + c.cfg.chanState, c.cfg.signer, commitSpend, remoteCommit, + commitPoint, c.cfg.auxLeafStore, c.cfg.auxResolver, ) if err != nil { return err diff --git a/lnwallet/aux_resolutions.go b/lnwallet/aux_resolutions.go new file mode 100644 index 0000000000..e3b04fc5aa --- /dev/null +++ b/lnwallet/aux_resolutions.go @@ -0,0 +1,89 @@ +package lnwallet + +import ( + "github.com/btcsuite/btcd/btcutil" + "github.com/btcsuite/btcd/wire" + "github.com/lightningnetwork/lnd/fn" + "github.com/lightningnetwork/lnd/input" + "github.com/lightningnetwork/lnd/lnwire" + "github.com/lightningnetwork/lnd/tlv" +) + +// CloseType is an enum that represents the type of close that we are trying to +// resolve. +type CloseType uint8 + +const ( + // LocalForceClose represents a local force close. + LocalForceClose CloseType = iota + + // RemoteForceClose represents a remote force close. + RemoteForceClose + + // Breach represents a breach by the remote party. + Breach +) + +// ResolutionReq is used to ask an outside sub-system for additional +// information needed to resolve a contract. +type ResolutionReq struct { + // ChanPoint is the channel point of the channel that we are trying to + // resolve. + ChanPoint wire.OutPoint + + // ShortChanID is the short channel ID of the channel that we are + // trying to resolve. + ShortChanID lnwire.ShortChannelID + + // Initiator is a bool if we're the initiator of the channel. + Initiator bool + + // CommitBlob is an optional commit blob for the channel. + CommitBlob fn.Option[tlv.Blob] + + // FundingBlob is an optional funding blob for the channel. + FundingBlob fn.Option[tlv.Blob] + + // Type is the type of the witness that we are trying to resolve. + Type input.WitnessType + + // CloseType is the type of close that we are trying to resolve. + CloseType CloseType + + // CommitTx is the force close commitment transaction. + CommitTx *wire.MsgTx + + // CommitFee is the fee that was paid for the commitment transaction. + CommitFee btcutil.Amount + + // ContractPoint is the outpoint of the contract we're trying to + // resolve. + ContractPoint wire.OutPoint + + // SignDesc is the sign descriptor for the contract. + SignDesc input.SignDescriptor + + // KeyRing is the key ring for the channel. + KeyRing *CommitmentKeyRing + + // CsvDelay is the CSV delay for the local output for this commitment. + CsvDelay uint32 + + // BreachCsvDelay is the CSV delay for the remote output. This is only + // set when the CloseType is Breach. This indicates the CSV delay to + // use for the remote party's to_local delayed output, that is now + // rightfully ours in a breach situation. + BreachCsvDelay fn.Option[uint32] + + // CltvDelay is the CLTV delay for the outpoint. + CltvDelay fn.Option[uint32] +} + +// AuxContractResolver is an interface that is used to resolve contracts that +// may need additional outside information to resolve correctly. +type AuxContractResolver interface { + // ResolveContract is called to resolve a contract that needs + // additional information to resolve properly. If no extra information + // is required, a nil Result error is returned. + ResolveContract(ResolutionReq) fn.Result[tlv.Blob] +} diff --git a/lnwallet/channel.go b/lnwallet/channel.go index 8f7513f105..f3e0769506 100644 --- a/lnwallet/channel.go +++ b/lnwallet/channel.go @@ -764,6 +764,10 @@ type LightningChannel struct { // custom channel variants. auxSigner fn.Option[AuxSigner] + // auxResolver is an optional component that can be used to modify the + // way contracts are resolved. + auxResolver fn.Option[AuxContractResolver] + // Capacity is the total capacity of this channel. Capacity btcutil.Amount @@ -824,8 +828,9 @@ type channelOpts struct { localNonce *musig2.Nonces remoteNonce *musig2.Nonces - leafStore fn.Option[AuxLeafStore] - auxSigner fn.Option[AuxSigner] + leafStore fn.Option[AuxLeafStore] + auxSigner fn.Option[AuxSigner] + auxResolver fn.Option[AuxContractResolver] skipNonceInit bool } @@ -871,6 +876,14 @@ func WithAuxSigner(signer AuxSigner) ChannelOpt { } } +// WithAuxResolver is used to specify a custom aux contract resolver for the +// channel. +func WithAuxResolver(resolver AuxContractResolver) ChannelOpt { + return func(o *channelOpts) { + o.auxResolver = fn.Some[AuxContractResolver](resolver) + } +} + // defaultChannelOpts returns the set of default options for a new channel. func defaultChannelOpts() *channelOpts { return &channelOpts{} @@ -924,6 +937,7 @@ func NewLightningChannel(signer input.Signer, Signer: signer, leafStore: opts.leafStore, auxSigner: opts.auxSigner, + auxResolver: opts.auxResolver, sigPool: sigPool, currentHeight: localCommit.CommitHeight, commitChains: commitChains, @@ -1884,6 +1898,10 @@ type HtlcRetribution struct { // this HTLC was offered by us. This flag is used determine the exact // witness type should be used to sweep the output. IsIncoming bool + + // ResolutionBlob is a blob used for aux channels that permits a + // spender of this output to claim all funds. + ResolutionBlob fn.Option[tlv.Blob] } // BreachRetribution contains all the data necessary to bring a channel @@ -1953,6 +1971,14 @@ type BreachRetribution struct { // breaching commitment transaction. This allows downstream clients to // have access to the public keys used in the scripts. KeyRing *CommitmentKeyRing + + // LocalResolutionBlob is a blob used for aux channels that permits an + // honest party to sweep the local commitment output. + LocalResolutionBlob fn.Option[tlv.Blob] + + // RemoteResolutionBlob is a blob used for aux channels that permits an + // honest party to sweep the remote commitment output. + RemoteResolutionBlob fn.Option[tlv.Blob] } // NewBreachRetribution creates a new fully populated BreachRetribution for the @@ -1964,7 +1990,9 @@ type BreachRetribution struct { // the required fields then ErrRevLogDataMissing will be returned. func NewBreachRetribution(chanState *channeldb.OpenChannel, stateNum uint64, breachHeight uint32, spendTx *wire.MsgTx, - leafStore fn.Option[AuxLeafStore]) (*BreachRetribution, error) { + leafStore fn.Option[AuxLeafStore], + auxResolver fn.Option[AuxContractResolver]) (*BreachRetribution, + error) { // Query the on-disk revocation log for the snapshot which was recorded // at this particular state num. Based on whether a legacy revocation @@ -2127,6 +2155,38 @@ func NewBreachRetribution(chanState *channeldb.OpenChannel, stateNum uint64, return nil, err } } + + // At this point, we'll check to see if we need any extra + // resolution data for this output. + resolveReq := ResolutionReq{ + ChanPoint: chanState.FundingOutpoint, + ShortChanID: chanState.ShortChanID(), + Initiator: chanState.IsInitiator, + FundingBlob: chanState.CustomBlob, + Type: input.TaprootRemoteCommitSpend, + CloseType: Breach, + CommitTx: spendTx, + SignDesc: *br.LocalOutputSignDesc, + KeyRing: keyRing, + CsvDelay: ourDelay, + BreachCsvDelay: fn.Some(theirDelay), + CommitFee: chanState.RemoteCommitment.CommitFee, + } + if revokedLog != nil { + resolveReq.CommitBlob = revokedLog.CustomBlob.ValOpt() + } + + resolveBlob := fn.MapOptionZ( + auxResolver, + func(a AuxContractResolver) fn.Result[tlv.Blob] { + return a.ResolveContract(resolveReq) + }, + ) + if err := resolveBlob.Err(); err != nil { + return nil, fmt.Errorf("unable to aux resolve: %w", err) + } + + br.LocalResolutionBlob = resolveBlob.Option() } // Similarly, if their balance exceeds the remote party's dust limit, @@ -2174,6 +2234,37 @@ func NewBreachRetribution(chanState *channeldb.OpenChannel, stateNum uint64, return nil, err } } + + // At this point, we'll check to see if we need any extra + // resolution data for this output. + resolveReq := ResolutionReq{ + ChanPoint: chanState.FundingOutpoint, + ShortChanID: chanState.ShortChanID(), + Initiator: chanState.IsInitiator, + FundingBlob: chanState.CustomBlob, + Type: input.TaprootCommitmentRevoke, + CloseType: Breach, + CommitTx: spendTx, + SignDesc: *br.RemoteOutputSignDesc, + KeyRing: keyRing, + CsvDelay: theirDelay, + BreachCsvDelay: fn.Some(theirDelay), + CommitFee: chanState.RemoteCommitment.CommitFee, + } + if revokedLog != nil { + resolveReq.CommitBlob = revokedLog.CustomBlob.ValOpt() + } + resolveBlob := fn.MapOptionZ( + auxResolver, + func(a AuxContractResolver) fn.Result[tlv.Blob] { + return a.ResolveContract(resolveReq) + }, + ) + if err := resolveBlob.Err(); err != nil { + return nil, fmt.Errorf("unable to aux resolve: %w", err) + } + + br.RemoteResolutionBlob = resolveBlob.Option() } // Finally, with all the necessary data constructed, we can pad the @@ -6473,6 +6564,11 @@ type CommitOutputResolution struct { // that pay to the local party within the broadcast commitment // transaction. MaturityDelay uint32 + + // ResolutionBlob is a blob used for aux channels that permits a + // spender of the output to properly resolve it in the case of a force + // close. + ResolutionBlob fn.Option[tlv.Blob] } // UnilateralCloseSummary describes the details of a detected unilateral @@ -6530,7 +6626,9 @@ type UnilateralCloseSummary struct { func NewUnilateralCloseSummary(chanState *channeldb.OpenChannel, signer input.Signer, commitSpend *chainntnfs.SpendDetail, remoteCommit channeldb.ChannelCommitment, commitPoint *btcec.PublicKey, - leafStore fn.Option[AuxLeafStore]) (*UnilateralCloseSummary, error) { + leafStore fn.Option[AuxLeafStore], + auxResolver fn.Option[AuxContractResolver]) (*UnilateralCloseSummary, + error) { // First, we'll generate the commitment point and the revocation point // so we can re-construct the HTLC state and also our payment key. @@ -6554,11 +6652,17 @@ func NewUnilateralCloseSummary(chanState *channeldb.OpenChannel, // Next, we'll obtain HTLC resolutions for all the outgoing HTLC's we // had on their commitment transaction. - var leaseExpiry uint32 + var ( + leaseExpiry uint32 + selfPoint *wire.OutPoint + localBalance int64 + isRemoteInitiator = !chanState.IsInitiator + commitTxBroadcast = commitSpend.SpendingTx + ) + if chanState.ChanType.HasLeaseExpiration() { leaseExpiry = chanState.ThawHeight } - isRemoteInitiator := !chanState.IsInitiator htlcResolutions, err := extractHtlcResolutions( chainfee.SatPerKWeight(remoteCommit.FeePerKw), commitType, signer, remoteCommit.Htlcs, keyRing, &chanState.LocalChanCfg, @@ -6567,12 +6671,10 @@ func NewUnilateralCloseSummary(chanState *channeldb.OpenChannel, auxResult.AuxLeaves, ) if err != nil { - return nil, fmt.Errorf("unable to create htlc "+ - "resolutions: %v", err) + return nil, fmt.Errorf("unable to create htlc resolutions: %w", + err) } - commitTxBroadcast := commitSpend.SpendingTx - // Before we can generate the proper sign descriptor, we'll need to // locate the output index of our non-delayed output on the commitment // transaction. @@ -6587,14 +6689,8 @@ func NewUnilateralCloseSummary(chanState *channeldb.OpenChannel, ) if err != nil { return nil, fmt.Errorf("unable to create self commit "+ - "script: %v", err) + "script: %w", err) } - - var ( - selfPoint *wire.OutPoint - localBalance int64 - ) - for outputIndex, txOut := range commitTxBroadcast.TxOut { if bytes.Equal(txOut.PkScript, selfScript.PkScript()) { selfPoint = &wire.OutPoint{ @@ -6657,6 +6753,35 @@ func NewUnilateralCloseSummary(chanState *channeldb.OpenChannel, return nil, err } } + + // At this point, we'll check to see if we need any extra + // resolution data for this output. + resolveReq := ResolutionReq{ + ChanPoint: chanState.FundingOutpoint, + ShortChanID: chanState.ShortChanID(), + Initiator: chanState.IsInitiator, + CommitBlob: chanState.RemoteCommitment.CustomBlob, + FundingBlob: chanState.CustomBlob, + Type: input.TaprootRemoteCommitSpend, + CloseType: RemoteForceClose, + CommitTx: commitTxBroadcast, + ContractPoint: *selfPoint, + SignDesc: commitResolution.SelfOutputSignDesc, + KeyRing: keyRing, + CsvDelay: maturityDelay, + CommitFee: chanState.RemoteCommitment.CommitFee, + } + resolveBlob := fn.MapOptionZ( + auxResolver, + func(a AuxContractResolver) fn.Result[tlv.Blob] { + return a.ResolveContract(resolveReq) + }, + ) + if err := resolveBlob.Err(); err != nil { + return nil, fmt.Errorf("unable to aux resolve: %w", err) + } + + commitResolution.ResolutionBlob = resolveBlob.Option() } closeSummary := channeldb.ChannelCloseSummary{ @@ -7513,7 +7638,7 @@ func (lc *LightningChannel) ForceClose() (*LocalForceCloseSummary, error) { localCommitment := lc.channelState.LocalCommitment summary, err := NewLocalForceCloseSummary( lc.channelState, lc.Signer, commitTx, - localCommitment.CommitHeight, lc.leafStore, + localCommitment.CommitHeight, lc.leafStore, lc.auxResolver, ) if err != nil { return nil, fmt.Errorf("unable to gen force close "+ @@ -7531,7 +7656,9 @@ func (lc *LightningChannel) ForceClose() (*LocalForceCloseSummary, error) { // transaction corresponding to localCommit. func NewLocalForceCloseSummary(chanState *channeldb.OpenChannel, signer input.Signer, commitTx *wire.MsgTx, stateNum uint64, - leafStore fn.Option[AuxLeafStore]) (*LocalForceCloseSummary, error) { + leafStore fn.Option[AuxLeafStore], + auxResolver fn.Option[AuxContractResolver]) (*LocalForceCloseSummary, + error) { // Re-derive the original pkScript for to-self output within the // commitment transaction. We'll need this to find the corresponding @@ -7655,6 +7782,35 @@ func NewLocalForceCloseSummary(chanState *channeldb.OpenChannel, return nil, err } } + + // At this point, we'll check to see if we need any extra + // resolution data for this output. + resolveBlob := fn.MapOptionZ( + auxResolver, + func(a AuxContractResolver) fn.Result[tlv.Blob] { + //nolint:lll + return a.ResolveContract(ResolutionReq{ + ChanPoint: chanState.FundingOutpoint, + ShortChanID: chanState.ShortChanID(), + Initiator: chanState.IsInitiator, + CommitBlob: chanState.LocalCommitment.CustomBlob, + FundingBlob: chanState.CustomBlob, + Type: input.TaprootLocalCommitSpend, + CloseType: LocalForceClose, + CommitTx: commitTx, + ContractPoint: commitResolution.SelfOutPoint, + SignDesc: commitResolution.SelfOutputSignDesc, + KeyRing: keyRing, + CsvDelay: csvTimeout, + CommitFee: chanState.LocalCommitment.CommitFee, + }) + }, + ) + if err := resolveBlob.Err(); err != nil { + return nil, fmt.Errorf("unable to aux resolve: %w", err) + } + + commitResolution.ResolutionBlob = resolveBlob.Option() } // Once the delay output has been found (if it exists), then we'll also diff --git a/lnwallet/channel_test.go b/lnwallet/channel_test.go index 54ca723e27..5e90285786 100644 --- a/lnwallet/channel_test.go +++ b/lnwallet/channel_test.go @@ -6005,6 +6005,7 @@ func TestChannelUnilateralCloseHtlcResolution(t *testing.T) { aliceChannel.channelState.RemoteCommitment, aliceChannel.channelState.RemoteCurrentRevocation, fn.Some[AuxLeafStore](&MockAuxLeafStore{}), + fn.Some[AuxContractResolver](&MockAuxContractResolver{}), ) require.NoError(t, err, "unable to create alice close summary") @@ -6155,6 +6156,7 @@ func TestChannelUnilateralClosePendingCommit(t *testing.T) { aliceChannel.channelState.RemoteCommitment, aliceChannel.channelState.RemoteCurrentRevocation, fn.Some[AuxLeafStore](&MockAuxLeafStore{}), + fn.Some[AuxContractResolver](&MockAuxContractResolver{}), ) require.NoError(t, err, "unable to create alice close summary") @@ -6173,6 +6175,7 @@ func TestChannelUnilateralClosePendingCommit(t *testing.T) { aliceRemoteChainTip.Commitment, aliceChannel.channelState.RemoteNextRevocation, fn.Some[AuxLeafStore](&MockAuxLeafStore{}), + fn.Some[AuxContractResolver](&MockAuxContractResolver{}), ) require.NoError(t, err, "unable to create alice close summary") @@ -7054,6 +7057,7 @@ func TestNewBreachRetributionSkipsDustHtlcs(t *testing.T) { breachRet, err := NewBreachRetribution( aliceChannel.channelState, revokedStateNum, 100, breachTx, fn.Some[AuxLeafStore](&MockAuxLeafStore{}), + fn.Some[AuxContractResolver](&MockAuxContractResolver{}), ) require.NoError(t, err, "unable to create breach retribution") @@ -10612,6 +10616,7 @@ func testNewBreachRetribution(t *testing.T, chanType channeldb.ChannelType) { _, err = NewBreachRetribution( aliceChannel.channelState, stateNum, breachHeight, breachTx, fn.Some[AuxLeafStore](&MockAuxLeafStore{}), + fn.Some[AuxContractResolver](&MockAuxContractResolver{}), ) require.ErrorIs(t, err, channeldb.ErrNoPastDeltas) @@ -10620,6 +10625,7 @@ func testNewBreachRetribution(t *testing.T, chanType channeldb.ChannelType) { _, err = NewBreachRetribution( aliceChannel.channelState, stateNum, breachHeight, nil, fn.Some[AuxLeafStore](&MockAuxLeafStore{}), + fn.Some[AuxContractResolver](&MockAuxContractResolver{}), ) require.ErrorIs(t, err, channeldb.ErrNoPastDeltas) @@ -10666,6 +10672,7 @@ func testNewBreachRetribution(t *testing.T, chanType channeldb.ChannelType) { br, err := NewBreachRetribution( aliceChannel.channelState, stateNum, breachHeight, breachTx, fn.Some[AuxLeafStore](&MockAuxLeafStore{}), + fn.Some[AuxContractResolver](&MockAuxContractResolver{}), ) require.NoError(t, err) @@ -10678,6 +10685,7 @@ func testNewBreachRetribution(t *testing.T, chanType channeldb.ChannelType) { br, err = NewBreachRetribution( aliceChannel.channelState, stateNum, breachHeight, nil, fn.Some[AuxLeafStore](&MockAuxLeafStore{}), + fn.Some[AuxContractResolver](&MockAuxContractResolver{}), ) require.NoError(t, err) assertRetribution(br, 1, 0) @@ -10687,6 +10695,7 @@ func testNewBreachRetribution(t *testing.T, chanType channeldb.ChannelType) { _, err = NewBreachRetribution( aliceChannel.channelState, stateNum+1, breachHeight, breachTx, fn.Some[AuxLeafStore](&MockAuxLeafStore{}), + fn.Some[AuxContractResolver](&MockAuxContractResolver{}), ) require.ErrorIs(t, err, channeldb.ErrLogEntryNotFound) @@ -10695,6 +10704,7 @@ func testNewBreachRetribution(t *testing.T, chanType channeldb.ChannelType) { _, err = NewBreachRetribution( aliceChannel.channelState, stateNum+1, breachHeight, nil, fn.Some[AuxLeafStore](&MockAuxLeafStore{}), + fn.Some[AuxContractResolver](&MockAuxContractResolver{}), ) require.ErrorIs(t, err, channeldb.ErrLogEntryNotFound) } diff --git a/lnwallet/mock.go b/lnwallet/mock.go index 6e48f5b442..68d5dcca78 100644 --- a/lnwallet/mock.go +++ b/lnwallet/mock.go @@ -485,3 +485,14 @@ func (a *MockAuxSigner) VerifySecondLevelSigs(chanState AuxChanState, return args.Error(0) } + +type MockAuxContractResolver struct{} + +// ResolveContract is called to resolve a contract that needs +// additional information to resolve properly. If no extra information +// is required, a nil Result error is returned. +func (*MockAuxContractResolver) ResolveContract( + ResolutionReq) fn.Result[tlv.Blob] { + + return fn.Ok[tlv.Blob](nil) +} diff --git a/server.go b/server.go index 8fe89bbe2f..0c209b0498 100644 --- a/server.go +++ b/server.go @@ -1629,6 +1629,7 @@ func newServer(cfg *Config, listenAddrs []net.Addr, br, err := lnwallet.NewBreachRetribution( channel, commitHeight, 0, nil, implCfg.AuxLeafStore, + implCfg.AuxContractResolver, ) if err != nil { return nil, 0, err From 74d27f2c7c74fa69d7abb77bd56821c6f92511a0 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Mon, 3 Jun 2024 22:56:06 -0700 Subject: [PATCH 146/218] contractcourt: add CommitBlob to taprootBriefcase This'll be used to store the extra resolution information for the commitment outputs. --- contractcourt/briefcase.go | 12 ++++++++ contractcourt/taproot_briefcase.go | 41 +++++++++++++++++++++++-- contractcourt/taproot_briefcase_test.go | 10 ++++++ 3 files changed, 61 insertions(+), 2 deletions(-) diff --git a/contractcourt/briefcase.go b/contractcourt/briefcase.go index 2132da6deb..26df50b307 100644 --- a/contractcourt/briefcase.go +++ b/contractcourt/briefcase.go @@ -10,9 +10,11 @@ import ( "github.com/btcsuite/btcd/txscript" "github.com/btcsuite/btcd/wire" "github.com/lightningnetwork/lnd/channeldb" + "github.com/lightningnetwork/lnd/fn" "github.com/lightningnetwork/lnd/input" "github.com/lightningnetwork/lnd/kvdb" "github.com/lightningnetwork/lnd/lnwallet" + "github.com/lightningnetwork/lnd/tlv" ) // ContractResolutions is a wrapper struct around the two forms of resolutions @@ -1554,6 +1556,12 @@ func encodeTaprootAuxData(w io.Writer, c *ContractResolutions) error { commitSignDesc := commitResolution.SelfOutputSignDesc //nolint:lll tapCase.CtrlBlocks.Val.CommitSweepCtrlBlock = commitSignDesc.ControlBlock + + c.CommitResolution.ResolutionBlob.WhenSome(func(b []byte) { + tapCase.SettledCommitBlob = tlv.SomeRecordT( + tlv.NewPrimitiveRecord[tlv.TlvType2](b), + ) + }) } for _, htlc := range c.HtlcResolutions.IncomingHTLCs { @@ -1640,6 +1648,10 @@ func decodeTapRootAuxData(r io.Reader, c *ContractResolutions) error { if c.CommitResolution != nil { c.CommitResolution.SelfOutputSignDesc.ControlBlock = tapCase.CtrlBlocks.Val.CommitSweepCtrlBlock + + tapCase.SettledCommitBlob.WhenSomeV(func(b []byte) { + c.CommitResolution.ResolutionBlob = fn.Some(b) + }) } for i := range c.HtlcResolutions.IncomingHTLCs { diff --git a/contractcourt/taproot_briefcase.go b/contractcourt/taproot_briefcase.go index 8544582c08..0a2beeff79 100644 --- a/contractcourt/taproot_briefcase.go +++ b/contractcourt/taproot_briefcase.go @@ -29,6 +29,17 @@ type taprootBriefcase struct { // are to be spent via a keyspend path. This includes anchors, and any // revocation paths. TapTweaks tlv.RecordT[tlv.TlvType1, tapTweaks] + + // SettledCommitBlob is an optional record that contains an opaque blob + // that may be used to properly sweep commitment outputs on a force + // close transaction. + SettledCommitBlob tlv.OptionalRecordT[tlv.TlvType2, tlv.Blob] + + // BreachCommitBlob is an optional record that contains an opaque blob + // used to sweep a remote party's breached output. + BreachedCommitBlob tlv.OptionalRecordT[tlv.TlvType3, tlv.Blob] + + // TODO(roasbeef): htlc blobs } // TODO(roasbeef): morph into new tlv record @@ -48,6 +59,18 @@ func (t *taprootBriefcase) EncodeRecords() []tlv.Record { t.CtrlBlocks.Record(), t.TapTweaks.Record(), } + + t.SettledCommitBlob.WhenSome( + func(r tlv.RecordT[tlv.TlvType2, tlv.Blob]) { + records = append(records, r.Record()) + }, + ) + t.BreachedCommitBlob.WhenSome( + func(r tlv.RecordT[tlv.TlvType3, tlv.Blob]) { + records = append(records, r.Record()) + }, + ) + return records } @@ -71,16 +94,30 @@ func (t *taprootBriefcase) Encode(w io.Writer) error { // Decode decodes the given reader into the target struct. func (t *taprootBriefcase) Decode(r io.Reader) error { - stream, err := tlv.NewStream(t.DecodeRecords()...) + settledCommitBlob := t.SettledCommitBlob.Zero() + breachedCommitBlob := t.BreachedCommitBlob.Zero() + records := append( + t.DecodeRecords(), + settledCommitBlob.Record(), + breachedCommitBlob.Record(), + ) + stream, err := tlv.NewStream(records...) if err != nil { return err } - _, err = stream.DecodeWithParsedTypes(r) + typeMap, err := stream.DecodeWithParsedTypes(r) if err != nil { return err } + if val, ok := typeMap[t.SettledCommitBlob.TlvType()]; ok && val == nil { + t.SettledCommitBlob = tlv.SomeRecordT(settledCommitBlob) + } + if v, ok := typeMap[t.BreachedCommitBlob.TlvType()]; ok && v == nil { + t.BreachedCommitBlob = tlv.SomeRecordT(breachedCommitBlob) + } + return nil } diff --git a/contractcourt/taproot_briefcase_test.go b/contractcourt/taproot_briefcase_test.go index 1b0d0b399b..a7d52d9635 100644 --- a/contractcourt/taproot_briefcase_test.go +++ b/contractcourt/taproot_briefcase_test.go @@ -70,6 +70,10 @@ func TestTaprootBriefcase(t *testing.T) { _, err = rand.Read(anchorTweak[:]) require.NoError(t, err) + var commitBlob [100]byte + _, err = rand.Read(commitBlob[:]) + require.NoError(t, err) + testCase := &taprootBriefcase{ CtrlBlocks: tlv.NewRecordT[tlv.TlvType0](ctrlBlocks{ CommitSweepCtrlBlock: sweepCtrlBlock[:], @@ -83,6 +87,12 @@ func TestTaprootBriefcase(t *testing.T) { BreachedHtlcTweaks: randHtlcTweaks(t), BreachedSecondLevelHltcTweaks: randHtlcTweaks(t), }), + SettledCommitBlob: tlv.SomeRecordT( + tlv.NewPrimitiveRecord[tlv.TlvType2](commitBlob[:]), + ), + BreachedCommitBlob: tlv.SomeRecordT( + tlv.NewPrimitiveRecord[tlv.TlvType3](commitBlob[:]), + ), } var b bytes.Buffer From 07ee114116f5be96da85377fcc2a12d1560dbf1a Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Mon, 3 Jun 2024 23:00:38 -0700 Subject: [PATCH 147/218] lnwallet+peer: move internalKeyForAddr to lnwallet package This way we can re-use it. We also make it slightly more generalized. --- lnwallet/interface.go | 63 +++++++++++++++++++++++++++++++++ peer/brontide.go | 81 +++++++++++-------------------------------- server.go | 81 +++++++++++++++++++++++++++++++++++-------- sweep/sweeper.go | 6 ++-- sweep/sweeper_test.go | 8 +++-- 5 files changed, 160 insertions(+), 79 deletions(-) diff --git a/lnwallet/interface.go b/lnwallet/interface.go index af5955d8e6..85317d6852 100644 --- a/lnwallet/interface.go +++ b/lnwallet/interface.go @@ -11,6 +11,7 @@ import ( "github.com/btcsuite/btcd/btcutil" "github.com/btcsuite/btcd/btcutil/hdkeychain" "github.com/btcsuite/btcd/btcutil/psbt" + "github.com/btcsuite/btcd/chaincfg" "github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/btcsuite/btcd/txscript" "github.com/btcsuite/btcd/wire" @@ -594,6 +595,68 @@ type MessageSigner interface { doubleHash bool) (*ecdsa.Signature, error) } +// AddrWithKey wraps a normal addr, but also includes the internal key for the +// delivery addr if known. +type AddrWithKey struct { + lnwire.DeliveryAddress + + InternalKey fn.Option[btcec.PublicKey] + + // TODO(roasbeef): consolidate w/ instance in chan closer +} + +// InternalKeyForAddr returns the internal key associated with a taproot +// address. +func InternalKeyForAddr(wallet WalletController, netParams *chaincfg.Params, + deliveryScript []byte) (fn.Option[keychain.KeyDescriptor], error) { + + none := fn.None[keychain.KeyDescriptor]() + + pkScript, err := txscript.ParsePkScript(deliveryScript) + if err != nil { + return none, err + } + addr, err := pkScript.Address(netParams) + if err != nil { + return none, err + } + + // If it's not a taproot address, we don't require to know the internal + // key in the first place. So we don't return an error here, but also no + // internal key. + _, isTaproot := addr.(*btcutil.AddressTaproot) + if !isTaproot { + return none, nil + } + + walletAddr, err := wallet.AddressInfo(addr) + if err != nil { + return none, err + } + + // No wallet addr. No error, but we'll return an nil error value here, + // as callers can use the .Option() method to get an option value. + if walletAddr == nil { + return none, nil + } + + pubKeyAddr, ok := walletAddr.(waddrmgr.ManagedPubKeyAddress) + if !ok { + return none, fmt.Errorf("expected pubkey addr, got %T", + pubKeyAddr) + } + + _, derivationPath, _ := pubKeyAddr.DerivationInfo() + + return fn.Some[keychain.KeyDescriptor](keychain.KeyDescriptor{ + KeyLocator: keychain.KeyLocator{ + Family: keychain.KeyFamily(derivationPath.Account), + Index: derivationPath.Index, + }, + PubKey: pubKeyAddr.PubKey(), + }), nil +} + // WalletDriver represents a "driver" for a particular concrete // WalletController implementation. A driver is identified by a globally unique // string identifier along with a 'New()' method which is responsible for diff --git a/peer/brontide.go b/peer/brontide.go index 2676740c6d..ecff687d3c 100644 --- a/peer/brontide.go +++ b/peer/brontide.go @@ -13,13 +13,11 @@ import ( "time" "github.com/btcsuite/btcd/btcec/v2" - "github.com/btcsuite/btcd/btcutil" "github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/btcsuite/btcd/connmgr" "github.com/btcsuite/btcd/txscript" "github.com/btcsuite/btcd/wire" "github.com/btcsuite/btclog" - "github.com/btcsuite/btcwallet/waddrmgr" "github.com/davecgh/go-spew/spew" "github.com/lightningnetwork/lnd/buffer" "github.com/lightningnetwork/lnd/build" @@ -37,6 +35,7 @@ import ( "github.com/lightningnetwork/lnd/htlcswitch/hop" "github.com/lightningnetwork/lnd/input" "github.com/lightningnetwork/lnd/invoices" + "github.com/lightningnetwork/lnd/keychain" "github.com/lightningnetwork/lnd/lnpeer" "github.com/lightningnetwork/lnd/lntypes" "github.com/lightningnetwork/lnd/lnutils" @@ -902,71 +901,31 @@ func (p *Brontide) QuitSignal() <-chan struct{} { return p.quit } -// internalKeyForAddr returns the internal key associated with a taproot -// address. -func internalKeyForAddr(wallet *lnwallet.LightningWallet, - deliveryScript []byte) (fn.Option[btcec.PublicKey], error) { - - none := fn.None[btcec.PublicKey]() - - pkScript, err := txscript.ParsePkScript(deliveryScript) - if err != nil { - return none, err - } - addr, err := pkScript.Address(&wallet.Cfg.NetParams) - if err != nil { - return none, err - } - - // If it's not a taproot address, we don't require to know the internal - // key in the first place. So we don't return an error here, but also no - // internal key. - _, isTaproot := addr.(*btcutil.AddressTaproot) - if !isTaproot { - return none, nil - } - - walletAddr, err := wallet.AddressInfo(addr) - if err != nil { - return none, err - } - - // If the address isn't known to the wallet, we can't determine the - // internal key. - if walletAddr == nil { - return none, nil - } - - pubKeyAddr, ok := walletAddr.(waddrmgr.ManagedPubKeyAddress) - if !ok { - return none, fmt.Errorf("expected pubkey addr, got %T", - pubKeyAddr) - } - - return fn.Some(*pubKeyAddr.PubKey()), nil -} - // addrWithInternalKey takes a delivery script, then attempts to supplement it // with information related to the internal key for the addr, but only if it's // a taproot addr. func (p *Brontide) addrWithInternalKey( - deliveryScript []byte) fn.Result[chancloser.DeliveryAddrWithKey] { + deliveryScript []byte) (*chancloser.DeliveryAddrWithKey, error) { - // TODO(roasbeef): not compatible with external shutdown addr? // Currently, custom channels cannot be created with external upfront // shutdown addresses, so this shouldn't be an issue. We only require // the internal key for taproot addresses to be able to provide a non // inclusion proof of any scripts. - - internalKey, err := internalKeyForAddr(p.cfg.Wallet, deliveryScript) + internalKeyDesc, err := lnwallet.InternalKeyForAddr( + p.cfg.Wallet, &p.cfg.Wallet.Cfg.NetParams, deliveryScript, + ) if err != nil { - return fn.Err[chancloser.DeliveryAddrWithKey](err) + return nil, fmt.Errorf("unable to fetch internal key: %w", err) } - return fn.Ok(chancloser.DeliveryAddrWithKey{ + return &chancloser.DeliveryAddrWithKey{ DeliveryAddress: deliveryScript, - InternalKey: internalKey, - }) + InternalKey: fn.MapOption( + func(desc keychain.KeyDescriptor) btcec.PublicKey { + return *desc.PubKey + }, + )(internalKeyDesc), + }, nil } // loadActiveChannels creates indexes within the peer for tracking all active @@ -1040,6 +999,8 @@ func (p *Brontide) loadActiveChannels(chans []*channeldb.OpenChannel) ( } } + // TODO(roasbeef): also make aux resolver here + var chanOpts []lnwallet.ChannelOpt p.cfg.AuxLeafStore.WhenSome(func(s lnwallet.AuxLeafStore) { chanOpts = append(chanOpts, lnwallet.WithLeafStore(s)) @@ -1211,7 +1172,7 @@ func (p *Brontide) loadActiveChannels(chans []*channeldb.OpenChannel) ( addr, err := p.addrWithInternalKey( info.DeliveryScript.Val, - ).Unpack() + ) if err != nil { shutdownInfoErr = fmt.Errorf("unable to make "+ "delivery addr: %w", err) @@ -2977,7 +2938,7 @@ func (p *Brontide) fetchActiveChanCloser(chanID lnwire.ChannelID) ( return nil, fmt.Errorf("unable to estimate fee") } - addr, err := p.addrWithInternalKey(deliveryScript).Unpack() + addr, err := p.addrWithInternalKey(deliveryScript) if err != nil { return nil, fmt.Errorf("unable to parse addr: %w", err) } @@ -3224,7 +3185,7 @@ func (p *Brontide) restartCoopClose(lnChan *lnwallet.LightningChannel) ( closingParty = lntypes.Local } - addr, err := p.addrWithInternalKey(deliveryScript).Unpack() + addr, err := p.addrWithInternalKey(deliveryScript) if err != nil { return nil, fmt.Errorf("unable to parse addr: %w", err) } @@ -3256,7 +3217,7 @@ func (p *Brontide) restartCoopClose(lnChan *lnwallet.LightningChannel) ( // createChanCloser constructs a ChanCloser from the passed parameters and is // used to de-duplicate code. func (p *Brontide) createChanCloser(channel *lnwallet.LightningChannel, - deliveryScript chancloser.DeliveryAddrWithKey, + deliveryScript *chancloser.DeliveryAddrWithKey, fee chainfee.SatPerKWeight, req *htlcswitch.ChanClose, closer lntypes.ChannelParty) (*chancloser.ChanCloser, error) { @@ -3291,7 +3252,7 @@ func (p *Brontide) createChanCloser(channel *lnwallet.LightningChannel, ChainParams: &p.cfg.Wallet.Cfg.NetParams, Quit: p.quit, }, - deliveryScript, + *deliveryScript, fee, uint32(startingHeight), req, @@ -3350,7 +3311,7 @@ func (p *Brontide) handleLocalCloseReq(req *htlcswitch.ChanClose) { return } } - addr, err := p.addrWithInternalKey(deliveryScript).Unpack() + addr, err := p.addrWithInternalKey(deliveryScript) if err != nil { err = fmt.Errorf("unable to parse addr for channel "+ "%v: %w", req.ChanPoint, err) diff --git a/server.go b/server.go index 0c209b0498..4f880263f0 100644 --- a/server.go +++ b/server.go @@ -18,6 +18,7 @@ import ( "github.com/btcsuite/btcd/btcec/v2" "github.com/btcsuite/btcd/btcec/v2/ecdsa" "github.com/btcsuite/btcd/btcutil" + "github.com/btcsuite/btcd/chaincfg" "github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/btcsuite/btcd/connmgr" "github.com/btcsuite/btcd/txscript" @@ -520,6 +521,8 @@ func newServer(cfg *Config, listenAddrs []net.Addr, var serializedPubKey [33]byte copy(serializedPubKey[:], nodeKeyDesc.PubKey.SerializeCompressed()) + netParams := cfg.ActiveNetParams.Params + // Initialize the sphinx router. replayLog := htlcswitch.NewDecayedLog( dbs.DecayedLogDB, cc.ChainNotifier, @@ -1121,8 +1124,10 @@ func newServer(cfg *Config, listenAddrs []net.Addr, }) s.sweeper = sweep.New(&sweep.UtxoSweeperConfig{ - FeeEstimator: cc.FeeEstimator, - GenSweepScript: newSweepPkScriptGen(cc.Wallet), + FeeEstimator: cc.FeeEstimator, + GenSweepScript: newSweepPkScriptGen( + cc.Wallet, s.cfg.ActiveNetParams.Params, + ), Signer: cc.Wallet.Cfg.Signer, Wallet: newSweeperWallet(cc.Wallet), Mempool: cc.MempoolNotifier, @@ -1165,10 +1170,19 @@ func newServer(cfg *Config, listenAddrs []net.Addr, s.breachArbitrator = contractcourt.NewBreachArbitrator( &contractcourt.BreachConfig{ - CloseLink: closeLink, - DB: s.chanStateDB, - Estimator: s.cc.FeeEstimator, - GenSweepScript: newSweepPkScriptGen(cc.Wallet), + CloseLink: closeLink, + DB: s.chanStateDB, + Estimator: s.cc.FeeEstimator, + GenSweepScript: func() ([]byte, error) { + addr, err := newSweepPkScriptGen( + cc.Wallet, netParams, + )().Unpack() + if err != nil { + return nil, err + } + + return addr.DeliveryAddress, nil + }, Notifier: cc.ChainNotifier, PublishTransaction: cc.Wallet.PublishTransaction, ContractBreaches: contractBreaches, @@ -1184,8 +1198,17 @@ func newServer(cfg *Config, listenAddrs []net.Addr, ChainHash: *s.cfg.ActiveNetParams.GenesisHash, IncomingBroadcastDelta: lncfg.DefaultIncomingBroadcastDelta, OutgoingBroadcastDelta: lncfg.DefaultOutgoingBroadcastDelta, - NewSweepAddr: newSweepPkScriptGen(cc.Wallet), - PublishTx: cc.Wallet.PublishTransaction, + NewSweepAddr: func() ([]byte, error) { + addr, err := newSweepPkScriptGen( + cc.Wallet, netParams, + )().Unpack() + if err != nil { + return nil, err + } + + return addr.DeliveryAddress, nil + }, + PublishTx: cc.Wallet.PublishTransaction, DeliverResolutionMsg: func(msgs ...contractcourt.ResolutionMsg) error { for _, msg := range msgs { err := s.htlcSwitch.ProcessContractResolution(msg) @@ -1663,8 +1686,17 @@ func newServer(cfg *Config, listenAddrs []net.Addr, return s.channelNotifier. SubscribeChannelEvents() }, - Signer: cc.Wallet.Cfg.Signer, - NewAddress: newSweepPkScriptGen(cc.Wallet), + Signer: cc.Wallet.Cfg.Signer, + NewAddress: func() ([]byte, error) { + addr, err := newSweepPkScriptGen( + cc.Wallet, netParams, + )().Unpack() + if err != nil { + return nil, err + } + + return addr.DeliveryAddress, nil + }, SecretKeyRing: s.cc.KeyRing, Dial: cfg.net.Dial, AuthDial: authDial, @@ -4904,18 +4936,39 @@ func (s *server) SendCustomMessage(peerPub [33]byte, msgType lnwire.MessageType, // Specifically, the script generated is a version 0, pay-to-witness-pubkey-hash // (p2wkh) output. func newSweepPkScriptGen( - wallet lnwallet.WalletController) func() ([]byte, error) { + wallet lnwallet.WalletController, + netParams *chaincfg.Params) func() fn.Result[lnwallet.AddrWithKey] { - return func() ([]byte, error) { + return func() fn.Result[lnwallet.AddrWithKey] { sweepAddr, err := wallet.NewAddress( lnwallet.TaprootPubkey, false, lnwallet.DefaultAccountName, ) if err != nil { - return nil, err + return fn.Err[lnwallet.AddrWithKey](err) + } + + addr, err := txscript.PayToAddrScript(sweepAddr) + if err != nil { + return fn.Err[lnwallet.AddrWithKey](err) } - return txscript.PayToAddrScript(sweepAddr) + internalKeyDesc, err := lnwallet.InternalKeyForAddr( + wallet, netParams, addr, + ) + if err != nil { + return fn.Err[lnwallet.AddrWithKey](err) + } + + return fn.Ok(lnwallet.AddrWithKey{ + DeliveryAddress: addr, + InternalKey: fn.MapOption(func( + desc keychain.KeyDescriptor) btcec.PublicKey { + + return *desc.PubKey + }, + )(internalKeyDesc), + }) } } diff --git a/sweep/sweeper.go b/sweep/sweeper.go index af2f0cec9e..92d2051185 100644 --- a/sweep/sweeper.go +++ b/sweep/sweeper.go @@ -318,7 +318,7 @@ type UtxoSweeper struct { type UtxoSweeperConfig struct { // GenSweepScript generates a P2WKH script belonging to the wallet where // funds can be swept. - GenSweepScript func() ([]byte, error) + GenSweepScript func() fn.Result[lnwallet.AddrWithKey] // FeeEstimator is used when crafting sweep transactions to estimate // the necessary fee relative to the expected size of the sweep @@ -797,11 +797,11 @@ func (s *UtxoSweeper) signalResult(pi *SweeperInput, result Result) { func (s *UtxoSweeper) sweep(set InputSet) error { // Generate an output script if there isn't an unused script available. if s.currentOutputScript == nil { - pkScript, err := s.cfg.GenSweepScript() + pkScript, err := s.cfg.GenSweepScript().Unpack() if err != nil { return fmt.Errorf("gen sweep script: %w", err) } - s.currentOutputScript = pkScript + s.currentOutputScript = pkScript.DeliveryAddress } // Create a fee bump request and ask the publisher to broadcast it. The diff --git a/sweep/sweeper_test.go b/sweep/sweeper_test.go index c8d9fc510b..6d9c6c3d2e 100644 --- a/sweep/sweeper_test.go +++ b/sweep/sweeper_test.go @@ -12,6 +12,7 @@ import ( "github.com/lightningnetwork/lnd/chainntnfs" "github.com/lightningnetwork/lnd/fn" "github.com/lightningnetwork/lnd/input" + "github.com/lightningnetwork/lnd/lnwallet" "github.com/lightningnetwork/lnd/lnwallet/chainfee" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" @@ -667,8 +668,11 @@ func TestSweepPendingInputs(t *testing.T) { Wallet: wallet, Aggregator: aggregator, Publisher: publisher, - GenSweepScript: func() ([]byte, error) { - return testPubKey.SerializeCompressed(), nil + GenSweepScript: func() fn.Result[lnwallet.AddrWithKey] { + //nolint:lll + return fn.Ok(lnwallet.AddrWithKey{ + DeliveryAddress: testPubKey.SerializeCompressed(), + }) }, NoDeadlineConfTarget: uint32(DefaultDeadlineDelta), }) From fc02cd7b0c9dbb2fe0d93f1071ea4a4efb586efa Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Mon, 3 Jun 2024 23:02:40 -0700 Subject: [PATCH 148/218] input: refactor all inputs to use MakeBaseInput, add opts In this commit, we refactor all the other constructors for the input to use MakeBaseInput. We also add a new set of functional options as well. This'll be useful later on to ensure that new options are properly applied to all the input types. --- input/input.go | 172 ++++++++++++++++++++++++++----------------------- input/mocks.go | 13 ++++ 2 files changed, 104 insertions(+), 81 deletions(-) diff --git a/input/input.go b/input/input.go index aef524a4c9..014c26f0ba 100644 --- a/input/input.go +++ b/input/input.go @@ -156,6 +156,18 @@ func (i *inputKit) UnconfParent() *TxInfo { return i.unconfParent } +// inputOpts holds options for the input. +type inputOpts struct { +} + +// defaultInputOpts returns a new inputOpts with default values. +func defaultInputOpts() *inputOpts { + return &inputOpts{} +} + +// InputOpt is a functional option argument to the input constructor. +type InputOpt func(*inputOpts) //nolint:revive + // BaseInput contains all the information needed to sweep a basic // output (CSV/CLTV/no time lock). type BaseInput struct { @@ -166,7 +178,12 @@ type BaseInput struct { // sweep transaction. func MakeBaseInput(outpoint *wire.OutPoint, witnessType WitnessType, signDescriptor *SignDescriptor, heightHint uint32, - unconfParent *TxInfo) BaseInput { + unconfParent *TxInfo, opts ...InputOpt) BaseInput { + + opt := defaultInputOpts() + for _, optF := range opts { + optF(opt) + } return BaseInput{ inputKit{ @@ -182,10 +199,11 @@ func MakeBaseInput(outpoint *wire.OutPoint, witnessType WitnessType, // NewBaseInput allocates and assembles a new *BaseInput that can be used to // construct a sweep transaction. func NewBaseInput(outpoint *wire.OutPoint, witnessType WitnessType, - signDescriptor *SignDescriptor, heightHint uint32) *BaseInput { + signDescriptor *SignDescriptor, heightHint uint32, + opts ...InputOpt) *BaseInput { input := MakeBaseInput( - outpoint, witnessType, signDescriptor, heightHint, nil, + outpoint, witnessType, signDescriptor, heightHint, nil, opts..., ) return &input @@ -195,36 +213,31 @@ func NewBaseInput(outpoint *wire.OutPoint, witnessType WitnessType, // construct a sweep transaction. func NewCsvInput(outpoint *wire.OutPoint, witnessType WitnessType, signDescriptor *SignDescriptor, heightHint uint32, - blockToMaturity uint32) *BaseInput { + blockToMaturity uint32, opts ...InputOpt) *BaseInput { - return &BaseInput{ - inputKit{ - outpoint: *outpoint, - witnessType: witnessType, - signDesc: *signDescriptor, - heightHint: heightHint, - blockToMaturity: blockToMaturity, - }, - } + input := MakeBaseInput( + outpoint, witnessType, signDescriptor, heightHint, nil, opts..., + ) + + input.blockToMaturity = blockToMaturity + + return &input } // NewCsvInputWithCltv assembles a new csv and cltv locked input that can be // used to construct a sweep transaction. func NewCsvInputWithCltv(outpoint *wire.OutPoint, witnessType WitnessType, signDescriptor *SignDescriptor, heightHint uint32, - csvDelay uint32, cltvExpiry uint32) *BaseInput { + csvDelay uint32, cltvExpiry uint32, opts ...InputOpt) *BaseInput { - return &BaseInput{ - inputKit{ - outpoint: *outpoint, - witnessType: witnessType, - signDesc: *signDescriptor, - heightHint: heightHint, - blockToMaturity: csvDelay, - cltvExpiry: cltvExpiry, - unconfParent: nil, - }, - } + input := MakeBaseInput( + outpoint, witnessType, signDescriptor, heightHint, nil, opts..., + ) + + input.blockToMaturity = csvDelay + input.cltvExpiry = cltvExpiry + + return &input } // CraftInputScript returns a valid set of input scripts allowing this output @@ -256,16 +269,16 @@ type HtlcSucceedInput struct { // construct a sweep transaction. func MakeHtlcSucceedInput(outpoint *wire.OutPoint, signDescriptor *SignDescriptor, preimage []byte, heightHint, - blocksToMaturity uint32) HtlcSucceedInput { + blocksToMaturity uint32, opts ...InputOpt) HtlcSucceedInput { + + input := MakeBaseInput( + outpoint, HtlcAcceptedRemoteSuccess, signDescriptor, + heightHint, nil, opts..., + ) + input.blockToMaturity = blocksToMaturity return HtlcSucceedInput{ - inputKit: inputKit{ - outpoint: *outpoint, - witnessType: HtlcAcceptedRemoteSuccess, - signDesc: *signDescriptor, - heightHint: heightHint, - blockToMaturity: blocksToMaturity, - }, + inputKit: input.inputKit, preimage: preimage, } } @@ -274,16 +287,17 @@ func MakeHtlcSucceedInput(outpoint *wire.OutPoint, // to spend an HTLC output for a taproot channel on the remote party's // commitment transaction. func MakeTaprootHtlcSucceedInput(op *wire.OutPoint, signDesc *SignDescriptor, - preimage []byte, heightHint, blocksToMaturity uint32) HtlcSucceedInput { + preimage []byte, heightHint, blocksToMaturity uint32, + opts ...InputOpt) HtlcSucceedInput { + + input := MakeBaseInput( + op, TaprootHtlcAcceptedRemoteSuccess, signDesc, + heightHint, nil, opts..., + ) + input.blockToMaturity = blocksToMaturity return HtlcSucceedInput{ - inputKit: inputKit{ - outpoint: *op, - witnessType: TaprootHtlcAcceptedRemoteSuccess, - signDesc: *signDesc, - heightHint: heightHint, - blockToMaturity: blocksToMaturity, - }, + inputKit: input.inputKit, preimage: preimage, } } @@ -388,7 +402,8 @@ func (i *HtlcSecondLevelAnchorInput) CraftInputScript(signer Signer, // to spend the HTLC output on our commit using the second level timeout // transaction. func MakeHtlcSecondLevelTimeoutAnchorInput(signedTx *wire.MsgTx, - signDetails *SignDetails, heightHint uint32) HtlcSecondLevelAnchorInput { + signDetails *SignDetails, heightHint uint32, + opts ...InputOpt) HtlcSecondLevelAnchorInput { // Spend an HTLC output on our local commitment tx using the // 2nd timeout transaction. @@ -408,16 +423,15 @@ func MakeHtlcSecondLevelTimeoutAnchorInput(signedTx *wire.MsgTx, ) } + input := MakeBaseInput( + &signedTx.TxIn[0].PreviousOutPoint, + HtlcOfferedTimeoutSecondLevelInputConfirmed, + &signDetails.SignDesc, heightHint, nil, opts..., + ) + input.blockToMaturity = 1 + return HtlcSecondLevelAnchorInput{ - inputKit: inputKit{ - outpoint: signedTx.TxIn[0].PreviousOutPoint, - witnessType: HtlcOfferedTimeoutSecondLevelInputConfirmed, - signDesc: signDetails.SignDesc, - heightHint: heightHint, - - // CSV delay is always 1 for these inputs. - blockToMaturity: 1, - }, + inputKit: input.inputKit, SignedTx: signedTx, createWitness: createWitness, } @@ -429,7 +443,7 @@ func MakeHtlcSecondLevelTimeoutAnchorInput(signedTx *wire.MsgTx, // sweep the second level HTLC aggregated with other transactions. func MakeHtlcSecondLevelTimeoutTaprootInput(signedTx *wire.MsgTx, signDetails *SignDetails, - heightHint uint32) HtlcSecondLevelAnchorInput { + heightHint uint32, opts ...InputOpt) HtlcSecondLevelAnchorInput { createWitness := func(signer Signer, txn *wire.MsgTx, hashCache *txscript.TxSigHashes, @@ -453,16 +467,15 @@ func MakeHtlcSecondLevelTimeoutTaprootInput(signedTx *wire.MsgTx, ) } + input := MakeBaseInput( + &signedTx.TxIn[0].PreviousOutPoint, + TaprootHtlcLocalOfferedTimeout, + &signDetails.SignDesc, heightHint, nil, opts..., + ) + input.blockToMaturity = 1 + return HtlcSecondLevelAnchorInput{ - inputKit: inputKit{ - outpoint: signedTx.TxIn[0].PreviousOutPoint, - witnessType: TaprootHtlcLocalOfferedTimeout, - signDesc: signDetails.SignDesc, - heightHint: heightHint, - - // CSV delay is always 1 for these inputs. - blockToMaturity: 1, - }, + inputKit: input.inputKit, SignedTx: signedTx, createWitness: createWitness, } @@ -473,7 +486,7 @@ func MakeHtlcSecondLevelTimeoutTaprootInput(signedTx *wire.MsgTx, // transaction. func MakeHtlcSecondLevelSuccessAnchorInput(signedTx *wire.MsgTx, signDetails *SignDetails, preimage lntypes.Preimage, - heightHint uint32) HtlcSecondLevelAnchorInput { + heightHint uint32, opts ...InputOpt) HtlcSecondLevelAnchorInput { // Spend an HTLC output on our local commitment tx using the 2nd // success transaction. @@ -492,18 +505,16 @@ func MakeHtlcSecondLevelSuccessAnchorInput(signedTx *wire.MsgTx, preimage[:], signer, &desc, txn, ) } + input := MakeBaseInput( + &signedTx.TxIn[0].PreviousOutPoint, + HtlcAcceptedSuccessSecondLevelInputConfirmed, + &signDetails.SignDesc, heightHint, nil, opts..., + ) + input.blockToMaturity = 1 return HtlcSecondLevelAnchorInput{ - inputKit: inputKit{ - outpoint: signedTx.TxIn[0].PreviousOutPoint, - witnessType: HtlcAcceptedSuccessSecondLevelInputConfirmed, - signDesc: signDetails.SignDesc, - heightHint: heightHint, - - // CSV delay is always 1 for these inputs. - blockToMaturity: 1, - }, SignedTx: signedTx, + inputKit: input.inputKit, createWitness: createWitness, } } @@ -513,7 +524,7 @@ func MakeHtlcSecondLevelSuccessAnchorInput(signedTx *wire.MsgTx, // commitment transaction. func MakeHtlcSecondLevelSuccessTaprootInput(signedTx *wire.MsgTx, signDetails *SignDetails, preimage lntypes.Preimage, - heightHint uint32) HtlcSecondLevelAnchorInput { + heightHint uint32, opts ...InputOpt) HtlcSecondLevelAnchorInput { createWitness := func(signer Signer, txn *wire.MsgTx, hashCache *txscript.TxSigHashes, @@ -537,16 +548,15 @@ func MakeHtlcSecondLevelSuccessTaprootInput(signedTx *wire.MsgTx, ) } + input := MakeBaseInput( + &signedTx.TxIn[0].PreviousOutPoint, + TaprootHtlcAcceptedLocalSuccess, + &signDetails.SignDesc, heightHint, nil, opts..., + ) + input.blockToMaturity = 1 + return HtlcSecondLevelAnchorInput{ - inputKit: inputKit{ - outpoint: signedTx.TxIn[0].PreviousOutPoint, - witnessType: TaprootHtlcAcceptedLocalSuccess, - signDesc: signDetails.SignDesc, - heightHint: heightHint, - - // CSV delay is always 1 for these inputs. - blockToMaturity: 1, - }, + inputKit: input.inputKit, SignedTx: signedTx, createWitness: createWitness, } diff --git a/input/mocks.go b/input/mocks.go index 2f38400d81..695525955c 100644 --- a/input/mocks.go +++ b/input/mocks.go @@ -8,8 +8,10 @@ import ( "github.com/btcsuite/btcd/btcec/v2/schnorr/musig2" "github.com/btcsuite/btcd/txscript" "github.com/btcsuite/btcd/wire" + "github.com/lightningnetwork/lnd/fn" "github.com/lightningnetwork/lnd/keychain" "github.com/lightningnetwork/lnd/lntypes" + "github.com/lightningnetwork/lnd/tlv" "github.com/stretchr/testify/mock" ) @@ -127,6 +129,17 @@ func (m *MockInput) UnconfParent() *TxInfo { return info.(*TxInfo) } +func (m *MockInput) ResolutionBlob() fn.Option[tlv.Blob] { + args := m.Called() + + info := args.Get(0) + if info == nil { + return fn.None[tlv.Blob]() + } + + return info.(fn.Option[tlv.Blob]) +} + // MockWitnessType implements the `WitnessType` interface and is used by other // packages for mock testing. type MockWitnessType struct { From 080771db62c9ebed36ccff7bf750bf4b0891e6a9 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Mon, 3 Jun 2024 23:02:57 -0700 Subject: [PATCH 149/218] input: add ResolutionBlob method to inputKit We also update breachedOutput w/ the new API. --- contractcourt/breach_arbitrator.go | 12 +++++++++ input/input.go | 43 +++++++++++++++++++++++++----- 2 files changed, 48 insertions(+), 7 deletions(-) diff --git a/contractcourt/breach_arbitrator.go b/contractcourt/breach_arbitrator.go index 9617c1ebee..98858d64e0 100644 --- a/contractcourt/breach_arbitrator.go +++ b/contractcourt/breach_arbitrator.go @@ -15,6 +15,7 @@ import ( "github.com/btcsuite/btcd/wire" "github.com/lightningnetwork/lnd/chainntnfs" "github.com/lightningnetwork/lnd/channeldb" + "github.com/lightningnetwork/lnd/fn" "github.com/lightningnetwork/lnd/input" "github.com/lightningnetwork/lnd/kvdb" "github.com/lightningnetwork/lnd/labels" @@ -22,6 +23,7 @@ import ( "github.com/lightningnetwork/lnd/lnutils" "github.com/lightningnetwork/lnd/lnwallet" "github.com/lightningnetwork/lnd/lnwallet/chainfee" + "github.com/lightningnetwork/lnd/tlv" ) const ( @@ -1067,6 +1069,10 @@ type breachedOutput struct { secondLevelTapTweak [32]byte witnessFunc input.WitnessGenerator + + resolutionBlob fn.Option[tlv.Blob] + + // TODO(roasbeef): function opt and hook into brar } // makeBreachedOutput assembles a new breachedOutput that can be used by the @@ -1174,6 +1180,12 @@ func (bo *breachedOutput) UnconfParent() *input.TxInfo { return nil } +// ResolutionBlob returns a special opaque blob to be used to sweep/resolve this +// input. +func (bo *breachedOutput) ResolutionBlob() fn.Option[tlv.Blob] { + return bo.resolutionBlob +} + // Add compile-time constraint ensuring breachedOutput implements the Input // interface. var _ input.Input = (*breachedOutput)(nil) diff --git a/input/input.go b/input/input.go index 014c26f0ba..6693e9fa8a 100644 --- a/input/input.go +++ b/input/input.go @@ -6,7 +6,9 @@ import ( "github.com/btcsuite/btcd/btcutil" "github.com/btcsuite/btcd/txscript" "github.com/btcsuite/btcd/wire" + "github.com/lightningnetwork/lnd/fn" "github.com/lightningnetwork/lnd/lntypes" + "github.com/lightningnetwork/lnd/tlv" ) // EmptyOutPoint is a zeroed outpoint. @@ -63,6 +65,10 @@ type Input interface { // UnconfParent returns information about a possibly unconfirmed parent // tx. UnconfParent() *TxInfo + + // ResolutionBlob returns a special opaque blob to be used to + // sweep/resolve this input. + ResolutionBlob() fn.Option[tlv.Blob] } // TxInfo describes properties of a parent tx that are relevant for CPFP. @@ -106,6 +112,10 @@ type inputKit struct { // unconfParent contains information about a potential unconfirmed // parent transaction. unconfParent *TxInfo + + // resolutionBlob is an optional blob that can be used to resolve an + // input. + resolutionBlob fn.Option[tlv.Blob] } // OutPoint returns the breached output's identifier that is to be included as @@ -156,8 +166,17 @@ func (i *inputKit) UnconfParent() *TxInfo { return i.unconfParent } -// inputOpts holds options for the input. +// ResolutionBlob returns a special opaque blob to be used to sweep/resolve +// this input. +func (i *inputKit) ResolutionBlob() fn.Option[tlv.Blob] { + return i.resolutionBlob +} + +// inputOpts contains options for constructing a new input. type inputOpts struct { + // resolutionBlob is an optional blob that can be used to resolve an + // input. + resolutionBlob fn.Option[tlv.Blob] } // defaultInputOpts returns a new inputOpts with default values. @@ -165,9 +184,18 @@ func defaultInputOpts() *inputOpts { return &inputOpts{} } -// InputOpt is a functional option argument to the input constructor. +// InputOpt is a functional option that can be used to modify the default input +// options. type InputOpt func(*inputOpts) //nolint:revive +// WithResolutionBlob is an option that can be used to set a resolution blob on +// for an input. +func WithResolutionBlob(b fn.Option[tlv.Blob]) InputOpt { + return func(o *inputOpts) { + o.resolutionBlob = b + } +} + // BaseInput contains all the information needed to sweep a basic // output (CSV/CLTV/no time lock). type BaseInput struct { @@ -187,11 +215,12 @@ func MakeBaseInput(outpoint *wire.OutPoint, witnessType WitnessType, return BaseInput{ inputKit{ - outpoint: *outpoint, - witnessType: witnessType, - signDesc: *signDescriptor, - heightHint: heightHint, - unconfParent: unconfParent, + outpoint: *outpoint, + witnessType: witnessType, + signDesc: *signDescriptor, + heightHint: heightHint, + unconfParent: unconfParent, + resolutionBlob: opt.resolutionBlob, }, } } From fef368bb38727740b901c5ff2afb22ac48e18f7d Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Mon, 3 Jun 2024 23:03:40 -0700 Subject: [PATCH 150/218] contractcourt: set resolution blob in commitSweepResolver --- contractcourt/commit_sweep_resolver.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/contractcourt/commit_sweep_resolver.go b/contractcourt/commit_sweep_resolver.go index f2392192e8..8f11695801 100644 --- a/contractcourt/commit_sweep_resolver.go +++ b/contractcourt/commit_sweep_resolver.go @@ -345,12 +345,18 @@ func (c *commitSweepResolver) Resolve(_ bool) (ContractResolver, error) { &c.commitResolution.SelfOutputSignDesc, c.broadcastHeight, c.commitResolution.MaturityDelay, c.leaseExpiry, + input.WithResolutionBlob( + c.commitResolution.ResolutionBlob, + ), ) } else { inp = input.NewCsvInput( &c.commitResolution.SelfOutPoint, witnessType, &c.commitResolution.SelfOutputSignDesc, c.broadcastHeight, c.commitResolution.MaturityDelay, + input.WithResolutionBlob( + c.commitResolution.ResolutionBlob, + ), ) } From 3726cfa31961778f2f3a88ff5ec01dae989da050 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Mon, 3 Jun 2024 23:05:19 -0700 Subject: [PATCH 151/218] server+sweep: convert GenSweepScript to use new addr type We convert it to use lnwallet.AddrWithKey, as in the future, knowing the internal key for an address will be useful. --- sweep/sweeper.go | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/sweep/sweeper.go b/sweep/sweeper.go index 92d2051185..c151940943 100644 --- a/sweep/sweeper.go +++ b/sweep/sweeper.go @@ -298,7 +298,7 @@ type UtxoSweeper struct { // to sweep. inputs InputsMap - currentOutputScript []byte + currentOutputScript fn.Option[lnwallet.AddrWithKey] relayFeeRate chainfee.SatPerKWeight @@ -796,12 +796,19 @@ func (s *UtxoSweeper) signalResult(pi *SweeperInput, result Result) { // the tx. The output address is only marked as used if the publish succeeds. func (s *UtxoSweeper) sweep(set InputSet) error { // Generate an output script if there isn't an unused script available. - if s.currentOutputScript == nil { - pkScript, err := s.cfg.GenSweepScript().Unpack() + if s.currentOutputScript.IsNone() { + addr, err := s.cfg.GenSweepScript().Unpack() if err != nil { return fmt.Errorf("gen sweep script: %w", err) } - s.currentOutputScript = pkScript.DeliveryAddress + s.currentOutputScript = fn.Some(addr) + } + + sweepAddr, err := s.currentOutputScript.UnwrapOrErr( + fmt.Errorf("none sweep script"), + ) + if err != nil { + return err } // Create a fee bump request and ask the publisher to broadcast it. The @@ -811,7 +818,7 @@ func (s *UtxoSweeper) sweep(set InputSet) error { Inputs: set.Inputs(), Budget: set.Budget(), DeadlineHeight: set.DeadlineHeight(), - DeliveryAddress: s.currentOutputScript, + DeliveryAddress: sweepAddr.DeliveryAddress, MaxFeeRate: s.cfg.MaxFeeRate.FeePerKWeight(), StartingFeeRate: set.StartingFeeRate(), // TODO(yy): pass the strategy here. @@ -1702,10 +1709,10 @@ func (s *UtxoSweeper) handleBumpEventTxPublished(r *BumpResult) error { log.Debugf("Published sweep tx %v, num_inputs=%v, height=%v", tx.TxHash(), len(tx.TxIn), s.currentHeight) - // If there's no error, remove the output script. Otherwise - // keep it so that it can be reused for the next transaction - // and causes no address inflation. - s.currentOutputScript = nil + // If there's no error, remove the output script. Otherwise keep it so + // that it can be reused for the next transaction and causes no address + // inflation. + s.currentOutputScript = fn.None[lnwallet.AddrWithKey]() return nil } From 23e99ddd4de6c1879a532dd66adde87fc61f6ce7 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Mon, 3 Jun 2024 23:06:17 -0700 Subject: [PATCH 152/218] sweep: add new AuxSweeper interface In this commit, we add a new AuxSweeper interface. This'll take a set of inputs, and a change addr for the sweep transaction, then optionally return a new sweep output to be added to the sweep transaction. We also add a new NotifyBroadcast method. This'll be used to notify that we're _about_ to broadcast a sweeping transaction. The set of inputs is passed in, which allows the caller to prepare for the ultimate broadcast of the sweeping transaction. We also add ExtraTxOut to BumpRequest pass fees to NotifyBroadcast. This allows the callee to know the total fee of the sweeping transaction. --- lnwallet/interface.go | 2 +- server.go | 7 +-- sweep/fee_bumper.go | 121 +++++++++++++++++++++++++++------------ sweep/fee_bumper_test.go | 45 +++++++++------ sweep/interface.go | 32 +++++++++++ sweep/mock_test.go | 19 ++++++ sweep/sweeper.go | 2 +- 7 files changed, 166 insertions(+), 62 deletions(-) diff --git a/lnwallet/interface.go b/lnwallet/interface.go index 85317d6852..0a27ece1ed 100644 --- a/lnwallet/interface.go +++ b/lnwallet/interface.go @@ -600,7 +600,7 @@ type MessageSigner interface { type AddrWithKey struct { lnwire.DeliveryAddress - InternalKey fn.Option[btcec.PublicKey] + InternalKey fn.Option[keychain.KeyDescriptor] // TODO(roasbeef): consolidate w/ instance in chan closer } diff --git a/server.go b/server.go index 4f880263f0..a8296d7c70 100644 --- a/server.go +++ b/server.go @@ -4962,12 +4962,7 @@ func newSweepPkScriptGen( return fn.Ok(lnwallet.AddrWithKey{ DeliveryAddress: addr, - InternalKey: fn.MapOption(func( - desc keychain.KeyDescriptor) btcec.PublicKey { - - return *desc.PubKey - }, - )(internalKeyDesc), + InternalKey: internalKeyDesc, }) } } diff --git a/sweep/fee_bumper.go b/sweep/fee_bumper.go index 1d3fa5aedd..5afa95bac0 100644 --- a/sweep/fee_bumper.go +++ b/sweep/fee_bumper.go @@ -110,7 +110,7 @@ type BumpRequest struct { DeadlineHeight int32 // DeliveryAddress is the script to send the change output to. - DeliveryAddress []byte + DeliveryAddress lnwallet.AddrWithKey // MaxFeeRate is the maximum fee rate that can be used for fee bumping. MaxFeeRate chainfee.SatPerKWeight @@ -118,6 +118,10 @@ type BumpRequest struct { // StartingFeeRate is an optional parameter that can be used to specify // the initial fee rate to use for the fee function. StartingFeeRate fn.Option[chainfee.SatPerKWeight] + + // ExtraTxOut tracks if this bump request has an optional set of extra + // outputs to add to the transaction. + ExtraTxOut fn.Option[SweepOutput] } // MaxFeeRateAllowed returns the maximum fee rate allowed for the given @@ -127,7 +131,9 @@ type BumpRequest struct { func (r *BumpRequest) MaxFeeRateAllowed() (chainfee.SatPerKWeight, error) { // Get the size of the sweep tx, which will be used to calculate the // budget fee rate. - size, err := calcSweepTxWeight(r.Inputs, r.DeliveryAddress) + size, err := calcSweepTxWeight( + r.Inputs, r.DeliveryAddress.DeliveryAddress, + ) if err != nil { return 0, err } @@ -248,6 +254,10 @@ type TxPublisherConfig struct { // Notifier is used to monitor the confirmation status of the tx. Notifier chainntnfs.ChainNotifier + + // AuxSweeper is an optional interface that can be used to modify the + // way sweep transaction are generated. + AuxSweeper fn.Option[AuxSweeper] } // TxPublisher is an implementation of the Bumper interface. It utilizes the @@ -401,16 +411,18 @@ func (t *TxPublisher) createRBFCompliantTx(req *BumpRequest, for { // Create a new tx with the given fee rate and check its // mempool acceptance. - tx, fee, err := t.createAndCheckTx(req, f) + sweepCtx, err := t.createAndCheckTx(req, f) switch { case err == nil: // The tx is valid, return the request ID. - requestID := t.storeRecord(tx, req, f, fee) + requestID := t.storeRecord( + sweepCtx.tx, req, f, sweepCtx.fee, + ) log.Infof("Created tx %v for %v inputs: feerate=%v, "+ - "fee=%v, inputs=%v", tx.TxHash(), - len(req.Inputs), f.FeeRate(), fee, + "fee=%v, inputs=%v", sweepCtx.tx.TxHash(), + len(req.Inputs), f.FeeRate(), sweepCtx.fee, inputTypeSummary(req.Inputs)) return requestID, nil @@ -421,8 +433,8 @@ func (t *TxPublisher) createRBFCompliantTx(req *BumpRequest, // We should at least start with a feerate above the // mempool min feerate, so if we get this error, it // means something is wrong earlier in the pipeline. - log.Errorf("Current fee=%v, feerate=%v, %v", fee, - f.FeeRate(), err) + log.Errorf("Current fee=%v, feerate=%v, %v", + sweepCtx.fee, f.FeeRate(), err) fallthrough @@ -434,8 +446,8 @@ func (t *TxPublisher) createRBFCompliantTx(req *BumpRequest, // increased or maxed out. for !increased { log.Debugf("Increasing fee for next round, "+ - "current fee=%v, feerate=%v", fee, - f.FeeRate()) + "current fee=%v, feerate=%v", + sweepCtx.fee, f.FeeRate()) // If the fee function tells us that we have // used up the budget, we will return an error @@ -484,30 +496,34 @@ func (t *TxPublisher) storeRecord(tx *wire.MsgTx, req *BumpRequest, // script, and the fee rate. In addition, it validates the tx's mempool // acceptance before returning a tx that can be published directly, along with // its fee. -func (t *TxPublisher) createAndCheckTx(req *BumpRequest, f FeeFunction) ( - *wire.MsgTx, btcutil.Amount, error) { +func (t *TxPublisher) createAndCheckTx(req *BumpRequest, + f FeeFunction) (*sweepTxCtx, error) { // Create the sweep tx with max fee rate of 0 as the fee function // guarantees the fee rate used here won't exceed the max fee rate. - tx, fee, err := t.createSweepTx( + sweepCtx, err := t.createSweepTx( req.Inputs, req.DeliveryAddress, f.FeeRate(), ) if err != nil { - return nil, fee, fmt.Errorf("create sweep tx: %w", err) + return sweepCtx, fmt.Errorf("create sweep tx: %w", err) } // Sanity check the budget still covers the fee. - if fee > req.Budget { - return nil, fee, fmt.Errorf("%w: budget=%v, fee=%v", - ErrNotEnoughBudget, req.Budget, fee) + if sweepCtx.fee > req.Budget { + return sweepCtx, fmt.Errorf("%w: budget=%v, fee=%v", + ErrNotEnoughBudget, req.Budget, sweepCtx.fee) } + // If we had an extra txOut, then we'll update the result to include + // it. + req.ExtraTxOut = sweepCtx.extraTxOut + // Validate the tx's mempool acceptance. - err = t.cfg.Wallet.CheckMempoolAcceptance(tx) + err = t.cfg.Wallet.CheckMempoolAcceptance(sweepCtx.tx) // Exit early if the tx is valid. if err == nil { - return tx, fee, nil + return sweepCtx, nil } // Print an error log if the chain backend doesn't support the mempool @@ -515,18 +531,18 @@ func (t *TxPublisher) createAndCheckTx(req *BumpRequest, f FeeFunction) ( if errors.Is(err, rpcclient.ErrBackendVersion) { log.Errorf("TestMempoolAccept not supported by backend, " + "consider upgrading it to a newer version") - return tx, fee, nil + return sweepCtx, nil } // We are running on a backend that doesn't implement the RPC // testmempoolaccept, eg, neutrino, so we'll skip the check. if errors.Is(err, chain.ErrUnimplemented) { log.Debug("Skipped testmempoolaccept due to not implemented") - return tx, fee, nil + return sweepCtx, nil } - return nil, fee, fmt.Errorf("tx=%v failed mempool check: %w", - tx.TxHash(), err) + return sweepCtx, fmt.Errorf("tx=%v failed mempool check: %w", + sweepCtx.tx.TxHash(), err) } // broadcast takes a monitored tx and publishes it to the network. Prior to the @@ -547,6 +563,15 @@ func (t *TxPublisher) broadcast(requestID uint64) (*BumpResult, error) { log.Debugf("Publishing sweep tx %v, num_inputs=%v, height=%v", txid, len(tx.TxIn), t.currentHeight.Load()) + // Before we go to broadcast, we'll notify the aux sweeper, if it's + // present of this new broadcast attempt. + err := fn.MapOptionZ(t.cfg.AuxSweeper, func(aux AuxSweeper) error { + return aux.NotifyBroadcast(record.req, tx, record.fee) + }) + if err != nil { + return nil, fmt.Errorf("unable to notify aux sweeper: %w", err) + } + // Set the event, and change it to TxFailed if the wallet fails to // publish it. event := TxPublished @@ -554,7 +579,7 @@ func (t *TxPublisher) broadcast(requestID uint64) (*BumpResult, error) { // Publish the sweeping tx with customized label. If the publish fails, // this error will be saved in the `BumpResult` and it will be removed // from being monitored. - err := t.cfg.Wallet.PublishTransaction( + err = t.cfg.Wallet.PublishTransaction( tx, labels.MakeLabel(labels.LabelTypeSweepTransaction, nil), ) if err != nil { @@ -933,7 +958,7 @@ func (t *TxPublisher) createAndPublishTx(requestID uint64, // NOTE: The fee function is expected to have increased its returned // fee rate after calling the SkipFeeBump method. So we can use it // directly here. - tx, fee, err := t.createAndCheckTx(r.req, r.feeFunction) + sweepCtx, err := t.createAndCheckTx(r.req, r.feeFunction) // If the error is fee related, we will return no error and let the fee // bumper retry it at next block. @@ -980,17 +1005,17 @@ func (t *TxPublisher) createAndPublishTx(requestID uint64, // The tx has been created without any errors, we now register a new // record by overwriting the same requestID. t.records.Store(requestID, &monitorRecord{ - tx: tx, + tx: sweepCtx.tx, req: r.req, feeFunction: r.feeFunction, - fee: fee, + fee: sweepCtx.fee, }) // Attempt to broadcast this new tx. result, err := t.broadcast(requestID) if err != nil { log.Infof("Failed to broadcast replacement tx %v: %v", - tx.TxHash(), err) + sweepCtx.tx.TxHash(), err) return fn.None[BumpResult]() } @@ -1016,7 +1041,8 @@ func (t *TxPublisher) createAndPublishTx(requestID uint64, return fn.Some(*result) } - log.Infof("Replaced tx=%v with new tx=%v", oldTx.TxHash(), tx.TxHash()) + log.Infof("Replaced tx=%v with new tx=%v", oldTx.TxHash(), + sweepCtx.tx.TxHash()) // Otherwise, it's a successful RBF, set the event and return. result.Event = TxReplaced @@ -1129,17 +1155,28 @@ func calcCurrentConfTarget(currentHeight, deadline int32) uint32 { return confTarget } +// sweepTxCtx houses a sweep transaction with additional context. +type sweepTxCtx struct { + tx *wire.MsgTx + + fee btcutil.Amount + + extraTxOut fn.Option[SweepOutput] +} + // createSweepTx creates a sweeping tx based on the given inputs, change // address and fee rate. -func (t *TxPublisher) createSweepTx(inputs []input.Input, changePkScript []byte, - feeRate chainfee.SatPerKWeight) (*wire.MsgTx, btcutil.Amount, error) { +func (t *TxPublisher) createSweepTx(inputs []input.Input, + changePkScript lnwallet.AddrWithKey, + feeRate chainfee.SatPerKWeight) (*sweepTxCtx, error) { // Validate and calculate the fee and change amount. txFee, changeAmtOpt, locktimeOpt, err := prepareSweepTx( - inputs, changePkScript, feeRate, t.currentHeight.Load(), + inputs, changePkScript.DeliveryAddress, feeRate, + t.currentHeight.Load(), ) if err != nil { - return nil, 0, err + return nil, err } var ( @@ -1185,7 +1222,7 @@ func (t *TxPublisher) createSweepTx(inputs []input.Input, changePkScript []byte, // If there's a change amount, add it to the transaction. changeAmtOpt.WhenSome(func(changeAmt btcutil.Amount) { sweepTx.AddTxOut(&wire.TxOut{ - PkScript: changePkScript, + PkScript: changePkScript.DeliveryAddress, Value: int64(changeAmt), }) }) @@ -1196,7 +1233,7 @@ func (t *TxPublisher) createSweepTx(inputs []input.Input, changePkScript []byte, prevInputFetcher, err := input.MultiPrevOutFetcher(inputs) if err != nil { - return nil, 0, fmt.Errorf("error creating prev input fetcher "+ + return nil, fmt.Errorf("error creating prev input fetcher "+ "for hash cache: %v", err) } hashCache := txscript.NewTxSigHashes(sweepTx, prevInputFetcher) @@ -1224,14 +1261,17 @@ func (t *TxPublisher) createSweepTx(inputs []input.Input, changePkScript []byte, for idx, inp := range idxs { if err := addInputScript(idx, inp); err != nil { - return nil, 0, err + return nil, err } } log.Debugf("Created sweep tx %v for inputs:\n%v", sweepTx.TxHash(), inputTypeSummary(inputs)) - return sweepTx, txFee, nil + return &sweepTxCtx{ + tx: sweepTx, + fee: txFee, + }, nil } // prepareSweepTx returns the tx fee, an optional change amount and an optional @@ -1323,7 +1363,8 @@ func prepareSweepTx(inputs []input.Input, changePkScript []byte, changeFloor := lnwallet.DustLimitForSize(len(changePkScript)) // If the change amount is dust, we'll move it into the fees. - if changeAmt < changeFloor { + switch { + case changeAmt < changeFloor: log.Infof("Change amt %v below dustlimit %v, not adding "+ "change output", changeAmt, changeFloor) @@ -1340,6 +1381,10 @@ func prepareSweepTx(inputs []input.Input, changePkScript []byte, // Set the change amount to none. changeAmtOpt = fn.None[btcutil.Amount]() + + // Otherwise, we'll actually recognize it as a change output. + default: + // TODO(roasbeef): Implement (later commit in this PR). } // Optionally set the locktime. diff --git a/sweep/fee_bumper_test.go b/sweep/fee_bumper_test.go index 4c4a9519fe..db9dd40457 100644 --- a/sweep/fee_bumper_test.go +++ b/sweep/fee_bumper_test.go @@ -11,6 +11,7 @@ import ( "github.com/btcsuite/btcd/wire" "github.com/btcsuite/btcwallet/chain" "github.com/lightningnetwork/lnd/chainntnfs" + "github.com/lightningnetwork/lnd/fn" "github.com/lightningnetwork/lnd/input" "github.com/lightningnetwork/lnd/keychain" "github.com/lightningnetwork/lnd/lnwallet" @@ -21,12 +22,14 @@ import ( var ( // Create a taproot change script. - changePkScript = []byte{ - 0x51, 0x20, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + changePkScript = lnwallet.AddrWithKey{ + DeliveryAddress: []byte{ + 0x51, 0x20, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + }, } testInputCount atomic.Uint64 @@ -117,7 +120,9 @@ func TestCalcSweepTxWeight(t *testing.T) { require.Zero(t, weight) // Use a correct change script to test the success case. - weight, err = calcSweepTxWeight([]input.Input{&inp}, changePkScript) + weight, err = calcSweepTxWeight( + []input.Input{&inp}, changePkScript.DeliveryAddress, + ) require.NoError(t, err) // BaseTxSize 8 bytes @@ -137,7 +142,9 @@ func TestBumpRequestMaxFeeRateAllowed(t *testing.T) { inp := createTestInput(100, input.WitnessKeyHash) // The weight is 487. - weight, err := calcSweepTxWeight([]input.Input{&inp}, changePkScript) + weight, err := calcSweepTxWeight( + []input.Input{&inp}, changePkScript.DeliveryAddress, + ) require.NoError(t, err) // Define a test budget and calculates its fee rate. @@ -154,7 +161,9 @@ func TestBumpRequestMaxFeeRateAllowed(t *testing.T) { // Use a wrong change script to test the error case. name: "error calc weight", req: &BumpRequest{ - DeliveryAddress: []byte{1}, + DeliveryAddress: lnwallet.AddrWithKey{ + DeliveryAddress: []byte{1}, + }, }, expectedMaxFeeRate: 0, expectedErr: true, @@ -239,7 +248,8 @@ func TestInitializeFeeFunction(t *testing.T) { // Create a publisher using the mocks. tp := NewTxPublisher(TxPublisherConfig{ - Estimator: estimator, + Estimator: estimator, + AuxSweeper: fn.Some[AuxSweeper](&MockAuxSweeper{}), }) // Create a test feerate. @@ -304,7 +314,9 @@ func TestStoreRecord(t *testing.T) { tx := &wire.MsgTx{} // Create a publisher using the mocks. - tp := NewTxPublisher(TxPublisherConfig{}) + tp := NewTxPublisher(TxPublisherConfig{ + AuxSweeper: fn.Some[AuxSweeper](&MockAuxSweeper{}), + }) // Get the current counter and check it's increased later. initialCounter := tp.requestCounter.Load() @@ -369,10 +381,11 @@ func createTestPublisher(t *testing.T) (*TxPublisher, *mockers) { // Create a publisher using the mocks. tp := NewTxPublisher(TxPublisherConfig{ - Estimator: m.estimator, - Signer: m.signer, - Wallet: m.wallet, - Notifier: m.notifier, + Estimator: m.estimator, + Signer: m.signer, + Wallet: m.wallet, + Notifier: m.notifier, + AuxSweeper: fn.Some[AuxSweeper](&MockAuxSweeper{}), }) return tp, m @@ -451,7 +464,7 @@ func TestCreateAndCheckTx(t *testing.T) { t.Run(tc.name, func(t *testing.T) { // Call the method under test. - _, _, err := tp.createAndCheckTx(tc.req, m.feeFunc) + _, err := tp.createAndCheckTx(tc.req, m.feeFunc) // Check the result is as expected. require.ErrorIs(t, err, tc.expectedErr) diff --git a/sweep/interface.go b/sweep/interface.go index 4b02f143c3..41120613bc 100644 --- a/sweep/interface.go +++ b/sweep/interface.go @@ -1,8 +1,12 @@ package sweep import ( + "github.com/btcsuite/btcd/btcutil" "github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/btcsuite/btcd/wire" + "github.com/lightningnetwork/lnd/fn" + "github.com/lightningnetwork/lnd/input" + "github.com/lightningnetwork/lnd/keychain" "github.com/lightningnetwork/lnd/lnwallet" ) @@ -57,3 +61,31 @@ type Wallet interface { // service. BackEnd() string } + +// SweepOutput is an output used to sweep funds from a channel output. +type SweepOutput struct { //nolint:revive + wire.TxOut + + // IsExtra indicates whether this output is an extra output that was + // added by a party other than the sweeper. + IsExtra bool + + // InternalKey is the taproot internal key of the extra output. This is + // None, if this isn't a taproot output. + InternalKey fn.Option[keychain.KeyDescriptor] +} + +// AuxSweeper is used to enable a 3rd party to further shape the sweeping +// transaction by adding a set of extra outputs to the sweeping transaction. +type AuxSweeper interface { + // DeriveSweepAddr takes a set of inputs, and the change address we'd + // use to sweep them, and maybe results an extra sweep output that we + // should add to the sweeping transaction. + DeriveSweepAddr(inputs []input.Input, + change lnwallet.AddrWithKey) fn.Result[SweepOutput] + + // NotifyBroadcast is used to notify external callers of the broadcast + // of a sweep transaction, generated by the passed BumpRequest. + NotifyBroadcast(req *BumpRequest, tx *wire.MsgTx, + totalFees btcutil.Amount) error +} diff --git a/sweep/mock_test.go b/sweep/mock_test.go index 605b8d14ec..8552fbadce 100644 --- a/sweep/mock_test.go +++ b/sweep/mock_test.go @@ -314,3 +314,22 @@ func (m *MockFeeFunction) IncreaseFeeRate(confTarget uint32) (bool, error) { return args.Bool(0), args.Error(1) } + +type MockAuxSweeper struct{} + +// DeriveSweepAddr takes a set of inputs, and the change address we'd +// use to sweep them, and maybe results an extra sweep output that we +// should add to the sweeping transaction. +func (*MockAuxSweeper) DeriveSweepAddr(_ []input.Input, + _ lnwallet.AddrWithKey) fn.Result[SweepOutput] { + + return fn.Ok(SweepOutput{}) +} + +// NotifyBroadcast is used to notify external callers of the broadcast +// of a sweep transaction, generated by the passed BumpRequest. +func (*MockAuxSweeper) NotifyBroadcast(_ *BumpRequest, _ *wire.MsgTx, + _ btcutil.Amount) error { + + return nil +} diff --git a/sweep/sweeper.go b/sweep/sweeper.go index c151940943..a4b6d5638f 100644 --- a/sweep/sweeper.go +++ b/sweep/sweeper.go @@ -818,7 +818,7 @@ func (s *UtxoSweeper) sweep(set InputSet) error { Inputs: set.Inputs(), Budget: set.Budget(), DeadlineHeight: set.DeadlineHeight(), - DeliveryAddress: sweepAddr.DeliveryAddress, + DeliveryAddress: sweepAddr, MaxFeeRate: s.cfg.MaxFeeRate.FeePerKWeight(), StartingFeeRate: set.StartingFeeRate(), // TODO(yy): pass the strategy here. From a21fb1b69c63ac843445c299d610e2f5b7597b0e Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Mon, 3 Jun 2024 23:08:37 -0700 Subject: [PATCH 153/218] sweep: update sweeper to use AuxSweeper to add extra change addr In this commit, we start to use the AuxSweeper (if present) to obtain a new extra change addr we should add to the sweeping transaction. With this, we'll take the set of inputs and our change addr, and then maybe gain a new change addr to add to the sweep transaction. The extra change addr will be treated as an extra required tx out, shared across all the relevant inputs. This'll also be used in NeedWalletInput to make sure that we add an extra input if needed to be able to pay for the change addr. --- config_builder.go | 5 ++ server.go | 10 +-- sweep/aggregator.go | 16 +++-- sweep/aggregator_test.go | 10 +-- sweep/fee_bumper.go | 127 +++++++++++++++++++++++++++++-------- sweep/interface.go | 6 ++ sweep/mock_test.go | 29 ++++++++- sweep/tx_input_set.go | 44 +++++++++++-- sweep/tx_input_set_test.go | 109 ++++++++++++++++++++++++------- sweep/txgenerator.go | 56 ++++++++-------- sweep/txgenerator_test.go | 4 +- 11 files changed, 315 insertions(+), 101 deletions(-) diff --git a/config_builder.go b/config_builder.go index 7d6235f397..1c3a842ef1 100644 --- a/config_builder.go +++ b/config_builder.go @@ -50,6 +50,7 @@ import ( "github.com/lightningnetwork/lnd/rpcperms" "github.com/lightningnetwork/lnd/signal" "github.com/lightningnetwork/lnd/sqldb" + "github.com/lightningnetwork/lnd/sweep" "github.com/lightningnetwork/lnd/walletunlocker" "github.com/lightningnetwork/lnd/watchtower" "github.com/lightningnetwork/lnd/watchtower/wtclient" @@ -188,6 +189,10 @@ type AuxComponents struct { // modify the way a coop-close transaction is constructed. AuxChanCloser fn.Option[chancloser.AuxChanCloser] + // AuxSweeper is an optional interface that can be used to modify the + // way sweep transaction are generated. + AuxSweeper fn.Option[sweep.AuxSweeper] + // AuxContractResolver is an optional interface that can be used to // modify the way contracts are resolved. AuxContractResolver fn.Option[lnwallet.AuxContractResolver] diff --git a/server.go b/server.go index a8296d7c70..2d0fc7344f 100644 --- a/server.go +++ b/server.go @@ -1114,13 +1114,15 @@ func newServer(cfg *Config, listenAddrs []net.Addr, aggregator := sweep.NewBudgetAggregator( cc.FeeEstimator, sweep.DefaultMaxInputsPerTx, + s.implCfg.AuxSweeper, ) s.txPublisher = sweep.NewTxPublisher(sweep.TxPublisherConfig{ - Signer: cc.Wallet.Cfg.Signer, - Wallet: cc.Wallet, - Estimator: cc.FeeEstimator, - Notifier: cc.ChainNotifier, + Signer: cc.Wallet.Cfg.Signer, + Wallet: cc.Wallet, + Estimator: cc.FeeEstimator, + Notifier: cc.ChainNotifier, + AuxSweeper: s.implCfg.AuxSweeper, }) s.sweeper = sweep.New(&sweep.UtxoSweeperConfig{ diff --git a/sweep/aggregator.go b/sweep/aggregator.go index 6028adca3e..cd809e81a9 100644 --- a/sweep/aggregator.go +++ b/sweep/aggregator.go @@ -5,6 +5,7 @@ import ( "github.com/btcsuite/btcd/btcutil" "github.com/btcsuite/btcd/wire" + "github.com/lightningnetwork/lnd/fn" "github.com/lightningnetwork/lnd/input" "github.com/lightningnetwork/lnd/lntypes" "github.com/lightningnetwork/lnd/lnwallet" @@ -31,6 +32,10 @@ type BudgetAggregator struct { // maxInputs specifies the maximum number of inputs allowed in a single // sweep tx. maxInputs uint32 + + // auxSweeper is an optional interface that can be used to modify the + // way sweep transaction are generated. + auxSweeper fn.Option[AuxSweeper] } // Compile-time constraint to ensure BudgetAggregator implements UtxoAggregator. @@ -38,11 +43,12 @@ var _ UtxoAggregator = (*BudgetAggregator)(nil) // NewBudgetAggregator creates a new instance of a BudgetAggregator. func NewBudgetAggregator(estimator chainfee.Estimator, - maxInputs uint32) *BudgetAggregator { + maxInputs uint32, auxSweeper fn.Option[AuxSweeper]) *BudgetAggregator { return &BudgetAggregator{ - estimator: estimator, - maxInputs: maxInputs, + estimator: estimator, + maxInputs: maxInputs, + auxSweeper: auxSweeper, } } @@ -159,7 +165,7 @@ func (b *BudgetAggregator) createInputSets(inputs []SweeperInput, // Create an InputSet using the max allowed number of inputs. set, err := NewBudgetInputSet( - currentInputs, deadlineHeight, + currentInputs, deadlineHeight, b.auxSweeper, ) if err != nil { log.Errorf("unable to create input set: %v", err) @@ -173,7 +179,7 @@ func (b *BudgetAggregator) createInputSets(inputs []SweeperInput, // Create an InputSet from the remaining inputs. if len(remainingInputs) > 0 { set, err := NewBudgetInputSet( - remainingInputs, deadlineHeight, + remainingInputs, deadlineHeight, b.auxSweeper, ) if err != nil { log.Errorf("unable to create input set: %v", err) diff --git a/sweep/aggregator_test.go b/sweep/aggregator_test.go index a32c849a03..6df0d73fa2 100644 --- a/sweep/aggregator_test.go +++ b/sweep/aggregator_test.go @@ -150,7 +150,7 @@ func TestBudgetAggregatorFilterInputs(t *testing.T) { // Init the budget aggregator with the mocked estimator and zero max // num of inputs. - b := NewBudgetAggregator(estimator, 0) + b := NewBudgetAggregator(estimator, 0, fn.None[AuxSweeper]()) // Call the method under test. result := b.filterInputs(inputs) @@ -214,7 +214,7 @@ func TestBudgetAggregatorSortInputs(t *testing.T) { } // Init the budget aggregator with zero max num of inputs. - b := NewBudgetAggregator(nil, 0) + b := NewBudgetAggregator(nil, 0, fn.None[AuxSweeper]()) // Call the method under test. result := b.sortInputs(inputs) @@ -279,7 +279,7 @@ func TestBudgetAggregatorCreateInputSets(t *testing.T) { } // Create a budget aggregator with max number of inputs set to 2. - b := NewBudgetAggregator(nil, 2) + b := NewBudgetAggregator(nil, 2, fn.None[AuxSweeper]()) // Create test cases. testCases := []struct { @@ -540,7 +540,9 @@ func TestBudgetInputSetClusterInputs(t *testing.T) { } // Create a budget aggregator with a max number of inputs set to 100. - b := NewBudgetAggregator(estimator, DefaultMaxInputsPerTx) + b := NewBudgetAggregator( + estimator, DefaultMaxInputsPerTx, fn.None[AuxSweeper](), + ) // Call the method under test. result := b.ClusterInputs(inputs) diff --git a/sweep/fee_bumper.go b/sweep/fee_bumper.go index 5afa95bac0..b1e34c0bfe 100644 --- a/sweep/fee_bumper.go +++ b/sweep/fee_bumper.go @@ -131,6 +131,8 @@ type BumpRequest struct { func (r *BumpRequest) MaxFeeRateAllowed() (chainfee.SatPerKWeight, error) { // Get the size of the sweep tx, which will be used to calculate the // budget fee rate. + // + // TODO(roasbeef): also wants the extra change output? size, err := calcSweepTxWeight( r.Inputs, r.DeliveryAddress.DeliveryAddress, ) @@ -175,7 +177,7 @@ func calcSweepTxWeight(inputs []input.Input, // TODO(yy): we should refactor the weight estimator to not require a // fee rate and max fee rate and make it a pure tx weight calculator. _, estimator, err := getWeightEstimate( - inputs, nil, feeRate, 0, outputPkScript, + inputs, nil, feeRate, 0, [][]byte{outputPkScript}, ) if err != nil { return 0, err @@ -1171,9 +1173,9 @@ func (t *TxPublisher) createSweepTx(inputs []input.Input, feeRate chainfee.SatPerKWeight) (*sweepTxCtx, error) { // Validate and calculate the fee and change amount. - txFee, changeAmtOpt, locktimeOpt, err := prepareSweepTx( - inputs, changePkScript.DeliveryAddress, feeRate, - t.currentHeight.Load(), + txFee, changeOutputsOpt, locktimeOpt, err := prepareSweepTx( + inputs, changePkScript, feeRate, t.currentHeight.Load(), + t.cfg.AuxSweeper, ) if err != nil { return nil, err @@ -1219,12 +1221,12 @@ func (t *TxPublisher) createSweepTx(inputs []input.Input, }) } - // If there's a change amount, add it to the transaction. - changeAmtOpt.WhenSome(func(changeAmt btcutil.Amount) { - sweepTx.AddTxOut(&wire.TxOut{ - PkScript: changePkScript.DeliveryAddress, - Value: int64(changeAmt), - }) + // If we have change outputs to add, then add it the sweep transaction + // here. + changeOutputsOpt.WhenSome(func(changeOuts []SweepOutput) { + for i := range changeOuts { + sweepTx.AddTxOut(&changeOuts[i].TxOut) + } }) // We'll default to using the current block height as locktime, if none @@ -1268,31 +1270,80 @@ func (t *TxPublisher) createSweepTx(inputs []input.Input, log.Debugf("Created sweep tx %v for inputs:\n%v", sweepTx.TxHash(), inputTypeSummary(inputs)) + // Try to locate the extra change output, though there might be None. + extraTxOut := fn.MapOption( + func(sweepOuts []SweepOutput) fn.Option[SweepOutput] { + for _, sweepOut := range sweepOuts { + if !sweepOut.IsExtra { + continue + } + + // If we sweep outputs of a custom channel, the + // custom leaves in those outputs will be merged + // into a single output, even if we sweep + // multiple outputs (e.g. to_remote and breached + // to_local of a breached channel) at the same + // time. So there will only ever be one extra + // output. + log.Debugf("Sweep produced extra_sweep_out=%v", + lnutils.SpewLogClosure(sweepOut)) + + return fn.Some(sweepOut) + } + + return fn.None[SweepOutput]() + }, + )(changeOutputsOpt) + return &sweepTxCtx{ - tx: sweepTx, - fee: txFee, + tx: sweepTx, + fee: txFee, + extraTxOut: fn.FlattenOption(extraTxOut), }, nil } -// prepareSweepTx returns the tx fee, an optional change amount and an optional -// locktime after a series of validations: +// prepareSweepTx returns the tx fee, a set of optional change outputs and an +// optional locktime after a series of validations: // 1. check the locktime has been reached. // 2. check the locktimes are the same. // 3. check the inputs cover the outputs. // // NOTE: if the change amount is below dust, it will be added to the tx fee. -func prepareSweepTx(inputs []input.Input, changePkScript []byte, - feeRate chainfee.SatPerKWeight, currentHeight int32) ( - btcutil.Amount, fn.Option[btcutil.Amount], fn.Option[int32], error) { +func prepareSweepTx(inputs []input.Input, changePkScript lnwallet.AddrWithKey, + feeRate chainfee.SatPerKWeight, currentHeight int32, + auxSweeper fn.Option[AuxSweeper]) ( + btcutil.Amount, fn.Option[[]SweepOutput], fn.Option[int32], error) { - noChange := fn.None[btcutil.Amount]() + noChange := fn.None[[]SweepOutput]() noLocktime := fn.None[int32]() + // Given the set of inputs we have, if we have an aux sweeper, then + // we'll attempt to see if we have any other change outputs we'll need + // to add to the sweep transaction. + changePkScripts := [][]byte{changePkScript.DeliveryAddress} + + var extraChangeOut fn.Option[SweepOutput] + err := fn.MapOptionZ( + auxSweeper, func(aux AuxSweeper) error { + extraOut := aux.DeriveSweepAddr(inputs, changePkScript) + if err := extraOut.Err(); err != nil { + return err + } + + extraChangeOut = extraOut.LeftToOption() + + return nil + }, + ) + if err != nil { + return 0, noChange, noLocktime, err + } + // Creating a weight estimator with nil outputs and zero max fee rate. // We don't allow adding customized outputs in the sweeping tx, and the // fee rate is already being managed before we get here. inputs, estimator, err := getWeightEstimate( - inputs, nil, feeRate, 0, changePkScript, + inputs, nil, feeRate, 0, changePkScripts, ) if err != nil { return 0, noChange, noLocktime, err @@ -1310,6 +1361,12 @@ func prepareSweepTx(inputs []input.Input, changePkScript []byte, requiredOutput btcutil.Amount ) + // If we have an extra change output, then we'll add it as a required + // output amt. + extraChangeOut.WhenSome(func(o SweepOutput) { + requiredOutput += btcutil.Amount(o.Value) + }) + // Go through each input and check if the required lock times have // reached and are the same. for _, o := range inputs { @@ -1356,14 +1413,21 @@ func prepareSweepTx(inputs []input.Input, changePkScript []byte, // The value remaining after the required output and fees is the // change output. changeAmt := totalInput - requiredOutput - txFee - changeAmtOpt := fn.Some(changeAmt) + changeOuts := make([]SweepOutput, 0, 2) + + extraChangeOut.WhenSome(func(o SweepOutput) { + changeOuts = append(changeOuts, o) + }) // We'll calculate the dust limit for the given changePkScript since it // is variable. - changeFloor := lnwallet.DustLimitForSize(len(changePkScript)) + changeFloor := lnwallet.DustLimitForSize( + len(changePkScript.DeliveryAddress), + ) - // If the change amount is dust, we'll move it into the fees. switch { + // If the change amount is dust, we'll move it into the fees, and + // ignore it. case changeAmt < changeFloor: log.Infof("Change amt %v below dustlimit %v, not adding "+ "change output", changeAmt, changeFloor) @@ -1379,12 +1443,16 @@ func prepareSweepTx(inputs []input.Input, changePkScript []byte, // The dust amount is added to the fee. txFee += changeAmt - // Set the change amount to none. - changeAmtOpt = fn.None[btcutil.Amount]() - // Otherwise, we'll actually recognize it as a change output. default: - // TODO(roasbeef): Implement (later commit in this PR). + changeOuts = append(changeOuts, SweepOutput{ + TxOut: wire.TxOut{ + Value: int64(changeAmt), + PkScript: changePkScript.DeliveryAddress, + }, + IsExtra: false, + InternalKey: changePkScript.InternalKey, + }) } // Optionally set the locktime. @@ -1393,6 +1461,11 @@ func prepareSweepTx(inputs []input.Input, changePkScript []byte, locktimeOpt = noLocktime } + var changeOutsOpt fn.Option[[]SweepOutput] + if len(changeOuts) > 0 { + changeOutsOpt = fn.Some(changeOuts) + } + log.Debugf("Creating sweep tx for %v inputs (%s) using %v, "+ "tx_weight=%v, tx_fee=%v, locktime=%v, parents_count=%v, "+ "parents_fee=%v, parents_weight=%v, current_height=%v", @@ -1400,5 +1473,5 @@ func prepareSweepTx(inputs []input.Input, changePkScript []byte, estimator.weight(), txFee, locktimeOpt, len(estimator.parents), estimator.parentsFee, estimator.parentsWeight, currentHeight) - return txFee, changeAmtOpt, locktimeOpt, nil + return txFee, changeOutsOpt, locktimeOpt, nil } diff --git a/sweep/interface.go b/sweep/interface.go index 41120613bc..acece31430 100644 --- a/sweep/interface.go +++ b/sweep/interface.go @@ -84,6 +84,12 @@ type AuxSweeper interface { DeriveSweepAddr(inputs []input.Input, change lnwallet.AddrWithKey) fn.Result[SweepOutput] + // ExtraBudgetForInputs is used to determine the extra budget that + // should be allocated to sweep the given set of inputs. This can be + // used to add extra funds to the sweep transaction, for example to + // cover fees for additional outputs of custom channels. + ExtraBudgetForInputs(inputs []input.Input) fn.Result[btcutil.Amount] + // NotifyBroadcast is used to notify external callers of the broadcast // of a sweep transaction, generated by the passed BumpRequest. NotifyBroadcast(req *BumpRequest, tx *wire.MsgTx, diff --git a/sweep/mock_test.go b/sweep/mock_test.go index 8552fbadce..c623ca3c0b 100644 --- a/sweep/mock_test.go +++ b/sweep/mock_test.go @@ -6,6 +6,7 @@ import ( "github.com/btcsuite/btcd/wire" "github.com/lightningnetwork/lnd/fn" "github.com/lightningnetwork/lnd/input" + "github.com/lightningnetwork/lnd/keychain" "github.com/lightningnetwork/lnd/lnwallet" "github.com/lightningnetwork/lnd/lnwallet/chainfee" "github.com/stretchr/testify/mock" @@ -315,15 +316,37 @@ func (m *MockFeeFunction) IncreaseFeeRate(confTarget uint32) (bool, error) { return args.Bool(0), args.Error(1) } -type MockAuxSweeper struct{} +type MockAuxSweeper struct { + mock.Mock +} // DeriveSweepAddr takes a set of inputs, and the change address we'd // use to sweep them, and maybe results an extra sweep output that we // should add to the sweeping transaction. -func (*MockAuxSweeper) DeriveSweepAddr(_ []input.Input, +func (m *MockAuxSweeper) DeriveSweepAddr(_ []input.Input, _ lnwallet.AddrWithKey) fn.Result[SweepOutput] { - return fn.Ok(SweepOutput{}) + return fn.Ok(SweepOutput{ + TxOut: wire.TxOut{ + Value: 123, + PkScript: changePkScript.DeliveryAddress, + }, + IsExtra: false, + InternalKey: fn.None[keychain.KeyDescriptor](), + }) +} + +// ExtraBudgetForInputs is used to determine the extra budget that +// should be allocated to sweep the given set of inputs. This can be +// used to add extra funds to the sweep transaction, for example to +// cover fees for additional outputs of custom channels. +func (m *MockAuxSweeper) ExtraBudgetForInputs( + _ []input.Input) fn.Result[btcutil.Amount] { + + args := m.Called() + amt := args.Get(0) + + return amt.(fn.Result[btcutil.Amount]) } // NotifyBroadcast is used to notify external callers of the broadcast diff --git a/sweep/tx_input_set.go b/sweep/tx_input_set.go index 31f20b7db1..3a95fff2fb 100644 --- a/sweep/tx_input_set.go +++ b/sweep/tx_input_set.go @@ -111,17 +111,26 @@ type BudgetInputSet struct { // deadlineHeight is the height which the inputs in this set must be // confirmed by. deadlineHeight int32 + + // extraBudget is a value that should be allocated to sweep the given + // set of inputs. This can be used to add extra funds to the sweep + // transaction, for example to cover fees for additional outputs of + // custom channels. + extraBudget btcutil.Amount } // Compile-time constraint to ensure budgetInputSet implements InputSet. var _ InputSet = (*BudgetInputSet)(nil) +// errEmptyInputs is returned when the input slice is empty. +var errEmptyInputs = fmt.Errorf("inputs slice is empty") + // validateInputs is used when creating new BudgetInputSet to ensure there are // no duplicate inputs and they all share the same deadline heights, if set. func validateInputs(inputs []SweeperInput, deadlineHeight int32) error { // Sanity check the input slice to ensure it's non-empty. if len(inputs) == 0 { - return fmt.Errorf("inputs slice is empty") + return errEmptyInputs } // inputDeadline tracks the input's deadline height. It will be updated @@ -167,8 +176,8 @@ func validateInputs(inputs []SweeperInput, deadlineHeight int32) error { } // NewBudgetInputSet creates a new BudgetInputSet. -func NewBudgetInputSet(inputs []SweeperInput, - deadlineHeight int32) (*BudgetInputSet, error) { +func NewBudgetInputSet(inputs []SweeperInput, deadlineHeight int32, + auxSweeper fn.Option[AuxSweeper]) (*BudgetInputSet, error) { // Validate the supplied inputs. if err := validateInputs(inputs, deadlineHeight); err != nil { @@ -186,9 +195,32 @@ func NewBudgetInputSet(inputs []SweeperInput, log.Tracef("Created %v", bi.String()) + // Attach an optional budget. This will be a no-op if the auxSweeper + // is not set. + if err := bi.attachExtraBudget(auxSweeper); err != nil { + return nil, err + } + return bi, nil } +// attachExtraBudget attaches an extra budget to the input set, if the passed +// aux sweeper is set. +func (b *BudgetInputSet) attachExtraBudget(s fn.Option[AuxSweeper]) error { + extraBudget, err := fn.MapOptionZ( + s, func(aux AuxSweeper) fn.Result[btcutil.Amount] { + return aux.ExtraBudgetForInputs(b.Inputs()) + }, + ).Unpack() + if err != nil { + return err + } + + b.extraBudget = extraBudget + + return nil +} + // String returns a human-readable description of the input set. func (b *BudgetInputSet) String() string { inputsDesc := "" @@ -212,8 +244,10 @@ func (b *BudgetInputSet) addInput(input SweeperInput) { func (b *BudgetInputSet) NeedWalletInput() bool { var ( // budgetNeeded is the amount that needs to be covered from - // other inputs. - budgetNeeded btcutil.Amount + // other inputs. We start at the value of the extra budget, + // which might be needed for custom channels that add extra + // outputs. + budgetNeeded = b.extraBudget // budgetBorrowable is the amount that can be borrowed from // other inputs. diff --git a/sweep/tx_input_set_test.go b/sweep/tx_input_set_test.go index b6a87b378b..b774eedff2 100644 --- a/sweep/tx_input_set_test.go +++ b/sweep/tx_input_set_test.go @@ -28,7 +28,9 @@ func TestNewBudgetInputSet(t *testing.T) { rt := require.New(t) // Pass an empty slice and expect an error. - set, err := NewBudgetInputSet([]SweeperInput{}, testHeight) + set, err := NewBudgetInputSet( + []SweeperInput{}, testHeight, fn.None[AuxSweeper](), + ) rt.ErrorContains(err, "inputs slice is empty") rt.Nil(set) @@ -66,23 +68,35 @@ func TestNewBudgetInputSet(t *testing.T) { } // Pass a slice of inputs with different deadline heights. - set, err = NewBudgetInputSet([]SweeperInput{input1, input2}, testHeight) + set, err = NewBudgetInputSet( + []SweeperInput{input1, input2}, testHeight, + fn.None[AuxSweeper](), + ) rt.ErrorContains(err, "input deadline height not matched") rt.Nil(set) // Pass a slice of inputs that only one input has the deadline height, // but it has a different value than the specified testHeight. - set, err = NewBudgetInputSet([]SweeperInput{input0, input2}, testHeight) + set, err = NewBudgetInputSet( + []SweeperInput{input0, input2}, testHeight, + fn.None[AuxSweeper](), + ) rt.ErrorContains(err, "input deadline height not matched") rt.Nil(set) // Pass a slice of inputs that are duplicates. - set, err = NewBudgetInputSet([]SweeperInput{input3, input3}, testHeight) + set, err = NewBudgetInputSet( + []SweeperInput{input3, input3}, testHeight, + fn.None[AuxSweeper](), + ) rt.ErrorContains(err, "duplicate inputs") rt.Nil(set) // Pass a slice of inputs that only one input has the deadline height, - set, err = NewBudgetInputSet([]SweeperInput{input0, input3}, testHeight) + set, err = NewBudgetInputSet( + []SweeperInput{input0, input3}, testHeight, + fn.None[AuxSweeper](), + ) rt.NoError(err) rt.NotNil(set) } @@ -102,7 +116,9 @@ func TestBudgetInputSetAddInput(t *testing.T) { } // Initialize an input set, which adds the above input. - set, err := NewBudgetInputSet([]SweeperInput{*pi}, testHeight) + set, err := NewBudgetInputSet( + []SweeperInput{*pi}, testHeight, fn.None[AuxSweeper](), + ) require.NoError(t, err) // Add the input to the set again. @@ -125,48 +141,55 @@ func TestNeedWalletInput(t *testing.T) { // Create a mock input that doesn't have required outputs. mockInput := &input.MockInput{} mockInput.On("RequiredTxOut").Return(nil) + mockInput.On("OutPoint").Return(wire.OutPoint{Hash: chainhash.Hash{1}}) defer mockInput.AssertExpectations(t) // Create a mock input that has required outputs. mockInputRequireOutput := &input.MockInput{} mockInputRequireOutput.On("RequiredTxOut").Return(&wire.TxOut{}) + mockInputRequireOutput.On("OutPoint").Return( + wire.OutPoint{Hash: chainhash.Hash{2}}, + ) defer mockInputRequireOutput.AssertExpectations(t) // We now create two pending inputs each has a budget of 100 satoshis. const budget = 100 // Create the pending input that doesn't have a required output. - piBudget := &SweeperInput{ + piBudget := SweeperInput{ Input: mockInput, params: Params{Budget: budget}, } // Create the pending input that has a required output. - piRequireOutput := &SweeperInput{ + piRequireOutput := SweeperInput{ Input: mockInputRequireOutput, params: Params{Budget: budget}, } testCases := []struct { name string - setupInputs func() []*SweeperInput + setupInputs func() []SweeperInput + extraBudget btcutil.Amount need bool + err error }{ { // When there are no pending inputs, we won't need a - // wallet input. Technically this should be an invalid + // wallet input. Technically this is be an invalid // state. name: "no inputs", - setupInputs: func() []*SweeperInput { + setupInputs: func() []SweeperInput { return nil }, need: false, + err: errEmptyInputs, }, { // When there's no required output, we don't need a // wallet input. name: "no required outputs", - setupInputs: func() []*SweeperInput { + setupInputs: func() []SweeperInput { // Create a sign descriptor to be used in the // pending input when calculating budgets can // be borrowed. @@ -177,15 +200,36 @@ func TestNeedWalletInput(t *testing.T) { } mockInput.On("SignDesc").Return(sd).Once() - return []*SweeperInput{piBudget} + return []SweeperInput{piBudget} }, need: false, }, + { + // When there's no required normal outputs, but an extra + // budget from custom channels, we will need a wallet + // input. + name: "no required normal outputs but extra budget", + setupInputs: func() []SweeperInput { + // Create a sign descriptor to be used in the + // pending input when calculating budgets can + // be borrowed. + sd := &input.SignDescriptor{ + Output: &wire.TxOut{ + Value: budget, + }, + } + mockInput.On("SignDesc").Return(sd).Once() + + return []SweeperInput{piBudget} + }, + extraBudget: 1000, + need: true, + }, { // When the output value cannot cover the budget, we // need a wallet input. name: "output value cannot cover budget", - setupInputs: func() []*SweeperInput { + setupInputs: func() []SweeperInput { // Create a sign descriptor to be used in the // pending input when calculating budgets can // be borrowed. @@ -194,8 +238,8 @@ func TestNeedWalletInput(t *testing.T) { Value: budget - 1, }, } - mockInput.On("SignDesc").Return(sd).Once() + mockInput.On("SignDesc").Return(sd).Once() // These two methods are only invoked when the // unit test is running with a logger. mockInput.On("OutPoint").Return( @@ -205,7 +249,7 @@ func TestNeedWalletInput(t *testing.T) { input.CommitmentAnchor, ).Maybe() - return []*SweeperInput{piBudget} + return []SweeperInput{piBudget} }, need: true, }, @@ -213,8 +257,8 @@ func TestNeedWalletInput(t *testing.T) { // When there's only inputs that require outputs, we // need wallet inputs. name: "only required outputs", - setupInputs: func() []*SweeperInput { - return []*SweeperInput{piRequireOutput} + setupInputs: func() []SweeperInput { + return []SweeperInput{piRequireOutput} }, need: true, }, @@ -223,7 +267,7 @@ func TestNeedWalletInput(t *testing.T) { // budget cannot cover the required, we need a wallet // input. name: "not enough budget to be borrowed", - setupInputs: func() []*SweeperInput { + setupInputs: func() []SweeperInput { // Create a sign descriptor to be used in the // pending input when calculating budgets can // be borrowed. @@ -237,7 +281,7 @@ func TestNeedWalletInput(t *testing.T) { } mockInput.On("SignDesc").Return(sd).Once() - return []*SweeperInput{ + return []SweeperInput{ piBudget, piRequireOutput, } }, @@ -248,7 +292,7 @@ func TestNeedWalletInput(t *testing.T) { // borrowed covers the required, we don't need wallet // inputs. name: "enough budget to be borrowed", - setupInputs: func() []*SweeperInput { + setupInputs: func() []SweeperInput { // Create a sign descriptor to be used in the // pending input when calculating budgets can // be borrowed. @@ -263,7 +307,7 @@ func TestNeedWalletInput(t *testing.T) { mockInput.On("SignDesc").Return(sd).Once() piBudget.Input = mockInput - return []*SweeperInput{ + return []SweeperInput{ piBudget, piRequireOutput, } }, @@ -276,12 +320,27 @@ func TestNeedWalletInput(t *testing.T) { // Setup testing inputs. inputs := tc.setupInputs() + // If an extra budget is set, then we'll update the mock + // to expect the extra budget. + mockAuxSweeper := &MockAuxSweeper{} + mockAuxSweeper.On("ExtraBudgetForInputs").Return( + fn.Ok(tc.extraBudget), + ) + // Initialize an input set, which adds the testing // inputs. - set := &BudgetInputSet{inputs: inputs} + set, err := NewBudgetInputSet( + inputs, 0, fn.Some[AuxSweeper](mockAuxSweeper), + ) + if err != nil { + require.ErrorIs(t, err, tc.err) + return + } result := set.NeedWalletInput() + require.Equal(t, tc.need, result) + mockAuxSweeper.AssertExpectations(t) }) } } @@ -434,7 +493,9 @@ func TestAddWalletInputSuccess(t *testing.T) { min, max).Return([]*lnwallet.Utxo{utxo, utxo}, nil).Once() // Initialize an input set with the pending input. - set, err := NewBudgetInputSet([]SweeperInput{*pi}, deadline) + set, err := NewBudgetInputSet( + []SweeperInput{*pi}, deadline, fn.None[AuxSweeper](), + ) require.NoError(t, err) // Add wallet inputs to the input set, which should give us an error as diff --git a/sweep/txgenerator.go b/sweep/txgenerator.go index 30e11023e1..43fc802ba7 100644 --- a/sweep/txgenerator.go +++ b/sweep/txgenerator.go @@ -38,7 +38,7 @@ func createSweepTx(inputs []input.Input, outputs []*wire.TxOut, signer input.Signer) (*wire.MsgTx, btcutil.Amount, error) { inputs, estimator, err := getWeightEstimate( - inputs, outputs, feeRate, maxFeeRate, changePkScript, + inputs, outputs, feeRate, maxFeeRate, [][]byte{changePkScript}, ) if err != nil { return nil, 0, err @@ -221,7 +221,7 @@ func createSweepTx(inputs []input.Input, outputs []*wire.TxOut, // Additionally, it returns counts for the number of csv and cltv inputs. func getWeightEstimate(inputs []input.Input, outputs []*wire.TxOut, feeRate, maxFeeRate chainfee.SatPerKWeight, - outputPkScript []byte) ([]input.Input, *weightEstimator, error) { + outputPkScripts [][]byte) ([]input.Input, *weightEstimator, error) { // We initialize a weight estimator so we can accurately asses the // amount of fees we need to pay for this sweep transaction. @@ -237,31 +237,33 @@ func getWeightEstimate(inputs []input.Input, outputs []*wire.TxOut, // If there is any leftover change after paying to the given outputs // and required outputs, it will go to a single segwit p2wkh or p2tr - // address. This will be our change address, so ensure it contributes to - // our weight estimate. Note that if we have other outputs, we might end - // up creating a sweep tx without a change output. It is okay to add the - // change output to the weight estimate regardless, since the estimated - // fee will just be subtracted from this already dust output, and - // trimmed. - switch { - case txscript.IsPayToTaproot(outputPkScript): - weightEstimate.addP2TROutput() - - case txscript.IsPayToWitnessScriptHash(outputPkScript): - weightEstimate.addP2WSHOutput() - - case txscript.IsPayToWitnessPubKeyHash(outputPkScript): - weightEstimate.addP2WKHOutput() - - case txscript.IsPayToPubKeyHash(outputPkScript): - weightEstimate.estimator.AddP2PKHOutput() - - case txscript.IsPayToScriptHash(outputPkScript): - weightEstimate.estimator.AddP2SHOutput() - - default: - // Unknown script type. - return nil, nil, errors.New("unknown script type") + // address. This will be our change address, so ensure it contributes + // to our weight estimate. Note that if we have other outputs, we might + // end up creating a sweep tx without a change output. It is okay to + // add the change output to the weight estimate regardless, since the + // estimated fee will just be subtracted from this already dust output, + // and trimmed. + for _, outputPkScript := range outputPkScripts { + switch { + case txscript.IsPayToTaproot(outputPkScript): + weightEstimate.addP2TROutput() + + case txscript.IsPayToWitnessScriptHash(outputPkScript): + weightEstimate.addP2WSHOutput() + + case txscript.IsPayToWitnessPubKeyHash(outputPkScript): + weightEstimate.addP2WKHOutput() + + case txscript.IsPayToPubKeyHash(outputPkScript): + weightEstimate.estimator.AddP2PKHOutput() + + case txscript.IsPayToScriptHash(outputPkScript): + weightEstimate.estimator.AddP2SHOutput() + + default: + // Unknown script type. + return nil, nil, errors.New("unknown script type") + } } // For each output, use its witness type to determine the estimate diff --git a/sweep/txgenerator_test.go b/sweep/txgenerator_test.go index 48dcacd499..71477bd6ec 100644 --- a/sweep/txgenerator_test.go +++ b/sweep/txgenerator_test.go @@ -51,7 +51,7 @@ func TestWeightEstimate(t *testing.T) { } _, estimator, err := getWeightEstimate( - inputs, nil, 0, 0, changePkScript, + inputs, nil, 0, 0, [][]byte{changePkScript}, ) require.NoError(t, err) @@ -153,7 +153,7 @@ func testUnknownScriptInner(t *testing.T, pkscript []byte, expectFail bool) { )) } - _, _, err := getWeightEstimate(inputs, nil, 0, 0, pkscript) + _, _, err := getWeightEstimate(inputs, nil, 0, 0, [][]byte{pkscript}) if expectFail { require.Error(t, err) } else { From 678028cee9f3c277007b53220740cd6899d809bd Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Fri, 7 Jun 2024 20:55:59 -0700 Subject: [PATCH 154/218] contractcourt: pause resolution for HTLCs w/ custom records This is a hold over until the aux resolution is finalized for HTLC outputs. --- contractcourt/htlc_incoming_contest_resolver.go | 11 +++++++++++ contractcourt/htlc_outgoing_contest_resolver.go | 11 +++++++++++ contractcourt/htlc_success_resolver.go | 11 +++++++++++ contractcourt/htlc_timeout_resolver.go | 11 +++++++++++ 4 files changed, 44 insertions(+) diff --git a/contractcourt/htlc_incoming_contest_resolver.go b/contractcourt/htlc_incoming_contest_resolver.go index 6bda4e398b..9ffa799437 100644 --- a/contractcourt/htlc_incoming_contest_resolver.go +++ b/contractcourt/htlc_incoming_contest_resolver.go @@ -99,6 +99,17 @@ func (h *htlcIncomingContestResolver) Resolve( return nil, nil } + // If the HTLC has custom records, then for now we'll pause resolution. + // + // TODO(roasbeef): Implement resolving HTLCs with custom records + // (follow-up PR). + if len(h.htlc.CustomRecords) != 0 { + select { //nolint:gosimple + case <-h.quit: + return nil, errResolverShuttingDown + } + } + // First try to parse the payload. If that fails, we can stop resolution // now. payload, nextHopOnionBlob, err := h.decodePayload() diff --git a/contractcourt/htlc_outgoing_contest_resolver.go b/contractcourt/htlc_outgoing_contest_resolver.go index 2466544c98..c75b898222 100644 --- a/contractcourt/htlc_outgoing_contest_resolver.go +++ b/contractcourt/htlc_outgoing_contest_resolver.go @@ -58,6 +58,17 @@ func (h *htlcOutgoingContestResolver) Resolve( return nil, nil } + // If the HTLC has custom records, then for now we'll pause resolution. + // + // TODO(roasbeef): Implement resolving HTLCs with custom records + // (follow-up PR). + if len(h.htlc.CustomRecords) != 0 { + select { //nolint:gosimple + case <-h.quit: + return nil, errResolverShuttingDown + } + } + // Otherwise, we'll watch for two external signals to decide if we'll // morph into another resolver, or fully resolve the contract. // diff --git a/contractcourt/htlc_success_resolver.go b/contractcourt/htlc_success_resolver.go index 6eee939eac..3b07828d48 100644 --- a/contractcourt/htlc_success_resolver.go +++ b/contractcourt/htlc_success_resolver.go @@ -123,6 +123,17 @@ func (h *htlcSuccessResolver) Resolve( return nil, nil } + // If the HTLC has custom records, then for now we'll pause resolution. + // + // TODO(roasbeef): Implement resolving HTLCs with custom records + // (follow-up PR). + if len(h.htlc.CustomRecords) != 0 { + select { //nolint:gosimple + case <-h.quit: + return nil, errResolverShuttingDown + } + } + // If we don't have a success transaction, then this means that this is // an output on the remote party's commitment transaction. if h.htlcResolution.SignedSuccessTx == nil { diff --git a/contractcourt/htlc_timeout_resolver.go b/contractcourt/htlc_timeout_resolver.go index 62ff832071..87cbfce0b9 100644 --- a/contractcourt/htlc_timeout_resolver.go +++ b/contractcourt/htlc_timeout_resolver.go @@ -426,6 +426,17 @@ func (h *htlcTimeoutResolver) Resolve( return nil, nil } + // If the HTLC has custom records, then for now we'll pause resolution. + // + // TODO(roasbeef): Implement resolving HTLCs with custom records + // (follow-up PR). + if len(h.htlc.CustomRecords) != 0 { + select { //nolint:gosimple + case <-h.quit: + return nil, errResolverShuttingDown + } + } + // Start by spending the HTLC output, either by broadcasting the // second-level timeout transaction, or directly if this is the remote // commitment. From d0501ab840b4bd0d5f6fda644742d678778daf1b Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Sat, 8 Jun 2024 20:10:27 -0700 Subject: [PATCH 155/218] multi: hook up new aux interfaces --- contractcourt/chain_arbitrator.go | 14 ++++++++++++++ funding/manager.go | 7 +++++++ peer/brontide.go | 17 +++++++++++++++-- server.go | 3 +++ 4 files changed, 39 insertions(+), 2 deletions(-) diff --git a/contractcourt/chain_arbitrator.go b/contractcourt/chain_arbitrator.go index d61e479018..c29178b438 100644 --- a/contractcourt/chain_arbitrator.go +++ b/contractcourt/chain_arbitrator.go @@ -225,6 +225,10 @@ type ChainArbitratorConfig struct { // AuxSigner is an optional signer that can be used to sign auxiliary // leaves for certain custom channel types. AuxSigner fn.Option[lnwallet.AuxSigner] + + // AuxResolver is an optional interface that can be used to modify the + // way contracts are resolved. + AuxResolver fn.Option[lnwallet.AuxContractResolver] } // ChainArbitrator is a sub-system that oversees the on-chain resolution of all @@ -314,6 +318,9 @@ func (a *arbChannel) NewAnchorResolutions() (*lnwallet.AnchorResolutions, a.c.cfg.AuxSigner.WhenSome(func(s lnwallet.AuxSigner) { chanOpts = append(chanOpts, lnwallet.WithAuxSigner(s)) }) + a.c.cfg.AuxResolver.WhenSome(func(s lnwallet.AuxContractResolver) { + chanOpts = append(chanOpts, lnwallet.WithAuxResolver(s)) + }) chanMachine, err := lnwallet.NewLightningChannel( a.c.cfg.Signer, channel, nil, chanOpts..., @@ -367,6 +374,9 @@ func (a *arbChannel) ForceCloseChan() (*lnwallet.LocalForceCloseSummary, error) a.c.cfg.AuxSigner.WhenSome(func(s lnwallet.AuxSigner) { chanOpts = append(chanOpts, lnwallet.WithAuxSigner(s)) }) + a.c.cfg.AuxResolver.WhenSome(func(s lnwallet.AuxContractResolver) { + chanOpts = append(chanOpts, lnwallet.WithAuxResolver(s)) + }) // Finally, we'll force close the channel completing // the force close workflow. @@ -581,6 +591,8 @@ func (c *ChainArbitrator) Start() error { isOurAddr: c.cfg.IsOurAddress, contractBreach: breachClosure, extractStateNumHint: lnwallet.GetStateNumHint, + auxLeafStore: c.cfg.AuxLeafStore, + auxResolver: c.cfg.AuxResolver, }, ) if err != nil { @@ -1210,6 +1222,8 @@ func (c *ChainArbitrator) WatchNewChannel(newChan *channeldb.OpenChannel) error ) }, extractStateNumHint: lnwallet.GetStateNumHint, + auxLeafStore: c.cfg.AuxLeafStore, + auxResolver: c.cfg.AuxResolver, }, ) if err != nil { diff --git a/funding/manager.go b/funding/manager.go index ed0ef0e7fc..2f89c17ad8 100644 --- a/funding/manager.go +++ b/funding/manager.go @@ -558,6 +558,10 @@ type Config struct { // AuxSigner is an optional signer that can be used to sign auxiliary // leaves for certain custom channel types. AuxSigner fn.Option[lnwallet.AuxSigner] + + // AuxResolver is an optional interface that can be used to modify the + // way contracts are resolved. + AuxResolver fn.Option[lnwallet.AuxContractResolver] } // Manager acts as an orchestrator/bridge between the wallet's @@ -1090,6 +1094,9 @@ func (f *Manager) advanceFundingState(channel *channeldb.OpenChannel, f.cfg.AuxSigner.WhenSome(func(s lnwallet.AuxSigner) { chanOpts = append(chanOpts, lnwallet.WithAuxSigner(s)) }) + f.cfg.AuxResolver.WhenSome(func(s lnwallet.AuxContractResolver) { + chanOpts = append(chanOpts, lnwallet.WithAuxResolver(s)) + }) // We create the state-machine object which wraps the database state. lnChannel, err := lnwallet.NewLightningChannel( diff --git a/peer/brontide.go b/peer/brontide.go index ecff687d3c..fa42f13584 100644 --- a/peer/brontide.go +++ b/peer/brontide.go @@ -396,6 +396,10 @@ type Config struct { // leaves for certain custom channel types. AuxSigner fn.Option[lnwallet.AuxSigner] + // AuxResolver is an optional interface that can be used to modify the + // way contracts are resolved. + AuxResolver fn.Option[lnwallet.AuxContractResolver] + // PongBuf is a slice we'll reuse instead of allocating memory on the // heap. Since only reads will occur and no writes, there is no need // for any synchronization primitives. As a result, it's safe to share @@ -999,8 +1003,6 @@ func (p *Brontide) loadActiveChannels(chans []*channeldb.OpenChannel) ( } } - // TODO(roasbeef): also make aux resolver here - var chanOpts []lnwallet.ChannelOpt p.cfg.AuxLeafStore.WhenSome(func(s lnwallet.AuxLeafStore) { chanOpts = append(chanOpts, lnwallet.WithLeafStore(s)) @@ -1008,6 +1010,14 @@ func (p *Brontide) loadActiveChannels(chans []*channeldb.OpenChannel) ( p.cfg.AuxSigner.WhenSome(func(s lnwallet.AuxSigner) { chanOpts = append(chanOpts, lnwallet.WithAuxSigner(s)) }) + p.cfg.AuxResolver.WhenSome( + func(s lnwallet.AuxContractResolver) { + chanOpts = append( + chanOpts, lnwallet.WithAuxResolver(s), + ) + }, + ) + lnChan, err := lnwallet.NewLightningChannel( p.cfg.Signer, dbChan, p.cfg.SigPool, chanOpts..., ) @@ -4254,6 +4264,9 @@ func (p *Brontide) addActiveChannel(c *lnpeer.NewChannel) error { p.cfg.AuxSigner.WhenSome(func(s lnwallet.AuxSigner) { chanOpts = append(chanOpts, lnwallet.WithAuxSigner(s)) }) + p.cfg.AuxResolver.WhenSome(func(s lnwallet.AuxContractResolver) { + chanOpts = append(chanOpts, lnwallet.WithAuxResolver(s)) + }) // If not already active, we'll add this channel to the set of active // channels, so we can look it up later easily according to its channel diff --git a/server.go b/server.go index 2d0fc7344f..7d269d75cd 100644 --- a/server.go +++ b/server.go @@ -1317,6 +1317,7 @@ func newServer(cfg *Config, listenAddrs []net.Addr, }, AuxLeafStore: implCfg.AuxLeafStore, AuxSigner: implCfg.AuxSigner, + AuxResolver: implCfg.AuxContractResolver, }, dbs.ChanStateDB) // Select the configuration and funding parameters for Bitcoin. @@ -1566,6 +1567,7 @@ func newServer(cfg *Config, listenAddrs []net.Addr, IsSweeperOutpoint: s.sweeper.IsSweeperOutpoint, AuxFundingController: implCfg.AuxFundingController, AuxSigner: implCfg.AuxSigner, + AuxResolver: implCfg.AuxContractResolver, }) if err != nil { return nil, err @@ -4119,6 +4121,7 @@ func (s *server) peerConnected(conn net.Conn, connReq *connmgr.ConnReq, AuxSigner: s.implCfg.AuxSigner, MsgRouter: s.implCfg.MsgRouter, AuxChanCloser: s.implCfg.AuxChanCloser, + AuxResolver: s.implCfg.AuxContractResolver, } copy(pCfg.PubKeyBytes[:], peerAddr.IdentityKey.SerializeCompressed()) From ded6959fd5c5b98ff6a50959477289c01f202613 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Sun, 23 Jun 2024 23:30:52 -0700 Subject: [PATCH 156/218] contractcourt: update GenSweepScript to return internal key For the upcoming aux sweeper integration, the internal key is needed for the call backs. --- contractcourt/breach_arbitrator.go | 6 +++--- contractcourt/breach_arbitrator_test.go | 22 +++++++++++++--------- server.go | 13 +++---------- 3 files changed, 19 insertions(+), 22 deletions(-) diff --git a/contractcourt/breach_arbitrator.go b/contractcourt/breach_arbitrator.go index 98858d64e0..f7875ec10d 100644 --- a/contractcourt/breach_arbitrator.go +++ b/contractcourt/breach_arbitrator.go @@ -149,7 +149,7 @@ type BreachConfig struct { Estimator chainfee.Estimator // GenSweepScript generates the receiving scripts for swept outputs. - GenSweepScript func() ([]byte, error) + GenSweepScript func() fn.Result[lnwallet.AddrWithKey] // Notifier provides a publish/subscribe interface for event driven // notifications regarding the confirmation of txids. @@ -1517,7 +1517,7 @@ func (b *BreachArbitrator) sweepSpendableOutputsTxn(txWeight lntypes.WeightUnit, // sweep the funds to. // TODO(roasbeef): possibly create many outputs to minimize change in // the future? - pkScript, err := b.cfg.GenSweepScript() + pkScript, err := b.cfg.GenSweepScript().Unpack() if err != nil { return nil, err } @@ -1545,7 +1545,7 @@ func (b *BreachArbitrator) sweepSpendableOutputsTxn(txWeight lntypes.WeightUnit, // We begin by adding the output to which our funds will be deposited. txn.AddTxOut(&wire.TxOut{ - PkScript: pkScript, + PkScript: pkScript.DeliveryAddress, Value: sweepAmt, }) diff --git a/contractcourt/breach_arbitrator_test.go b/contractcourt/breach_arbitrator_test.go index f3a56e8aac..4bb863b52e 100644 --- a/contractcourt/breach_arbitrator_test.go +++ b/contractcourt/breach_arbitrator_test.go @@ -2135,15 +2135,19 @@ func createTestArbiter(t *testing.T, contractBreaches chan *ContractBreachEvent, // Assemble our test arbiter. notifier := mock.MakeMockSpendNotifier() ba := NewBreachArbitrator(&BreachConfig{ - CloseLink: func(_ *wire.OutPoint, _ ChannelCloseType) {}, - DB: db.ChannelStateDB(), - Estimator: chainfee.NewStaticEstimator(12500, 0), - GenSweepScript: func() ([]byte, error) { return nil, nil }, - ContractBreaches: contractBreaches, - Signer: signer, - Notifier: notifier, - PublishTransaction: func(_ *wire.MsgTx, _ string) error { return nil }, - Store: store, + CloseLink: func(_ *wire.OutPoint, _ ChannelCloseType) {}, + DB: db.ChannelStateDB(), + Estimator: chainfee.NewStaticEstimator(12500, 0), + GenSweepScript: func() fn.Result[lnwallet.AddrWithKey] { + return fn.Ok(lnwallet.AddrWithKey{}) + }, + ContractBreaches: contractBreaches, + Signer: signer, + Notifier: notifier, + PublishTransaction: func(_ *wire.MsgTx, _ string) error { + return nil + }, + Store: store, }) if err := ba.Start(); err != nil { diff --git a/server.go b/server.go index 7d269d75cd..8806151bf0 100644 --- a/server.go +++ b/server.go @@ -1175,16 +1175,9 @@ func newServer(cfg *Config, listenAddrs []net.Addr, CloseLink: closeLink, DB: s.chanStateDB, Estimator: s.cc.FeeEstimator, - GenSweepScript: func() ([]byte, error) { - addr, err := newSweepPkScriptGen( - cc.Wallet, netParams, - )().Unpack() - if err != nil { - return nil, err - } - - return addr.DeliveryAddress, nil - }, + GenSweepScript: newSweepPkScriptGen( + cc.Wallet, s.cfg.ActiveNetParams.Params, + ), Notifier: cc.ChainNotifier, PublishTransaction: cc.Wallet.PublishTransaction, ContractBreaches: contractBreaches, From 4c16e55acad46dfadded211de99641ed7ff26979 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Sun, 23 Jun 2024 23:32:11 -0700 Subject: [PATCH 157/218] contractcourt: update makeBreachedOutput to accept resolution blob --- contractcourt/breach_arbitrator.go | 39 ++++++++++++++++++++++--- contractcourt/breach_arbitrator_test.go | 3 ++ contractcourt/utxonursery.go | 2 ++ 3 files changed, 40 insertions(+), 4 deletions(-) diff --git a/contractcourt/breach_arbitrator.go b/contractcourt/breach_arbitrator.go index f7875ec10d..2d11b450d1 100644 --- a/contractcourt/breach_arbitrator.go +++ b/contractcourt/breach_arbitrator.go @@ -1078,10 +1078,9 @@ type breachedOutput struct { // makeBreachedOutput assembles a new breachedOutput that can be used by the // breach arbiter to construct a justice or sweep transaction. func makeBreachedOutput(outpoint *wire.OutPoint, - witnessType input.StandardWitnessType, - secondLevelScript []byte, - signDescriptor *input.SignDescriptor, - confHeight uint32) breachedOutput { + witnessType input.StandardWitnessType, secondLevelScript []byte, + signDescriptor *input.SignDescriptor, confHeight uint32, + resolutionBlob fn.Option[tlv.Blob]) breachedOutput { amount := signDescriptor.Output.Value @@ -1092,6 +1091,7 @@ func makeBreachedOutput(outpoint *wire.OutPoint, witnessType: witnessType, signDesc: *signDescriptor, confHeight: confHeight, + resolutionBlob: resolutionBlob, } } @@ -1270,6 +1270,7 @@ func newRetributionInfo(chanPoint *wire.OutPoint, nil, breachInfo.LocalOutputSignDesc, breachInfo.BreachHeight, + breachInfo.LocalResolutionBlob, ) breachedOutputs = append(breachedOutputs, localOutput) @@ -1296,6 +1297,7 @@ func newRetributionInfo(chanPoint *wire.OutPoint, nil, breachInfo.RemoteOutputSignDesc, breachInfo.BreachHeight, + breachInfo.RemoteResolutionBlob, ) breachedOutputs = append(breachedOutputs, remoteOutput) @@ -1330,6 +1332,7 @@ func newRetributionInfo(chanPoint *wire.OutPoint, breachInfo.HtlcRetributions[i].SecondLevelWitnessScript, &breachInfo.HtlcRetributions[i].SignDesc, breachInfo.BreachHeight, + breachInfo.HtlcRetributions[i].ResolutionBlob, ) // For taproot outputs, we also need to hold onto the second @@ -1636,12 +1639,28 @@ func taprootBriefcaseFromRetInfo(retInfo *retributionInfo) *taprootBriefcase { //nolint:lll tapCase.CtrlBlocks.Val.CommitSweepCtrlBlock = bo.signDesc.ControlBlock + bo.resolutionBlob.WhenSome(func(blob tlv.Blob) { + tapCase.SettledCommitBlob = tlv.SomeRecordT( + tlv.NewPrimitiveRecord[tlv.TlvType2]( + blob, + ), + ) + }) + // To spend the revoked output again, we'll store the same // control block value as above, but in a different place. case input.TaprootCommitmentRevoke: //nolint:lll tapCase.CtrlBlocks.Val.RevokeSweepCtrlBlock = bo.signDesc.ControlBlock + bo.resolutionBlob.WhenSome(func(blob tlv.Blob) { + tapCase.BreachedCommitBlob = tlv.SomeRecordT( + tlv.NewPrimitiveRecord[tlv.TlvType3]( + blob, + ), + ) + }) + // For spending the HTLC outputs, we'll store the first and // second level tweak values. case input.TaprootHtlcAcceptedRevoke: @@ -1679,12 +1698,24 @@ func applyTaprootRetInfo(tapCase *taprootBriefcase, //nolint:lll bo.signDesc.ControlBlock = tapCase.CtrlBlocks.Val.CommitSweepCtrlBlock + tapCase.SettledCommitBlob.WhenSomeV( + func(blob tlv.Blob) { + bo.resolutionBlob = fn.Some(blob) + }, + ) + // To spend the revoked output again, we'll apply the same // control block value as above, but to a different place. case input.TaprootCommitmentRevoke: //nolint:lll bo.signDesc.ControlBlock = tapCase.CtrlBlocks.Val.RevokeSweepCtrlBlock + tapCase.BreachedCommitBlob.WhenSomeV( + func(blob tlv.Blob) { + bo.resolutionBlob = fn.Some(blob) + }, + ) + // For spending the HTLC outputs, we'll apply the first and // second level tweak values. case input.TaprootHtlcAcceptedRevoke: diff --git a/contractcourt/breach_arbitrator_test.go b/contractcourt/breach_arbitrator_test.go index 4bb863b52e..addb33bae1 100644 --- a/contractcourt/breach_arbitrator_test.go +++ b/contractcourt/breach_arbitrator_test.go @@ -1199,6 +1199,8 @@ func TestBreachCreateJusticeTx(t *testing.T) { input.HtlcSecondLevelRevoke, } + rBlob := fn.Some([]byte{0x01}) + breachedOutputs := make([]breachedOutput, len(outputTypes)) for i, wt := range outputTypes { // Create a fake breached output for each type, ensuring they @@ -1217,6 +1219,7 @@ func TestBreachCreateJusticeTx(t *testing.T) { nil, signDesc, 1, + rBlob, ) } diff --git a/contractcourt/utxonursery.go b/contractcourt/utxonursery.go index 4073841628..b7b4d33a8b 100644 --- a/contractcourt/utxonursery.go +++ b/contractcourt/utxonursery.go @@ -21,6 +21,7 @@ import ( "github.com/lightningnetwork/lnd/lnutils" "github.com/lightningnetwork/lnd/lnwallet" "github.com/lightningnetwork/lnd/sweep" + "github.com/lightningnetwork/lnd/tlv" ) // SUMMARY OF OUTPUT STATES @@ -1423,6 +1424,7 @@ func makeKidOutput(outpoint, originChanPoint *wire.OutPoint, return kidOutput{ breachedOutput: makeBreachedOutput( outpoint, witnessType, nil, signDescriptor, heightHint, + fn.None[tlv.Blob](), ), isHtlc: isHtlc, originChanPoint: *originChanPoint, From 3d7d9d26122dcc051cf21efc5c3d30c8293aba11 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Sun, 23 Jun 2024 23:33:27 -0700 Subject: [PATCH 158/218] contractcourt: integration aux sweeper to breach arb Similar to the sweeper, when we're about to make a new breach transaction, we ask the sweeper for a new change address, if it has one. Then when we go to publish, we notify broadcast. --- contractcourt/breach_arbitrator.go | 115 +++++++++++++++++++++--- contractcourt/breach_arbitrator_test.go | 6 +- server.go | 1 + 3 files changed, 105 insertions(+), 17 deletions(-) diff --git a/contractcourt/breach_arbitrator.go b/contractcourt/breach_arbitrator.go index 2d11b450d1..dc690e85c7 100644 --- a/contractcourt/breach_arbitrator.go +++ b/contractcourt/breach_arbitrator.go @@ -23,6 +23,7 @@ import ( "github.com/lightningnetwork/lnd/lnutils" "github.com/lightningnetwork/lnd/lnwallet" "github.com/lightningnetwork/lnd/lnwallet/chainfee" + "github.com/lightningnetwork/lnd/sweep" "github.com/lightningnetwork/lnd/tlv" ) @@ -174,6 +175,10 @@ type BreachConfig struct { // breached channels. This is used in conjunction with DB to recover // from crashes, restarts, or other failures. Store RetributionStorer + + // AuxSweeper is an optional interface that can be used to modify the + // way sweep transaction are generated. + AuxSweeper fn.Option[sweep.AuxSweeper] } // BreachArbitrator is a special subsystem which is responsible for watching and @@ -737,10 +742,28 @@ justiceTxBroadcast: brarLog.Debugf("Broadcasting justice tx: %v", lnutils.SpewLogClosure( finalTx)) + // As we're about to broadcast our breach transaction, we'll notify the + // aux sweeper of our broadcast attempt first. + err = fn.MapOptionZ(b.cfg.AuxSweeper, func(aux sweep.AuxSweeper) error { + bumpReq := sweep.BumpRequest{ + Inputs: finalTx.inputs, + DeliveryAddress: finalTx.sweepAddr, + ExtraTxOut: finalTx.extraTxOut, + } + + return aux.NotifyBroadcast( + &bumpReq, finalTx.justiceTx, finalTx.fee, + ) + }) + if err != nil { + brarLog.Errorf("unable to notify broadcast: %w", err) + return + } + // We'll now attempt to broadcast the transaction which finalized the // channel's retribution against the cheating counter party. label := labels.MakeLabel(labels.LabelTypeJusticeTransaction, nil) - err = b.cfg.PublishTransaction(finalTx, label) + err = b.cfg.PublishTransaction(finalTx.justiceTx, label) if err != nil { brarLog.Errorf("Unable to broadcast justice tx: %v", err) } @@ -860,7 +883,9 @@ Loop: "spending commitment outs: %v", lnutils.SpewLogClosure(tx)) - err = b.cfg.PublishTransaction(tx, label) + err = b.cfg.PublishTransaction( + tx.justiceTx, label, + ) if err != nil { brarLog.Warnf("Unable to broadcast "+ "commit out spending justice "+ @@ -875,7 +900,9 @@ Loop: "spending HTLC outs: %v", lnutils.SpewLogClosure(tx)) - err = b.cfg.PublishTransaction(tx, label) + err = b.cfg.PublishTransaction( + tx.justiceTx, label, + ) if err != nil { brarLog.Warnf("Unable to broadcast "+ "HTLC out spending justice "+ @@ -890,7 +917,9 @@ Loop: "spending second-level HTLC output: %v", lnutils.SpewLogClosure(tx)) - err = b.cfg.PublishTransaction(tx, label) + err = b.cfg.PublishTransaction( + tx.justiceTx, label, + ) if err != nil { brarLog.Warnf("Unable to broadcast "+ "second-level HTLC out "+ @@ -1372,10 +1401,10 @@ func newRetributionInfo(chanPoint *wire.OutPoint, // spend the to_local output and commitment level HTLC outputs separately, // before the CSV locks expire. type justiceTxVariants struct { - spendAll *wire.MsgTx - spendCommitOuts *wire.MsgTx - spendHTLCs *wire.MsgTx - spendSecondLevelHTLCs []*wire.MsgTx + spendAll *justiceTxCtx + spendCommitOuts *justiceTxCtx + spendHTLCs *justiceTxCtx + spendSecondLevelHTLCs []*justiceTxCtx } // createJusticeTx creates transactions which exacts "justice" by sweeping ALL @@ -1439,7 +1468,9 @@ func (b *BreachArbitrator) createJusticeTx( err) } - secondLevelSweeps := make([]*wire.MsgTx, 0, len(secondLevelInputs)) + // TODO(roasbeef): only register one of them? + + secondLevelSweeps := make([]*justiceTxCtx, 0, len(secondLevelInputs)) for _, input := range secondLevelInputs { sweepTx, err := b.createSweepTx(input) if err != nil { @@ -1456,9 +1487,23 @@ func (b *BreachArbitrator) createJusticeTx( return txs, nil } +// justiceTxCtx contains the justice transaction along with other related meta +// data. +type justiceTxCtx struct { + justiceTx *wire.MsgTx + + sweepAddr lnwallet.AddrWithKey + + extraTxOut fn.Option[sweep.SweepOutput] + + fee btcutil.Amount + + inputs []input.Input +} + // createSweepTx creates a tx that sweeps the passed inputs back to our wallet. -func (b *BreachArbitrator) createSweepTx(inputs ...input.Input) (*wire.MsgTx, - error) { +func (b *BreachArbitrator) createSweepTx( + inputs ...input.Input) (*justiceTxCtx, error) { if len(inputs) == 0 { return nil, nil @@ -1481,6 +1526,18 @@ func (b *BreachArbitrator) createSweepTx(inputs ...input.Input) (*wire.MsgTx, // nLockTime, and output are already included in the TxWeightEstimator. weightEstimate.AddP2TROutput() + // If any of our inputs has a resolution blob, then we'll add another + // P2TR _output_, since we'll want to separate the custom channel + // outputs from the regular, BTC only outputs. So we only need one such + // output, which'll carry the custom channel "valuables" from both the + // breached commitment and HTLC outputs. + hasBlobs := fn.Any(func(i input.Input) bool { + return i.ResolutionBlob().IsSome() + }, inputs) + if hasBlobs { + weightEstimate.AddP2TROutput() + } + // Next, we iterate over the breached outputs contained in the // retribution info. For each, we switch over the witness type such // that we contribute the appropriate weight for each input and @@ -1514,7 +1571,7 @@ func (b *BreachArbitrator) createSweepTx(inputs ...input.Input) (*wire.MsgTx, // sweepSpendableOutputsTxn creates a signed transaction from a sequence of // spendable outputs by sweeping the funds into a single p2wkh output. func (b *BreachArbitrator) sweepSpendableOutputsTxn(txWeight lntypes.WeightUnit, - inputs ...input.Input) (*wire.MsgTx, error) { + inputs ...input.Input) (*justiceTxCtx, error) { // First, we obtain a new public key script from the wallet which we'll // sweep the funds to. @@ -1539,6 +1596,18 @@ func (b *BreachArbitrator) sweepSpendableOutputsTxn(txWeight lntypes.WeightUnit, } txFee := feePerKw.FeeForWeight(txWeight) + // At this point, we'll check to see if we have any extra outputs to + // add from the aux sweeper. + extraChangeOut := fn.MapOptionZ( + b.cfg.AuxSweeper, + func(aux sweep.AuxSweeper) fn.Result[sweep.SweepOutput] { + return aux.DeriveSweepAddr(inputs, pkScript) + }, + ) + if err := extraChangeOut.Err(); err != nil { + return nil, err + } + // TODO(roasbeef): already start to siphon their funds into fees sweepAmt := int64(totalAmt - txFee) @@ -1546,12 +1615,24 @@ func (b *BreachArbitrator) sweepSpendableOutputsTxn(txWeight lntypes.WeightUnit, // information gathered above and the provided retribution information. txn := wire.NewMsgTx(2) - // We begin by adding the output to which our funds will be deposited. + // First, we'll add the extra sweep output if it exists, subtracting the + // amount from the sweep amt. + if b.cfg.AuxSweeper.IsSome() { + extraChangeOut.WhenResult(func(o sweep.SweepOutput) { + sweepAmt -= o.Value + + txn.AddTxOut(&o.TxOut) + }) + } + + // Next, we'll add the output to which our funds will be deposited. txn.AddTxOut(&wire.TxOut{ PkScript: pkScript.DeliveryAddress, Value: sweepAmt, }) + // TODO(roasbeef): add other output change modify sweep amt + // Next, we add all of the spendable outputs as inputs to the // transaction. for _, inp := range inputs { @@ -1607,7 +1688,13 @@ func (b *BreachArbitrator) sweepSpendableOutputsTxn(txWeight lntypes.WeightUnit, } } - return txn, nil + return &justiceTxCtx{ + justiceTx: txn, + sweepAddr: pkScript, + extraTxOut: extraChangeOut.Option(), + fee: txFee, + inputs: inputs, + }, nil } // RetributionStore handles persistence of retribution states to disk and is diff --git a/contractcourt/breach_arbitrator_test.go b/contractcourt/breach_arbitrator_test.go index addb33bae1..bd4ad85683 100644 --- a/contractcourt/breach_arbitrator_test.go +++ b/contractcourt/breach_arbitrator_test.go @@ -1230,16 +1230,16 @@ func TestBreachCreateJusticeTx(t *testing.T) { // The spendAll tx should be spending all the outputs. This is the // "regular" justice transaction type. - require.Len(t, justiceTxs.spendAll.TxIn, len(breachedOutputs)) + require.Len(t, justiceTxs.spendAll.justiceTx.TxIn, len(breachedOutputs)) // The spendCommitOuts tx should be spending the 4 types of commit outs // (note that in practice there will be at most two commit outputs per // commit, but we test all 4 types here). - require.Len(t, justiceTxs.spendCommitOuts.TxIn, 4) + require.Len(t, justiceTxs.spendCommitOuts.justiceTx.TxIn, 4) // Check that the spendHTLCs tx is spending the two revoked commitment // level HTLC output types. - require.Len(t, justiceTxs.spendHTLCs.TxIn, 2) + require.Len(t, justiceTxs.spendHTLCs.justiceTx.TxIn, 2) // Finally, check that the spendSecondLevelHTLCs txs are spending the // second level type. diff --git a/server.go b/server.go index 8806151bf0..ce9f9dd401 100644 --- a/server.go +++ b/server.go @@ -1185,6 +1185,7 @@ func newServer(cfg *Config, listenAddrs []net.Addr, Store: contractcourt.NewRetributionStore( dbs.ChanStateDB, ), + AuxSweeper: s.implCfg.AuxSweeper, }, ) From 1a3c94a50151f1d2356a77d70b67666bc3f102cd Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Tue, 25 Jun 2024 15:20:00 -0700 Subject: [PATCH 159/218] lnwire: add new taproot chans overlay feature bit --- lnwire/features.go | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/lnwire/features.go b/lnwire/features.go index 437a50dc21..c75d36b397 100644 --- a/lnwire/features.go +++ b/lnwire/features.go @@ -273,6 +273,14 @@ const ( // a BOLT 11 invoice. Bolt11BlindedPathsOptional = 263 + // SimpleTaprootOverlayChansRequired is a required bit that indicates + // support for the special custom taproot overlay channel. + SimpleTaprootOverlayChansOptional = 2025 + + // SimpleTaprootOverlayChansRequired is a required bit that indicates + // support for the special custom taproot overlay channel. + SimpleTaprootOverlayChansRequired = 2026 + // MaxBolt11Feature is the maximum feature bit value allowed in bolt 11 // invoices. // @@ -339,6 +347,8 @@ var Features = map[FeatureBit]string{ SimpleTaprootChannelsOptionalFinal: "simple-taproot-chans", SimpleTaprootChannelsRequiredStaging: "simple-taproot-chans-x", SimpleTaprootChannelsOptionalStaging: "simple-taproot-chans-x", + SimpleTaprootOverlayChansOptional: "taproot-overlay-chans", + SimpleTaprootOverlayChansRequired: "taproot-overlay-chans", Bolt11BlindedPathsOptional: "bolt-11-blinded-paths", Bolt11BlindedPathsRequired: "bolt-11-blinded-paths", } From 745f3a61812a146264d9e815b90925bff1b74a4b Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Tue, 25 Jun 2024 15:20:39 -0700 Subject: [PATCH 160/218] feature: add awareness of new taproot chans overlay feature bit This bit will be false by default in current production deployments. --- feature/default_sets.go | 4 ++++ feature/deps.go | 5 +++++ feature/manager.go | 7 +++++++ 3 files changed, 16 insertions(+) diff --git a/feature/default_sets.go b/feature/default_sets.go index cc802fe859..4a9b2bf64d 100644 --- a/feature/default_sets.go +++ b/feature/default_sets.go @@ -92,4 +92,8 @@ var defaultSetDesc = setDesc{ SetInit: {}, // I SetNodeAnn: {}, // N }, + lnwire.SimpleTaprootOverlayChansOptional: { + SetInit: {}, // I + SetNodeAnn: {}, // N + }, } diff --git a/feature/deps.go b/feature/deps.go index 64b4f2fc9c..922f252141 100644 --- a/feature/deps.go +++ b/feature/deps.go @@ -79,6 +79,11 @@ var deps = depDesc{ lnwire.AnchorsZeroFeeHtlcTxOptional: {}, lnwire.ExplicitChannelTypeOptional: {}, }, + lnwire.SimpleTaprootOverlayChansOptional: { + lnwire.SimpleTaprootChannelsOptionalStaging: {}, + lnwire.TLVOnionPayloadOptional: {}, + lnwire.ScidAliasOptional: {}, + }, lnwire.RouteBlindingOptional: { lnwire.TLVOnionPayloadOptional: {}, }, diff --git a/feature/manager.go b/feature/manager.go index 0b66539859..89f1d4b6bc 100644 --- a/feature/manager.go +++ b/feature/manager.go @@ -63,6 +63,9 @@ type Config struct { // NoRouteBlinding unsets route blinding feature bits. NoRouteBlinding bool + // NoTaprootOverlay unsets the taproot overlay channel feature bits. + NoTaprootOverlay bool + // CustomFeatures is a set of custom features to advertise in each // set. CustomFeatures map[Set][]lnwire.FeatureBit @@ -192,6 +195,10 @@ func newManager(cfg Config, desc setDesc) (*Manager, error) { raw.Unset(lnwire.Bolt11BlindedPathsOptional) raw.Unset(lnwire.Bolt11BlindedPathsRequired) } + if cfg.NoTaprootOverlay { + raw.Unset(lnwire.SimpleTaprootOverlayChansOptional) + raw.Unset(lnwire.SimpleTaprootOverlayChansRequired) + } for _, custom := range cfg.CustomFeatures[set] { if custom > set.Maximum() { return nil, fmt.Errorf("feature bit: %v "+ From 4efa39ddb10e9e7a55bc32e150be6d44213f7a3d Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Tue, 25 Jun 2024 15:31:32 -0700 Subject: [PATCH 161/218] lnwallet: add awareness of taproot overlay chan type to reservations --- lnwallet/reservation.go | 33 ++++++++++++++++++++++++++++----- lnwallet/wallet.go | 2 +- 2 files changed, 29 insertions(+), 6 deletions(-) diff --git a/lnwallet/reservation.go b/lnwallet/reservation.go index 7f1a89c228..4c4f58f8be 100644 --- a/lnwallet/reservation.go +++ b/lnwallet/reservation.go @@ -2,6 +2,7 @@ package lnwallet import ( "errors" + "fmt" "net" "sync" @@ -50,6 +51,11 @@ const ( // channels that use a musig2 funding output and the tapscript tree // where relevant for the commitment transaction pk scripts. CommitmentTypeSimpleTaproot + + // CommitmentTypeSimpleTaprootOverlay builds on the existing + // CommitmentTypeSimpleTaproot type but layers on a special overlay + // protocol. + CommitmentTypeSimpleTaprootOverlay ) // HasStaticRemoteKey returns whether the commitment type supports remote @@ -59,8 +65,11 @@ func (c CommitmentType) HasStaticRemoteKey() bool { case CommitmentTypeTweakless, CommitmentTypeAnchorsZeroFeeHtlcTx, CommitmentTypeScriptEnforcedLease, - CommitmentTypeSimpleTaproot: + CommitmentTypeSimpleTaproot, + CommitmentTypeSimpleTaprootOverlay: + return true + default: return false } @@ -71,8 +80,11 @@ func (c CommitmentType) HasAnchors() bool { switch c { case CommitmentTypeAnchorsZeroFeeHtlcTx, CommitmentTypeScriptEnforcedLease, - CommitmentTypeSimpleTaproot: + CommitmentTypeSimpleTaproot, + CommitmentTypeSimpleTaprootOverlay: + return true + default: return false } @@ -80,7 +92,8 @@ func (c CommitmentType) HasAnchors() bool { // IsTaproot returns true if the channel type is a taproot channel. func (c CommitmentType) IsTaproot() bool { - return c == CommitmentTypeSimpleTaproot + return c == CommitmentTypeSimpleTaproot || + c == CommitmentTypeSimpleTaprootOverlay } // String returns the name of the CommitmentType. @@ -96,6 +109,8 @@ func (c CommitmentType) String() string { return "script-enforced-lease" case CommitmentTypeSimpleTaproot: return "simple-taproot" + case CommitmentTypeSimpleTaprootOverlay: + return "simple-taproot-overlay" default: return "invalid" } @@ -424,7 +439,7 @@ func NewChannelReservation(capacity, localFundingAmt btcutil.Amount, chanType |= channeldb.FrozenBit } - if req.CommitType == CommitmentTypeSimpleTaproot { + if req.CommitType.IsTaproot() { chanType |= channeldb.SimpleTaprootFeatureBit } @@ -440,7 +455,15 @@ func NewChannelReservation(capacity, localFundingAmt btcutil.Amount, chanType |= channeldb.ScidAliasFeatureBit } - if req.TapscriptRoot.IsSome() { + taprootOverlay := req.CommitType == CommitmentTypeSimpleTaprootOverlay + switch { + case taprootOverlay && req.TapscriptRoot.IsNone(): + fallthrough + case !taprootOverlay && req.TapscriptRoot.IsSome(): + return nil, fmt.Errorf("taproot overlay chans must be set " + + "with tapscript root") + + case taprootOverlay && req.TapscriptRoot.IsSome(): chanType |= channeldb.TapscriptRootBit } diff --git a/lnwallet/wallet.go b/lnwallet/wallet.go index 39060f5a13..4f2ba25eda 100644 --- a/lnwallet/wallet.go +++ b/lnwallet/wallet.go @@ -983,7 +983,7 @@ func (l *LightningWallet) handleFundingReserveRequest(req *InitFundingReserveMsg TaprootPubkey, true, DefaultAccountName, ) }, - Musig2: req.CommitType == CommitmentTypeSimpleTaproot, + Musig2: req.CommitType.IsTaproot(), } fundingIntent, err = req.ChanFunder.ProvisionChannel( fundingReq, From 72d437d5c8934b7e5919e1e74622f941140add40 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Tue, 25 Jun 2024 15:21:05 -0700 Subject: [PATCH 162/218] funding: add chan type awareness for new taproot chans overlay --- funding/commitment_type_negotiation.go | 68 ++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/funding/commitment_type_negotiation.go b/funding/commitment_type_negotiation.go index f931b4702a..817709c73d 100644 --- a/funding/commitment_type_negotiation.go +++ b/funding/commitment_type_negotiation.go @@ -307,6 +307,74 @@ func explicitNegotiateCommitmentType(channelType lnwire.ChannelType, local, return lnwallet.CommitmentTypeSimpleTaproot, nil + // Simple taproot channels overlay only. + case channelFeatures.OnlyContains( + lnwire.SimpleTaprootOverlayChansRequired, + ): + + if !hasFeatures( + local, remote, + lnwire.SimpleTaprootOverlayChansOptional, + ) { + + return 0, errUnsupportedChannelType + } + + return lnwallet.CommitmentTypeSimpleTaprootOverlay, nil + + // Simple taproot overlay channels with scid only. + case channelFeatures.OnlyContains( + lnwire.SimpleTaprootOverlayChansRequired, + lnwire.ScidAliasRequired, + ): + + if !hasFeatures( + local, remote, + lnwire.SimpleTaprootOverlayChansOptional, + lnwire.ScidAliasOptional, + ) { + + return 0, errUnsupportedChannelType + } + + return lnwallet.CommitmentTypeSimpleTaprootOverlay, nil + + // Simple taproot overlay channels with zero conf only. + case channelFeatures.OnlyContains( + lnwire.SimpleTaprootOverlayChansRequired, + lnwire.ZeroConfRequired, + ): + + if !hasFeatures( + local, remote, + lnwire.SimpleTaprootOverlayChansOptional, + lnwire.ZeroConfOptional, + ) { + + return 0, errUnsupportedChannelType + } + + return lnwallet.CommitmentTypeSimpleTaprootOverlay, nil + + // Simple taproot overlay channels with scid and zero conf. + case channelFeatures.OnlyContains( + lnwire.SimpleTaprootOverlayChansRequired, + lnwire.ZeroConfRequired, + lnwire.ScidAliasRequired, + ): + + if !hasFeatures( + local, remote, + lnwire.SimpleTaprootOverlayChansOptional, + lnwire.ZeroConfOptional, + lnwire.ScidAliasOptional, + ) { + + return 0, errUnsupportedChannelType + } + + return lnwallet.CommitmentTypeSimpleTaprootOverlay, nil + // No features, use legacy commitment type. case channelFeatures.IsEmpty(): return lnwallet.CommitmentTypeLegacy, nil From 4eaba9b3dbf62652f59c81d0e3f207d1c5ceb20a Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Tue, 25 Jun 2024 15:31:45 -0700 Subject: [PATCH 163/218] lnrpc: add SIMPLE_TAPROOT_OVERLAY feature bit --- lnrpc/lightning.pb.go | 827 ++++++++++++++++++----------------- lnrpc/lightning.proto | 8 +- lnrpc/lightning.swagger.json | 5 +- 3 files changed, 428 insertions(+), 412 deletions(-) diff --git a/lnrpc/lightning.pb.go b/lnrpc/lightning.pb.go index 8c5b0513dc..60a970ba61 100644 --- a/lnrpc/lightning.pb.go +++ b/lnrpc/lightning.pb.go @@ -229,8 +229,13 @@ const ( // to guarantee that the channel initiator has no incentives to close a leased // channel before its maturity date. CommitmentType_SCRIPT_ENFORCED_LEASE CommitmentType = 4 - // TODO(roasbeef): need script enforce mirror type for the above as well? + // A channel that uses musig2 for the funding output, and the new tapscript + // features where relevant. CommitmentType_SIMPLE_TAPROOT CommitmentType = 5 + // Identical to the SIMPLE_TAPROOT channel type, but with extra functionality. + // This channel type also commits to additional meta data in the tapscript + // leaves for the scripts in a channel. + CommitmentType_SIMPLE_TAPROOT_OVERLAY CommitmentType = 6 ) // Enum value maps for CommitmentType. @@ -242,6 +247,7 @@ var ( 3: "ANCHORS", 4: "SCRIPT_ENFORCED_LEASE", 5: "SIMPLE_TAPROOT", + 6: "SIMPLE_TAPROOT_OVERLAY", } CommitmentType_value = map[string]int32{ "UNKNOWN_COMMITMENT_TYPE": 0, @@ -250,6 +256,7 @@ var ( "ANCHORS": 3, "SCRIPT_ENFORCED_LEASE": 4, "SIMPLE_TAPROOT": 5, + "SIMPLE_TAPROOT_OVERLAY": 6, } ) @@ -21071,7 +21078,7 @@ var file_lightning_proto_rawDesc = []byte{ 0x5f, 0x50, 0x55, 0x42, 0x4b, 0x45, 0x59, 0x5f, 0x48, 0x41, 0x53, 0x48, 0x10, 0x03, 0x12, 0x12, 0x0a, 0x0e, 0x54, 0x41, 0x50, 0x52, 0x4f, 0x4f, 0x54, 0x5f, 0x50, 0x55, 0x42, 0x4b, 0x45, 0x59, 0x10, 0x04, 0x12, 0x19, 0x0a, 0x15, 0x55, 0x4e, 0x55, 0x53, 0x45, 0x44, 0x5f, 0x54, 0x41, 0x50, - 0x52, 0x4f, 0x4f, 0x54, 0x5f, 0x50, 0x55, 0x42, 0x4b, 0x45, 0x59, 0x10, 0x05, 0x2a, 0x8c, 0x01, + 0x52, 0x4f, 0x4f, 0x54, 0x5f, 0x50, 0x55, 0x42, 0x4b, 0x45, 0x59, 0x10, 0x05, 0x2a, 0xa8, 0x01, 0x0a, 0x0e, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x6d, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1b, 0x0a, 0x17, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x5f, 0x43, 0x4f, 0x4d, 0x4d, 0x49, 0x54, 0x4d, 0x45, 0x4e, 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x10, 0x00, 0x12, 0x0a, 0x0a, @@ -21080,420 +21087,422 @@ var file_lightning_proto_rawDesc = []byte{ 0x12, 0x0b, 0x0a, 0x07, 0x41, 0x4e, 0x43, 0x48, 0x4f, 0x52, 0x53, 0x10, 0x03, 0x12, 0x19, 0x0a, 0x15, 0x53, 0x43, 0x52, 0x49, 0x50, 0x54, 0x5f, 0x45, 0x4e, 0x46, 0x4f, 0x52, 0x43, 0x45, 0x44, 0x5f, 0x4c, 0x45, 0x41, 0x53, 0x45, 0x10, 0x04, 0x12, 0x12, 0x0a, 0x0e, 0x53, 0x49, 0x4d, 0x50, - 0x4c, 0x45, 0x5f, 0x54, 0x41, 0x50, 0x52, 0x4f, 0x4f, 0x54, 0x10, 0x05, 0x2a, 0x61, 0x0a, 0x09, - 0x49, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x74, 0x6f, 0x72, 0x12, 0x15, 0x0a, 0x11, 0x49, 0x4e, 0x49, - 0x54, 0x49, 0x41, 0x54, 0x4f, 0x52, 0x5f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, - 0x12, 0x13, 0x0a, 0x0f, 0x49, 0x4e, 0x49, 0x54, 0x49, 0x41, 0x54, 0x4f, 0x52, 0x5f, 0x4c, 0x4f, - 0x43, 0x41, 0x4c, 0x10, 0x01, 0x12, 0x14, 0x0a, 0x10, 0x49, 0x4e, 0x49, 0x54, 0x49, 0x41, 0x54, - 0x4f, 0x52, 0x5f, 0x52, 0x45, 0x4d, 0x4f, 0x54, 0x45, 0x10, 0x02, 0x12, 0x12, 0x0a, 0x0e, 0x49, - 0x4e, 0x49, 0x54, 0x49, 0x41, 0x54, 0x4f, 0x52, 0x5f, 0x42, 0x4f, 0x54, 0x48, 0x10, 0x03, 0x2a, - 0x60, 0x0a, 0x0e, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x79, 0x70, - 0x65, 0x12, 0x10, 0x0a, 0x0c, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, - 0x4e, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x41, 0x4e, 0x43, 0x48, 0x4f, 0x52, 0x10, 0x01, 0x12, - 0x11, 0x0a, 0x0d, 0x49, 0x4e, 0x43, 0x4f, 0x4d, 0x49, 0x4e, 0x47, 0x5f, 0x48, 0x54, 0x4c, 0x43, - 0x10, 0x02, 0x12, 0x11, 0x0a, 0x0d, 0x4f, 0x55, 0x54, 0x47, 0x4f, 0x49, 0x4e, 0x47, 0x5f, 0x48, - 0x54, 0x4c, 0x43, 0x10, 0x03, 0x12, 0x0a, 0x0a, 0x06, 0x43, 0x4f, 0x4d, 0x4d, 0x49, 0x54, 0x10, - 0x04, 0x2a, 0x71, 0x0a, 0x11, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x4f, - 0x75, 0x74, 0x63, 0x6f, 0x6d, 0x65, 0x12, 0x13, 0x0a, 0x0f, 0x4f, 0x55, 0x54, 0x43, 0x4f, 0x4d, - 0x45, 0x5f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x43, - 0x4c, 0x41, 0x49, 0x4d, 0x45, 0x44, 0x10, 0x01, 0x12, 0x0d, 0x0a, 0x09, 0x55, 0x4e, 0x43, 0x4c, - 0x41, 0x49, 0x4d, 0x45, 0x44, 0x10, 0x02, 0x12, 0x0d, 0x0a, 0x09, 0x41, 0x42, 0x41, 0x4e, 0x44, - 0x4f, 0x4e, 0x45, 0x44, 0x10, 0x03, 0x12, 0x0f, 0x0a, 0x0b, 0x46, 0x49, 0x52, 0x53, 0x54, 0x5f, - 0x53, 0x54, 0x41, 0x47, 0x45, 0x10, 0x04, 0x12, 0x0b, 0x0a, 0x07, 0x54, 0x49, 0x4d, 0x45, 0x4f, - 0x55, 0x54, 0x10, 0x05, 0x2a, 0x39, 0x0a, 0x0e, 0x4e, 0x6f, 0x64, 0x65, 0x4d, 0x65, 0x74, 0x72, - 0x69, 0x63, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, - 0x4e, 0x10, 0x00, 0x12, 0x1a, 0x0a, 0x16, 0x42, 0x45, 0x54, 0x57, 0x45, 0x45, 0x4e, 0x4e, 0x45, - 0x53, 0x53, 0x5f, 0x43, 0x45, 0x4e, 0x54, 0x52, 0x41, 0x4c, 0x49, 0x54, 0x59, 0x10, 0x01, 0x2a, - 0x3b, 0x0a, 0x10, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x48, 0x54, 0x4c, 0x43, 0x53, 0x74, - 0x61, 0x74, 0x65, 0x12, 0x0c, 0x0a, 0x08, 0x41, 0x43, 0x43, 0x45, 0x50, 0x54, 0x45, 0x44, 0x10, - 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x53, 0x45, 0x54, 0x54, 0x4c, 0x45, 0x44, 0x10, 0x01, 0x12, 0x0c, - 0x0a, 0x08, 0x43, 0x41, 0x4e, 0x43, 0x45, 0x4c, 0x45, 0x44, 0x10, 0x02, 0x2a, 0xf6, 0x01, 0x0a, - 0x14, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x46, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x52, - 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x12, 0x17, 0x0a, 0x13, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, - 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x4e, 0x4f, 0x4e, 0x45, 0x10, 0x00, 0x12, 0x1a, - 0x0a, 0x16, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, - 0x5f, 0x54, 0x49, 0x4d, 0x45, 0x4f, 0x55, 0x54, 0x10, 0x01, 0x12, 0x1b, 0x0a, 0x17, 0x46, 0x41, - 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x4e, 0x4f, 0x5f, - 0x52, 0x4f, 0x55, 0x54, 0x45, 0x10, 0x02, 0x12, 0x18, 0x0a, 0x14, 0x46, 0x41, 0x49, 0x4c, 0x55, - 0x52, 0x45, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, - 0x03, 0x12, 0x2c, 0x0a, 0x28, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x52, 0x45, 0x41, - 0x53, 0x4f, 0x4e, 0x5f, 0x49, 0x4e, 0x43, 0x4f, 0x52, 0x52, 0x45, 0x43, 0x54, 0x5f, 0x50, 0x41, - 0x59, 0x4d, 0x45, 0x4e, 0x54, 0x5f, 0x44, 0x45, 0x54, 0x41, 0x49, 0x4c, 0x53, 0x10, 0x04, 0x12, - 0x27, 0x0a, 0x23, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, - 0x4e, 0x5f, 0x49, 0x4e, 0x53, 0x55, 0x46, 0x46, 0x49, 0x43, 0x49, 0x45, 0x4e, 0x54, 0x5f, 0x42, - 0x41, 0x4c, 0x41, 0x4e, 0x43, 0x45, 0x10, 0x05, 0x12, 0x1b, 0x0a, 0x17, 0x46, 0x41, 0x49, 0x4c, - 0x55, 0x52, 0x45, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x43, 0x41, 0x4e, 0x43, 0x45, - 0x4c, 0x45, 0x44, 0x10, 0x06, 0x2a, 0x89, 0x05, 0x0a, 0x0a, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, - 0x65, 0x42, 0x69, 0x74, 0x12, 0x18, 0x0a, 0x14, 0x44, 0x41, 0x54, 0x41, 0x4c, 0x4f, 0x53, 0x53, - 0x5f, 0x50, 0x52, 0x4f, 0x54, 0x45, 0x43, 0x54, 0x5f, 0x52, 0x45, 0x51, 0x10, 0x00, 0x12, 0x18, - 0x0a, 0x14, 0x44, 0x41, 0x54, 0x41, 0x4c, 0x4f, 0x53, 0x53, 0x5f, 0x50, 0x52, 0x4f, 0x54, 0x45, - 0x43, 0x54, 0x5f, 0x4f, 0x50, 0x54, 0x10, 0x01, 0x12, 0x17, 0x0a, 0x13, 0x49, 0x4e, 0x49, 0x54, - 0x49, 0x41, 0x4c, 0x5f, 0x52, 0x4f, 0x55, 0x49, 0x4e, 0x47, 0x5f, 0x53, 0x59, 0x4e, 0x43, 0x10, - 0x03, 0x12, 0x1f, 0x0a, 0x1b, 0x55, 0x50, 0x46, 0x52, 0x4f, 0x4e, 0x54, 0x5f, 0x53, 0x48, 0x55, - 0x54, 0x44, 0x4f, 0x57, 0x4e, 0x5f, 0x53, 0x43, 0x52, 0x49, 0x50, 0x54, 0x5f, 0x52, 0x45, 0x51, - 0x10, 0x04, 0x12, 0x1f, 0x0a, 0x1b, 0x55, 0x50, 0x46, 0x52, 0x4f, 0x4e, 0x54, 0x5f, 0x53, 0x48, - 0x55, 0x54, 0x44, 0x4f, 0x57, 0x4e, 0x5f, 0x53, 0x43, 0x52, 0x49, 0x50, 0x54, 0x5f, 0x4f, 0x50, - 0x54, 0x10, 0x05, 0x12, 0x16, 0x0a, 0x12, 0x47, 0x4f, 0x53, 0x53, 0x49, 0x50, 0x5f, 0x51, 0x55, - 0x45, 0x52, 0x49, 0x45, 0x53, 0x5f, 0x52, 0x45, 0x51, 0x10, 0x06, 0x12, 0x16, 0x0a, 0x12, 0x47, - 0x4f, 0x53, 0x53, 0x49, 0x50, 0x5f, 0x51, 0x55, 0x45, 0x52, 0x49, 0x45, 0x53, 0x5f, 0x4f, 0x50, - 0x54, 0x10, 0x07, 0x12, 0x11, 0x0a, 0x0d, 0x54, 0x4c, 0x56, 0x5f, 0x4f, 0x4e, 0x49, 0x4f, 0x4e, - 0x5f, 0x52, 0x45, 0x51, 0x10, 0x08, 0x12, 0x11, 0x0a, 0x0d, 0x54, 0x4c, 0x56, 0x5f, 0x4f, 0x4e, - 0x49, 0x4f, 0x4e, 0x5f, 0x4f, 0x50, 0x54, 0x10, 0x09, 0x12, 0x1a, 0x0a, 0x16, 0x45, 0x58, 0x54, - 0x5f, 0x47, 0x4f, 0x53, 0x53, 0x49, 0x50, 0x5f, 0x51, 0x55, 0x45, 0x52, 0x49, 0x45, 0x53, 0x5f, - 0x52, 0x45, 0x51, 0x10, 0x0a, 0x12, 0x1a, 0x0a, 0x16, 0x45, 0x58, 0x54, 0x5f, 0x47, 0x4f, 0x53, - 0x53, 0x49, 0x50, 0x5f, 0x51, 0x55, 0x45, 0x52, 0x49, 0x45, 0x53, 0x5f, 0x4f, 0x50, 0x54, 0x10, - 0x0b, 0x12, 0x19, 0x0a, 0x15, 0x53, 0x54, 0x41, 0x54, 0x49, 0x43, 0x5f, 0x52, 0x45, 0x4d, 0x4f, - 0x54, 0x45, 0x5f, 0x4b, 0x45, 0x59, 0x5f, 0x52, 0x45, 0x51, 0x10, 0x0c, 0x12, 0x19, 0x0a, 0x15, - 0x53, 0x54, 0x41, 0x54, 0x49, 0x43, 0x5f, 0x52, 0x45, 0x4d, 0x4f, 0x54, 0x45, 0x5f, 0x4b, 0x45, - 0x59, 0x5f, 0x4f, 0x50, 0x54, 0x10, 0x0d, 0x12, 0x14, 0x0a, 0x10, 0x50, 0x41, 0x59, 0x4d, 0x45, - 0x4e, 0x54, 0x5f, 0x41, 0x44, 0x44, 0x52, 0x5f, 0x52, 0x45, 0x51, 0x10, 0x0e, 0x12, 0x14, 0x0a, - 0x10, 0x50, 0x41, 0x59, 0x4d, 0x45, 0x4e, 0x54, 0x5f, 0x41, 0x44, 0x44, 0x52, 0x5f, 0x4f, 0x50, - 0x54, 0x10, 0x0f, 0x12, 0x0b, 0x0a, 0x07, 0x4d, 0x50, 0x50, 0x5f, 0x52, 0x45, 0x51, 0x10, 0x10, - 0x12, 0x0b, 0x0a, 0x07, 0x4d, 0x50, 0x50, 0x5f, 0x4f, 0x50, 0x54, 0x10, 0x11, 0x12, 0x16, 0x0a, - 0x12, 0x57, 0x55, 0x4d, 0x42, 0x4f, 0x5f, 0x43, 0x48, 0x41, 0x4e, 0x4e, 0x45, 0x4c, 0x53, 0x5f, - 0x52, 0x45, 0x51, 0x10, 0x12, 0x12, 0x16, 0x0a, 0x12, 0x57, 0x55, 0x4d, 0x42, 0x4f, 0x5f, 0x43, - 0x48, 0x41, 0x4e, 0x4e, 0x45, 0x4c, 0x53, 0x5f, 0x4f, 0x50, 0x54, 0x10, 0x13, 0x12, 0x0f, 0x0a, - 0x0b, 0x41, 0x4e, 0x43, 0x48, 0x4f, 0x52, 0x53, 0x5f, 0x52, 0x45, 0x51, 0x10, 0x14, 0x12, 0x0f, - 0x0a, 0x0b, 0x41, 0x4e, 0x43, 0x48, 0x4f, 0x52, 0x53, 0x5f, 0x4f, 0x50, 0x54, 0x10, 0x15, 0x12, - 0x1d, 0x0a, 0x19, 0x41, 0x4e, 0x43, 0x48, 0x4f, 0x52, 0x53, 0x5f, 0x5a, 0x45, 0x52, 0x4f, 0x5f, - 0x46, 0x45, 0x45, 0x5f, 0x48, 0x54, 0x4c, 0x43, 0x5f, 0x52, 0x45, 0x51, 0x10, 0x16, 0x12, 0x1d, - 0x0a, 0x19, 0x41, 0x4e, 0x43, 0x48, 0x4f, 0x52, 0x53, 0x5f, 0x5a, 0x45, 0x52, 0x4f, 0x5f, 0x46, - 0x45, 0x45, 0x5f, 0x48, 0x54, 0x4c, 0x43, 0x5f, 0x4f, 0x50, 0x54, 0x10, 0x17, 0x12, 0x1b, 0x0a, - 0x17, 0x52, 0x4f, 0x55, 0x54, 0x45, 0x5f, 0x42, 0x4c, 0x49, 0x4e, 0x44, 0x49, 0x4e, 0x47, 0x5f, - 0x52, 0x45, 0x51, 0x55, 0x49, 0x52, 0x45, 0x44, 0x10, 0x18, 0x12, 0x1b, 0x0a, 0x17, 0x52, 0x4f, - 0x55, 0x54, 0x45, 0x5f, 0x42, 0x4c, 0x49, 0x4e, 0x44, 0x49, 0x4e, 0x47, 0x5f, 0x4f, 0x50, 0x54, - 0x49, 0x4f, 0x4e, 0x41, 0x4c, 0x10, 0x19, 0x12, 0x0b, 0x0a, 0x07, 0x41, 0x4d, 0x50, 0x5f, 0x52, - 0x45, 0x51, 0x10, 0x1e, 0x12, 0x0b, 0x0a, 0x07, 0x41, 0x4d, 0x50, 0x5f, 0x4f, 0x50, 0x54, 0x10, - 0x1f, 0x2a, 0xac, 0x01, 0x0a, 0x0d, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x46, 0x61, 0x69, 0x6c, - 0x75, 0x72, 0x65, 0x12, 0x1a, 0x0a, 0x16, 0x55, 0x50, 0x44, 0x41, 0x54, 0x45, 0x5f, 0x46, 0x41, - 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, + 0x4c, 0x45, 0x5f, 0x54, 0x41, 0x50, 0x52, 0x4f, 0x4f, 0x54, 0x10, 0x05, 0x12, 0x1a, 0x0a, 0x16, + 0x53, 0x49, 0x4d, 0x50, 0x4c, 0x45, 0x5f, 0x54, 0x41, 0x50, 0x52, 0x4f, 0x4f, 0x54, 0x5f, 0x4f, + 0x56, 0x45, 0x52, 0x4c, 0x41, 0x59, 0x10, 0x06, 0x2a, 0x61, 0x0a, 0x09, 0x49, 0x6e, 0x69, 0x74, + 0x69, 0x61, 0x74, 0x6f, 0x72, 0x12, 0x15, 0x0a, 0x11, 0x49, 0x4e, 0x49, 0x54, 0x49, 0x41, 0x54, + 0x4f, 0x52, 0x5f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x13, 0x0a, 0x0f, + 0x49, 0x4e, 0x49, 0x54, 0x49, 0x41, 0x54, 0x4f, 0x52, 0x5f, 0x4c, 0x4f, 0x43, 0x41, 0x4c, 0x10, + 0x01, 0x12, 0x14, 0x0a, 0x10, 0x49, 0x4e, 0x49, 0x54, 0x49, 0x41, 0x54, 0x4f, 0x52, 0x5f, 0x52, + 0x45, 0x4d, 0x4f, 0x54, 0x45, 0x10, 0x02, 0x12, 0x12, 0x0a, 0x0e, 0x49, 0x4e, 0x49, 0x54, 0x49, + 0x41, 0x54, 0x4f, 0x52, 0x5f, 0x42, 0x4f, 0x54, 0x48, 0x10, 0x03, 0x2a, 0x60, 0x0a, 0x0e, 0x52, + 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x12, 0x10, 0x0a, + 0x0c, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, + 0x0a, 0x0a, 0x06, 0x41, 0x4e, 0x43, 0x48, 0x4f, 0x52, 0x10, 0x01, 0x12, 0x11, 0x0a, 0x0d, 0x49, + 0x4e, 0x43, 0x4f, 0x4d, 0x49, 0x4e, 0x47, 0x5f, 0x48, 0x54, 0x4c, 0x43, 0x10, 0x02, 0x12, 0x11, + 0x0a, 0x0d, 0x4f, 0x55, 0x54, 0x47, 0x4f, 0x49, 0x4e, 0x47, 0x5f, 0x48, 0x54, 0x4c, 0x43, 0x10, + 0x03, 0x12, 0x0a, 0x0a, 0x06, 0x43, 0x4f, 0x4d, 0x4d, 0x49, 0x54, 0x10, 0x04, 0x2a, 0x71, 0x0a, + 0x11, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x4f, 0x75, 0x74, 0x63, 0x6f, + 0x6d, 0x65, 0x12, 0x13, 0x0a, 0x0f, 0x4f, 0x55, 0x54, 0x43, 0x4f, 0x4d, 0x45, 0x5f, 0x55, 0x4e, + 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x43, 0x4c, 0x41, 0x49, 0x4d, + 0x45, 0x44, 0x10, 0x01, 0x12, 0x0d, 0x0a, 0x09, 0x55, 0x4e, 0x43, 0x4c, 0x41, 0x49, 0x4d, 0x45, + 0x44, 0x10, 0x02, 0x12, 0x0d, 0x0a, 0x09, 0x41, 0x42, 0x41, 0x4e, 0x44, 0x4f, 0x4e, 0x45, 0x44, + 0x10, 0x03, 0x12, 0x0f, 0x0a, 0x0b, 0x46, 0x49, 0x52, 0x53, 0x54, 0x5f, 0x53, 0x54, 0x41, 0x47, + 0x45, 0x10, 0x04, 0x12, 0x0b, 0x0a, 0x07, 0x54, 0x49, 0x4d, 0x45, 0x4f, 0x55, 0x54, 0x10, 0x05, + 0x2a, 0x39, 0x0a, 0x0e, 0x4e, 0x6f, 0x64, 0x65, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x54, 0x79, + 0x70, 0x65, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, + 0x1a, 0x0a, 0x16, 0x42, 0x45, 0x54, 0x57, 0x45, 0x45, 0x4e, 0x4e, 0x45, 0x53, 0x53, 0x5f, 0x43, + 0x45, 0x4e, 0x54, 0x52, 0x41, 0x4c, 0x49, 0x54, 0x59, 0x10, 0x01, 0x2a, 0x3b, 0x0a, 0x10, 0x49, + 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x48, 0x54, 0x4c, 0x43, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, + 0x0c, 0x0a, 0x08, 0x41, 0x43, 0x43, 0x45, 0x50, 0x54, 0x45, 0x44, 0x10, 0x00, 0x12, 0x0b, 0x0a, + 0x07, 0x53, 0x45, 0x54, 0x54, 0x4c, 0x45, 0x44, 0x10, 0x01, 0x12, 0x0c, 0x0a, 0x08, 0x43, 0x41, + 0x4e, 0x43, 0x45, 0x4c, 0x45, 0x44, 0x10, 0x02, 0x2a, 0xf6, 0x01, 0x0a, 0x14, 0x50, 0x61, 0x79, + 0x6d, 0x65, 0x6e, 0x74, 0x46, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x52, 0x65, 0x61, 0x73, 0x6f, + 0x6e, 0x12, 0x17, 0x0a, 0x13, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x52, 0x45, 0x41, + 0x53, 0x4f, 0x4e, 0x5f, 0x4e, 0x4f, 0x4e, 0x45, 0x10, 0x00, 0x12, 0x1a, 0x0a, 0x16, 0x46, 0x41, + 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x54, 0x49, 0x4d, + 0x45, 0x4f, 0x55, 0x54, 0x10, 0x01, 0x12, 0x1b, 0x0a, 0x17, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, + 0x45, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x4e, 0x4f, 0x5f, 0x52, 0x4f, 0x55, 0x54, + 0x45, 0x10, 0x02, 0x12, 0x18, 0x0a, 0x14, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x52, + 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, 0x03, 0x12, 0x2c, 0x0a, + 0x28, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, + 0x49, 0x4e, 0x43, 0x4f, 0x52, 0x52, 0x45, 0x43, 0x54, 0x5f, 0x50, 0x41, 0x59, 0x4d, 0x45, 0x4e, + 0x54, 0x5f, 0x44, 0x45, 0x54, 0x41, 0x49, 0x4c, 0x53, 0x10, 0x04, 0x12, 0x27, 0x0a, 0x23, 0x46, + 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x49, 0x4e, + 0x53, 0x55, 0x46, 0x46, 0x49, 0x43, 0x49, 0x45, 0x4e, 0x54, 0x5f, 0x42, 0x41, 0x4c, 0x41, 0x4e, + 0x43, 0x45, 0x10, 0x05, 0x12, 0x1b, 0x0a, 0x17, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, + 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x43, 0x41, 0x4e, 0x43, 0x45, 0x4c, 0x45, 0x44, 0x10, + 0x06, 0x2a, 0x89, 0x05, 0x0a, 0x0a, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x42, 0x69, 0x74, + 0x12, 0x18, 0x0a, 0x14, 0x44, 0x41, 0x54, 0x41, 0x4c, 0x4f, 0x53, 0x53, 0x5f, 0x50, 0x52, 0x4f, + 0x54, 0x45, 0x43, 0x54, 0x5f, 0x52, 0x45, 0x51, 0x10, 0x00, 0x12, 0x18, 0x0a, 0x14, 0x44, 0x41, + 0x54, 0x41, 0x4c, 0x4f, 0x53, 0x53, 0x5f, 0x50, 0x52, 0x4f, 0x54, 0x45, 0x43, 0x54, 0x5f, 0x4f, + 0x50, 0x54, 0x10, 0x01, 0x12, 0x17, 0x0a, 0x13, 0x49, 0x4e, 0x49, 0x54, 0x49, 0x41, 0x4c, 0x5f, + 0x52, 0x4f, 0x55, 0x49, 0x4e, 0x47, 0x5f, 0x53, 0x59, 0x4e, 0x43, 0x10, 0x03, 0x12, 0x1f, 0x0a, + 0x1b, 0x55, 0x50, 0x46, 0x52, 0x4f, 0x4e, 0x54, 0x5f, 0x53, 0x48, 0x55, 0x54, 0x44, 0x4f, 0x57, + 0x4e, 0x5f, 0x53, 0x43, 0x52, 0x49, 0x50, 0x54, 0x5f, 0x52, 0x45, 0x51, 0x10, 0x04, 0x12, 0x1f, + 0x0a, 0x1b, 0x55, 0x50, 0x46, 0x52, 0x4f, 0x4e, 0x54, 0x5f, 0x53, 0x48, 0x55, 0x54, 0x44, 0x4f, + 0x57, 0x4e, 0x5f, 0x53, 0x43, 0x52, 0x49, 0x50, 0x54, 0x5f, 0x4f, 0x50, 0x54, 0x10, 0x05, 0x12, + 0x16, 0x0a, 0x12, 0x47, 0x4f, 0x53, 0x53, 0x49, 0x50, 0x5f, 0x51, 0x55, 0x45, 0x52, 0x49, 0x45, + 0x53, 0x5f, 0x52, 0x45, 0x51, 0x10, 0x06, 0x12, 0x16, 0x0a, 0x12, 0x47, 0x4f, 0x53, 0x53, 0x49, + 0x50, 0x5f, 0x51, 0x55, 0x45, 0x52, 0x49, 0x45, 0x53, 0x5f, 0x4f, 0x50, 0x54, 0x10, 0x07, 0x12, + 0x11, 0x0a, 0x0d, 0x54, 0x4c, 0x56, 0x5f, 0x4f, 0x4e, 0x49, 0x4f, 0x4e, 0x5f, 0x52, 0x45, 0x51, + 0x10, 0x08, 0x12, 0x11, 0x0a, 0x0d, 0x54, 0x4c, 0x56, 0x5f, 0x4f, 0x4e, 0x49, 0x4f, 0x4e, 0x5f, + 0x4f, 0x50, 0x54, 0x10, 0x09, 0x12, 0x1a, 0x0a, 0x16, 0x45, 0x58, 0x54, 0x5f, 0x47, 0x4f, 0x53, + 0x53, 0x49, 0x50, 0x5f, 0x51, 0x55, 0x45, 0x52, 0x49, 0x45, 0x53, 0x5f, 0x52, 0x45, 0x51, 0x10, + 0x0a, 0x12, 0x1a, 0x0a, 0x16, 0x45, 0x58, 0x54, 0x5f, 0x47, 0x4f, 0x53, 0x53, 0x49, 0x50, 0x5f, + 0x51, 0x55, 0x45, 0x52, 0x49, 0x45, 0x53, 0x5f, 0x4f, 0x50, 0x54, 0x10, 0x0b, 0x12, 0x19, 0x0a, + 0x15, 0x53, 0x54, 0x41, 0x54, 0x49, 0x43, 0x5f, 0x52, 0x45, 0x4d, 0x4f, 0x54, 0x45, 0x5f, 0x4b, + 0x45, 0x59, 0x5f, 0x52, 0x45, 0x51, 0x10, 0x0c, 0x12, 0x19, 0x0a, 0x15, 0x53, 0x54, 0x41, 0x54, + 0x49, 0x43, 0x5f, 0x52, 0x45, 0x4d, 0x4f, 0x54, 0x45, 0x5f, 0x4b, 0x45, 0x59, 0x5f, 0x4f, 0x50, + 0x54, 0x10, 0x0d, 0x12, 0x14, 0x0a, 0x10, 0x50, 0x41, 0x59, 0x4d, 0x45, 0x4e, 0x54, 0x5f, 0x41, + 0x44, 0x44, 0x52, 0x5f, 0x52, 0x45, 0x51, 0x10, 0x0e, 0x12, 0x14, 0x0a, 0x10, 0x50, 0x41, 0x59, + 0x4d, 0x45, 0x4e, 0x54, 0x5f, 0x41, 0x44, 0x44, 0x52, 0x5f, 0x4f, 0x50, 0x54, 0x10, 0x0f, 0x12, + 0x0b, 0x0a, 0x07, 0x4d, 0x50, 0x50, 0x5f, 0x52, 0x45, 0x51, 0x10, 0x10, 0x12, 0x0b, 0x0a, 0x07, + 0x4d, 0x50, 0x50, 0x5f, 0x4f, 0x50, 0x54, 0x10, 0x11, 0x12, 0x16, 0x0a, 0x12, 0x57, 0x55, 0x4d, + 0x42, 0x4f, 0x5f, 0x43, 0x48, 0x41, 0x4e, 0x4e, 0x45, 0x4c, 0x53, 0x5f, 0x52, 0x45, 0x51, 0x10, + 0x12, 0x12, 0x16, 0x0a, 0x12, 0x57, 0x55, 0x4d, 0x42, 0x4f, 0x5f, 0x43, 0x48, 0x41, 0x4e, 0x4e, + 0x45, 0x4c, 0x53, 0x5f, 0x4f, 0x50, 0x54, 0x10, 0x13, 0x12, 0x0f, 0x0a, 0x0b, 0x41, 0x4e, 0x43, + 0x48, 0x4f, 0x52, 0x53, 0x5f, 0x52, 0x45, 0x51, 0x10, 0x14, 0x12, 0x0f, 0x0a, 0x0b, 0x41, 0x4e, + 0x43, 0x48, 0x4f, 0x52, 0x53, 0x5f, 0x4f, 0x50, 0x54, 0x10, 0x15, 0x12, 0x1d, 0x0a, 0x19, 0x41, + 0x4e, 0x43, 0x48, 0x4f, 0x52, 0x53, 0x5f, 0x5a, 0x45, 0x52, 0x4f, 0x5f, 0x46, 0x45, 0x45, 0x5f, + 0x48, 0x54, 0x4c, 0x43, 0x5f, 0x52, 0x45, 0x51, 0x10, 0x16, 0x12, 0x1d, 0x0a, 0x19, 0x41, 0x4e, + 0x43, 0x48, 0x4f, 0x52, 0x53, 0x5f, 0x5a, 0x45, 0x52, 0x4f, 0x5f, 0x46, 0x45, 0x45, 0x5f, 0x48, + 0x54, 0x4c, 0x43, 0x5f, 0x4f, 0x50, 0x54, 0x10, 0x17, 0x12, 0x1b, 0x0a, 0x17, 0x52, 0x4f, 0x55, + 0x54, 0x45, 0x5f, 0x42, 0x4c, 0x49, 0x4e, 0x44, 0x49, 0x4e, 0x47, 0x5f, 0x52, 0x45, 0x51, 0x55, + 0x49, 0x52, 0x45, 0x44, 0x10, 0x18, 0x12, 0x1b, 0x0a, 0x17, 0x52, 0x4f, 0x55, 0x54, 0x45, 0x5f, + 0x42, 0x4c, 0x49, 0x4e, 0x44, 0x49, 0x4e, 0x47, 0x5f, 0x4f, 0x50, 0x54, 0x49, 0x4f, 0x4e, 0x41, + 0x4c, 0x10, 0x19, 0x12, 0x0b, 0x0a, 0x07, 0x41, 0x4d, 0x50, 0x5f, 0x52, 0x45, 0x51, 0x10, 0x1e, + 0x12, 0x0b, 0x0a, 0x07, 0x41, 0x4d, 0x50, 0x5f, 0x4f, 0x50, 0x54, 0x10, 0x1f, 0x2a, 0xac, 0x01, + 0x0a, 0x0d, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x46, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x12, 0x1a, 0x0a, 0x16, 0x55, 0x50, 0x44, 0x41, 0x54, 0x45, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, - 0x45, 0x5f, 0x50, 0x45, 0x4e, 0x44, 0x49, 0x4e, 0x47, 0x10, 0x01, 0x12, 0x1c, 0x0a, 0x18, 0x55, - 0x50, 0x44, 0x41, 0x54, 0x45, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x4e, 0x4f, - 0x54, 0x5f, 0x46, 0x4f, 0x55, 0x4e, 0x44, 0x10, 0x02, 0x12, 0x1f, 0x0a, 0x1b, 0x55, 0x50, 0x44, - 0x41, 0x54, 0x45, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x49, 0x4e, 0x54, 0x45, - 0x52, 0x4e, 0x41, 0x4c, 0x5f, 0x45, 0x52, 0x52, 0x10, 0x03, 0x12, 0x24, 0x0a, 0x20, 0x55, 0x50, - 0x44, 0x41, 0x54, 0x45, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x49, 0x4e, 0x56, - 0x41, 0x4c, 0x49, 0x44, 0x5f, 0x50, 0x41, 0x52, 0x41, 0x4d, 0x45, 0x54, 0x45, 0x52, 0x10, 0x04, - 0x32, 0xb9, 0x27, 0x0a, 0x09, 0x4c, 0x69, 0x67, 0x68, 0x74, 0x6e, 0x69, 0x6e, 0x67, 0x12, 0x4a, - 0x0a, 0x0d, 0x57, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x12, - 0x1b, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x42, 0x61, - 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x6c, - 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x42, 0x61, 0x6c, 0x61, 0x6e, - 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4d, 0x0a, 0x0e, 0x43, 0x68, - 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x12, 0x1c, 0x2e, 0x6c, - 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x42, 0x61, 0x6c, 0x61, - 0x6e, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x6c, 0x6e, 0x72, - 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, - 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4b, 0x0a, 0x0f, 0x47, 0x65, 0x74, - 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x1d, 0x2e, 0x6c, - 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, - 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x6c, 0x6e, - 0x72, 0x70, 0x63, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x44, - 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x44, 0x0a, 0x0b, 0x45, 0x73, 0x74, 0x69, 0x6d, 0x61, - 0x74, 0x65, 0x46, 0x65, 0x65, 0x12, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x45, 0x73, - 0x74, 0x69, 0x6d, 0x61, 0x74, 0x65, 0x46, 0x65, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x45, 0x73, 0x74, 0x69, 0x6d, 0x61, 0x74, - 0x65, 0x46, 0x65, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3e, 0x0a, 0x09, - 0x53, 0x65, 0x6e, 0x64, 0x43, 0x6f, 0x69, 0x6e, 0x73, 0x12, 0x17, 0x2e, 0x6c, 0x6e, 0x72, 0x70, - 0x63, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x43, 0x6f, 0x69, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x43, - 0x6f, 0x69, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x44, 0x0a, 0x0b, - 0x4c, 0x69, 0x73, 0x74, 0x55, 0x6e, 0x73, 0x70, 0x65, 0x6e, 0x74, 0x12, 0x19, 0x2e, 0x6c, 0x6e, - 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x55, 0x6e, 0x73, 0x70, 0x65, 0x6e, 0x74, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4c, - 0x69, 0x73, 0x74, 0x55, 0x6e, 0x73, 0x70, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x4c, 0x0a, 0x15, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x54, - 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x1d, 0x2e, 0x6c, 0x6e, - 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, - 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x12, 0x2e, 0x6c, 0x6e, 0x72, - 0x70, 0x63, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x30, 0x01, - 0x12, 0x3b, 0x0a, 0x08, 0x53, 0x65, 0x6e, 0x64, 0x4d, 0x61, 0x6e, 0x79, 0x12, 0x16, 0x2e, 0x6c, - 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x4d, 0x61, 0x6e, 0x79, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x6e, - 0x64, 0x4d, 0x61, 0x6e, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x41, 0x0a, - 0x0a, 0x4e, 0x65, 0x77, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x18, 0x2e, 0x6c, 0x6e, - 0x72, 0x70, 0x63, 0x2e, 0x4e, 0x65, 0x77, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4e, 0x65, - 0x77, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x12, 0x44, 0x0a, 0x0b, 0x53, 0x69, 0x67, 0x6e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, - 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x4d, 0x65, 0x73, 0x73, - 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, - 0x70, 0x63, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4a, 0x0a, 0x0d, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, - 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x1b, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, - 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x56, 0x65, 0x72, - 0x69, 0x66, 0x79, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x44, 0x0a, 0x0b, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x50, 0x65, 0x65, - 0x72, 0x12, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, - 0x74, 0x50, 0x65, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x6c, - 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x50, 0x65, 0x65, 0x72, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4d, 0x0a, 0x0e, 0x44, 0x69, 0x73, 0x63, - 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x50, 0x65, 0x65, 0x72, 0x12, 0x1c, 0x2e, 0x6c, 0x6e, 0x72, - 0x70, 0x63, 0x2e, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x50, 0x65, 0x65, - 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, - 0x2e, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x50, 0x65, 0x65, 0x72, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3e, 0x0a, 0x09, 0x4c, 0x69, 0x73, 0x74, 0x50, - 0x65, 0x65, 0x72, 0x73, 0x12, 0x17, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, - 0x74, 0x50, 0x65, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, - 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x65, 0x65, 0x72, 0x73, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x47, 0x0a, 0x13, 0x53, 0x75, 0x62, 0x73, 0x63, - 0x72, 0x69, 0x62, 0x65, 0x50, 0x65, 0x65, 0x72, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x1c, - 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x65, 0x65, 0x72, 0x45, 0x76, 0x65, 0x6e, 0x74, - 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x1a, 0x10, 0x2e, 0x6c, - 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x65, 0x65, 0x72, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x30, 0x01, - 0x12, 0x38, 0x0a, 0x07, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x15, 0x2e, 0x6c, 0x6e, - 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x49, 0x6e, - 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x47, 0x0a, 0x0c, 0x47, 0x65, - 0x74, 0x44, 0x65, 0x62, 0x75, 0x67, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, - 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x44, 0x65, 0x62, 0x75, 0x67, 0x49, 0x6e, 0x66, 0x6f, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x47, - 0x65, 0x74, 0x44, 0x65, 0x62, 0x75, 0x67, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x50, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x52, 0x65, 0x63, 0x6f, 0x76, 0x65, - 0x72, 0x79, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1d, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x47, - 0x65, 0x74, 0x52, 0x65, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, - 0x74, 0x52, 0x65, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x50, 0x0a, 0x0f, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, - 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x12, 0x1d, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, - 0x2e, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, - 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x47, 0x0a, 0x0c, 0x4c, 0x69, 0x73, 0x74, 0x43, - 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x12, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, - 0x4c, 0x69, 0x73, 0x74, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, - 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x12, 0x56, 0x0a, 0x16, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x43, 0x68, 0x61, - 0x6e, 0x6e, 0x65, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x1f, 0x2e, 0x6c, 0x6e, 0x72, - 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x53, - 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x1a, 0x19, 0x2e, 0x6c, 0x6e, - 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, - 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x30, 0x01, 0x12, 0x4d, 0x0a, 0x0e, 0x43, 0x6c, 0x6f, 0x73, - 0x65, 0x64, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x12, 0x1c, 0x2e, 0x6c, 0x6e, 0x72, - 0x70, 0x63, 0x2e, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x64, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, - 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, - 0x2e, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x64, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x41, 0x0a, 0x0f, 0x4f, 0x70, 0x65, 0x6e, 0x43, - 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x53, 0x79, 0x6e, 0x63, 0x12, 0x19, 0x2e, 0x6c, 0x6e, 0x72, - 0x70, 0x63, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x13, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, - 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x43, 0x0a, 0x0b, 0x4f, 0x70, - 0x65, 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x12, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, - 0x63, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4f, 0x70, 0x65, - 0x6e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x30, 0x01, 0x12, - 0x53, 0x0a, 0x10, 0x42, 0x61, 0x74, 0x63, 0x68, 0x4f, 0x70, 0x65, 0x6e, 0x43, 0x68, 0x61, 0x6e, - 0x6e, 0x65, 0x6c, 0x12, 0x1e, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x42, 0x61, 0x74, 0x63, - 0x68, 0x4f, 0x70, 0x65, 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x42, 0x61, 0x74, 0x63, - 0x68, 0x4f, 0x70, 0x65, 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4c, 0x0a, 0x10, 0x46, 0x75, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x53, - 0x74, 0x61, 0x74, 0x65, 0x53, 0x74, 0x65, 0x70, 0x12, 0x1b, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, - 0x2e, 0x46, 0x75, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x69, 0x74, 0x69, - 0x6f, 0x6e, 0x4d, 0x73, 0x67, 0x1a, 0x1b, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x75, - 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x53, 0x74, 0x61, 0x74, 0x65, 0x53, 0x74, 0x65, 0x70, 0x52, 0x65, - 0x73, 0x70, 0x12, 0x50, 0x0a, 0x0f, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x41, 0x63, 0x63, - 0x65, 0x70, 0x74, 0x6f, 0x72, 0x12, 0x1c, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, - 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x1a, 0x1b, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, - 0x6e, 0x65, 0x6c, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x28, 0x01, 0x30, 0x01, 0x12, 0x46, 0x0a, 0x0c, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x43, 0x68, 0x61, - 0x6e, 0x6e, 0x65, 0x6c, 0x12, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x6c, 0x6f, - 0x73, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x18, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x53, 0x74, - 0x61, 0x74, 0x75, 0x73, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x30, 0x01, 0x12, 0x4d, 0x0a, 0x0e, - 0x41, 0x62, 0x61, 0x6e, 0x64, 0x6f, 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x12, 0x1c, - 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x62, 0x61, 0x6e, 0x64, 0x6f, 0x6e, 0x43, 0x68, - 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x6c, - 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x62, 0x61, 0x6e, 0x64, 0x6f, 0x6e, 0x43, 0x68, 0x61, 0x6e, - 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3f, 0x0a, 0x0b, 0x53, - 0x65, 0x6e, 0x64, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x12, 0x2e, 0x6c, 0x6e, 0x72, - 0x70, 0x63, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x13, - 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x03, 0x88, 0x02, 0x01, 0x28, 0x01, 0x30, 0x01, 0x12, 0x3a, 0x0a, 0x0f, - 0x53, 0x65, 0x6e, 0x64, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x53, 0x79, 0x6e, 0x63, 0x12, - 0x12, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x13, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x6e, 0x64, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x46, 0x0a, 0x0b, 0x53, 0x65, 0x6e, 0x64, - 0x54, 0x6f, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x12, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, - 0x53, 0x65, 0x6e, 0x64, 0x54, 0x6f, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x13, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x03, 0x88, 0x02, 0x01, 0x28, 0x01, 0x30, 0x01, - 0x12, 0x41, 0x0a, 0x0f, 0x53, 0x65, 0x6e, 0x64, 0x54, 0x6f, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x53, - 0x79, 0x6e, 0x63, 0x12, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x6e, 0x64, + 0x45, 0x5f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x1a, 0x0a, 0x16, 0x55, + 0x50, 0x44, 0x41, 0x54, 0x45, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x50, 0x45, + 0x4e, 0x44, 0x49, 0x4e, 0x47, 0x10, 0x01, 0x12, 0x1c, 0x0a, 0x18, 0x55, 0x50, 0x44, 0x41, 0x54, + 0x45, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x4e, 0x4f, 0x54, 0x5f, 0x46, 0x4f, + 0x55, 0x4e, 0x44, 0x10, 0x02, 0x12, 0x1f, 0x0a, 0x1b, 0x55, 0x50, 0x44, 0x41, 0x54, 0x45, 0x5f, + 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x49, 0x4e, 0x54, 0x45, 0x52, 0x4e, 0x41, 0x4c, + 0x5f, 0x45, 0x52, 0x52, 0x10, 0x03, 0x12, 0x24, 0x0a, 0x20, 0x55, 0x50, 0x44, 0x41, 0x54, 0x45, + 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x49, 0x4e, 0x56, 0x41, 0x4c, 0x49, 0x44, + 0x5f, 0x50, 0x41, 0x52, 0x41, 0x4d, 0x45, 0x54, 0x45, 0x52, 0x10, 0x04, 0x32, 0xb9, 0x27, 0x0a, + 0x09, 0x4c, 0x69, 0x67, 0x68, 0x74, 0x6e, 0x69, 0x6e, 0x67, 0x12, 0x4a, 0x0a, 0x0d, 0x57, 0x61, + 0x6c, 0x6c, 0x65, 0x74, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x12, 0x1b, 0x2e, 0x6c, 0x6e, + 0x72, 0x70, 0x63, 0x2e, 0x57, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, + 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, + 0x2e, 0x57, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4d, 0x0a, 0x0e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, + 0x6c, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x12, 0x1c, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, + 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, + 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4b, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x54, 0x72, 0x61, 0x6e, + 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x1d, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, + 0x2e, 0x47, 0x65, 0x74, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, + 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x65, 0x74, 0x61, 0x69, + 0x6c, 0x73, 0x12, 0x44, 0x0a, 0x0b, 0x45, 0x73, 0x74, 0x69, 0x6d, 0x61, 0x74, 0x65, 0x46, 0x65, + 0x65, 0x12, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x45, 0x73, 0x74, 0x69, 0x6d, 0x61, + 0x74, 0x65, 0x46, 0x65, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x6c, + 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x45, 0x73, 0x74, 0x69, 0x6d, 0x61, 0x74, 0x65, 0x46, 0x65, 0x65, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3e, 0x0a, 0x09, 0x53, 0x65, 0x6e, 0x64, + 0x43, 0x6f, 0x69, 0x6e, 0x73, 0x12, 0x17, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, + 0x6e, 0x64, 0x43, 0x6f, 0x69, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, + 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x43, 0x6f, 0x69, 0x6e, 0x73, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x44, 0x0a, 0x0b, 0x4c, 0x69, 0x73, 0x74, + 0x55, 0x6e, 0x73, 0x70, 0x65, 0x6e, 0x74, 0x12, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, + 0x4c, 0x69, 0x73, 0x74, 0x55, 0x6e, 0x73, 0x70, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x55, + 0x6e, 0x73, 0x70, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4c, + 0x0a, 0x15, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x54, 0x72, 0x61, 0x6e, 0x73, + 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x1d, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, + 0x47, 0x65, 0x74, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x12, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x54, + 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x30, 0x01, 0x12, 0x3b, 0x0a, 0x08, + 0x53, 0x65, 0x6e, 0x64, 0x4d, 0x61, 0x6e, 0x79, 0x12, 0x16, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, + 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x4d, 0x61, 0x6e, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x17, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x4d, 0x61, 0x6e, + 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x41, 0x0a, 0x0a, 0x4e, 0x65, 0x77, + 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x18, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, + 0x4e, 0x65, 0x77, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4e, 0x65, 0x77, 0x41, 0x64, 0x64, + 0x72, 0x65, 0x73, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x44, 0x0a, 0x0b, + 0x53, 0x69, 0x67, 0x6e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x19, 0x2e, 0x6c, 0x6e, + 0x72, 0x70, 0x63, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, + 0x69, 0x67, 0x6e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x4a, 0x0a, 0x0d, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x4d, 0x65, 0x73, 0x73, + 0x61, 0x67, 0x65, 0x12, 0x1b, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x56, 0x65, 0x72, 0x69, + 0x66, 0x79, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x1c, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x4d, + 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x44, + 0x0a, 0x0b, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x50, 0x65, 0x65, 0x72, 0x12, 0x19, 0x2e, + 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x50, 0x65, 0x65, + 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, + 0x2e, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x50, 0x65, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4d, 0x0a, 0x0e, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x6e, 0x6e, 0x65, + 0x63, 0x74, 0x50, 0x65, 0x65, 0x72, 0x12, 0x1c, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x44, + 0x69, 0x73, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x50, 0x65, 0x65, 0x72, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x44, 0x69, 0x73, + 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x50, 0x65, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x3e, 0x0a, 0x09, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x65, 0x65, 0x72, 0x73, + 0x12, 0x17, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x65, 0x65, + 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x6c, 0x6e, 0x72, 0x70, + 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x65, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x47, 0x0a, 0x13, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, + 0x50, 0x65, 0x65, 0x72, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x1c, 0x2e, 0x6c, 0x6e, 0x72, + 0x70, 0x63, 0x2e, 0x50, 0x65, 0x65, 0x72, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x53, 0x75, 0x62, 0x73, + 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x1a, 0x10, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, + 0x2e, 0x50, 0x65, 0x65, 0x72, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x30, 0x01, 0x12, 0x38, 0x0a, 0x07, + 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x15, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, + 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, + 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x47, 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x44, 0x65, 0x62, + 0x75, 0x67, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x47, + 0x65, 0x74, 0x44, 0x65, 0x62, 0x75, 0x67, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x44, 0x65, + 0x62, 0x75, 0x67, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x50, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x52, 0x65, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x49, 0x6e, + 0x66, 0x6f, 0x12, 0x1d, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x65, + 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x1e, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x65, 0x63, + 0x6f, 0x76, 0x65, 0x72, 0x79, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x50, 0x0a, 0x0f, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x43, 0x68, 0x61, 0x6e, + 0x6e, 0x65, 0x6c, 0x73, 0x12, 0x1d, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x65, 0x6e, + 0x64, 0x69, 0x6e, 0x67, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x65, 0x6e, 0x64, + 0x69, 0x6e, 0x67, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x47, 0x0a, 0x0c, 0x4c, 0x69, 0x73, 0x74, 0x43, 0x68, 0x61, 0x6e, 0x6e, + 0x65, 0x6c, 0x73, 0x12, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, + 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x1b, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x43, 0x68, 0x61, 0x6e, + 0x6e, 0x65, 0x6c, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x56, 0x0a, 0x16, + 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, + 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x1f, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, + 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x53, 0x75, 0x62, 0x73, 0x63, + 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x1a, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, + 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x55, 0x70, 0x64, 0x61, + 0x74, 0x65, 0x30, 0x01, 0x12, 0x4d, 0x0a, 0x0e, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x64, 0x43, 0x68, + 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x12, 0x1c, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, + 0x6c, 0x6f, 0x73, 0x65, 0x64, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x6c, 0x6f, + 0x73, 0x65, 0x64, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x41, 0x0a, 0x0f, 0x4f, 0x70, 0x65, 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x6e, + 0x65, 0x6c, 0x53, 0x79, 0x6e, 0x63, 0x12, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4f, + 0x70, 0x65, 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x13, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, + 0x6c, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x43, 0x0a, 0x0b, 0x4f, 0x70, 0x65, 0x6e, 0x43, 0x68, + 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x12, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4f, 0x70, + 0x65, 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x17, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x53, 0x74, 0x61, + 0x74, 0x75, 0x73, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x30, 0x01, 0x12, 0x53, 0x0a, 0x10, 0x42, + 0x61, 0x74, 0x63, 0x68, 0x4f, 0x70, 0x65, 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x12, + 0x1e, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x42, 0x61, 0x74, 0x63, 0x68, 0x4f, 0x70, 0x65, + 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x1f, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x42, 0x61, 0x74, 0x63, 0x68, 0x4f, 0x70, 0x65, + 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x4c, 0x0a, 0x10, 0x46, 0x75, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x53, 0x74, 0x61, 0x74, 0x65, + 0x53, 0x74, 0x65, 0x70, 0x12, 0x1b, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x75, 0x6e, + 0x64, 0x69, 0x6e, 0x67, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x73, + 0x67, 0x1a, 0x1b, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x75, 0x6e, 0x64, 0x69, 0x6e, + 0x67, 0x53, 0x74, 0x61, 0x74, 0x65, 0x53, 0x74, 0x65, 0x70, 0x52, 0x65, 0x73, 0x70, 0x12, 0x50, + 0x0a, 0x0f, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x6f, + 0x72, 0x12, 0x1c, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, + 0x6c, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x1a, + 0x1b, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x41, + 0x63, 0x63, 0x65, 0x70, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x28, 0x01, 0x30, 0x01, + 0x12, 0x46, 0x0a, 0x0c, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, + 0x12, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x43, 0x68, + 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x6c, + 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, + 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x30, 0x01, 0x12, 0x4d, 0x0a, 0x0e, 0x41, 0x62, 0x61, 0x6e, + 0x64, 0x6f, 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x12, 0x1c, 0x2e, 0x6c, 0x6e, 0x72, + 0x70, 0x63, 0x2e, 0x41, 0x62, 0x61, 0x6e, 0x64, 0x6f, 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, + 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, + 0x2e, 0x41, 0x62, 0x61, 0x6e, 0x64, 0x6f, 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3f, 0x0a, 0x0b, 0x53, 0x65, 0x6e, 0x64, 0x50, + 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x12, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, + 0x65, 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x13, 0x2e, 0x6c, 0x6e, 0x72, + 0x70, 0x63, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x03, 0x88, 0x02, 0x01, 0x28, 0x01, 0x30, 0x01, 0x12, 0x3a, 0x0a, 0x0f, 0x53, 0x65, 0x6e, 0x64, + 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x53, 0x79, 0x6e, 0x63, 0x12, 0x12, 0x2e, 0x6c, 0x6e, + 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x13, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x46, 0x0a, 0x0b, 0x53, 0x65, 0x6e, 0x64, 0x54, 0x6f, 0x52, 0x6f, + 0x75, 0x74, 0x65, 0x12, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x54, 0x6f, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x13, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x37, 0x0a, 0x0a, 0x41, 0x64, 0x64, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, - 0x65, 0x12, 0x0e, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, - 0x65, 0x1a, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x64, 0x64, 0x49, 0x6e, 0x76, - 0x6f, 0x69, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x45, 0x0a, 0x0c, - 0x4c, 0x69, 0x73, 0x74, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x73, 0x12, 0x19, 0x2e, 0x6c, - 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, - 0x4c, 0x69, 0x73, 0x74, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x33, 0x0a, 0x0d, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x49, 0x6e, 0x76, - 0x6f, 0x69, 0x63, 0x65, 0x12, 0x12, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x61, 0x79, - 0x6d, 0x65, 0x6e, 0x74, 0x48, 0x61, 0x73, 0x68, 0x1a, 0x0e, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, - 0x2e, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x12, 0x41, 0x0a, 0x11, 0x53, 0x75, 0x62, 0x73, - 0x63, 0x72, 0x69, 0x62, 0x65, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x73, 0x12, 0x1a, 0x2e, - 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x53, 0x75, 0x62, - 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x1a, 0x0e, 0x2e, 0x6c, 0x6e, 0x72, 0x70, - 0x63, 0x2e, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x30, 0x01, 0x12, 0x32, 0x0a, 0x0c, 0x44, - 0x65, 0x63, 0x6f, 0x64, 0x65, 0x50, 0x61, 0x79, 0x52, 0x65, 0x71, 0x12, 0x13, 0x2e, 0x6c, 0x6e, - 0x72, 0x70, 0x63, 0x2e, 0x50, 0x61, 0x79, 0x52, 0x65, 0x71, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, - 0x1a, 0x0d, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x61, 0x79, 0x52, 0x65, 0x71, 0x12, - 0x47, 0x0a, 0x0c, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x12, - 0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x61, 0x79, 0x6d, - 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x6c, 0x6e, + 0x6e, 0x73, 0x65, 0x22, 0x03, 0x88, 0x02, 0x01, 0x28, 0x01, 0x30, 0x01, 0x12, 0x41, 0x0a, 0x0f, + 0x53, 0x65, 0x6e, 0x64, 0x54, 0x6f, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x53, 0x79, 0x6e, 0x63, 0x12, + 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x54, 0x6f, 0x52, 0x6f, + 0x75, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x13, 0x2e, 0x6c, 0x6e, 0x72, + 0x70, 0x63, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x37, 0x0a, 0x0a, 0x41, 0x64, 0x64, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x12, 0x0e, 0x2e, + 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x1a, 0x19, 0x2e, + 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x64, 0x64, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x45, 0x0a, 0x0c, 0x4c, 0x69, 0x73, 0x74, + 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x73, 0x12, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, + 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, + 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x33, 0x0a, 0x0d, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, + 0x12, 0x12, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, + 0x48, 0x61, 0x73, 0x68, 0x1a, 0x0e, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x49, 0x6e, 0x76, + 0x6f, 0x69, 0x63, 0x65, 0x12, 0x41, 0x0a, 0x11, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, + 0x65, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x73, 0x12, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, + 0x63, 0x2e, 0x49, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, + 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x1a, 0x0e, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x49, 0x6e, + 0x76, 0x6f, 0x69, 0x63, 0x65, 0x30, 0x01, 0x12, 0x32, 0x0a, 0x0c, 0x44, 0x65, 0x63, 0x6f, 0x64, + 0x65, 0x50, 0x61, 0x79, 0x52, 0x65, 0x71, 0x12, 0x13, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, + 0x50, 0x61, 0x79, 0x52, 0x65, 0x71, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x1a, 0x0d, 0x2e, 0x6c, + 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x61, 0x79, 0x52, 0x65, 0x71, 0x12, 0x47, 0x0a, 0x0c, 0x4c, + 0x69, 0x73, 0x74, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x73, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4a, 0x0a, 0x0d, 0x44, 0x65, 0x6c, 0x65, - 0x74, 0x65, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x1b, 0x2e, 0x6c, 0x6e, 0x72, 0x70, - 0x63, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x44, - 0x65, 0x6c, 0x65, 0x74, 0x65, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x56, 0x0a, 0x11, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x41, 0x6c, - 0x6c, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x1f, 0x2e, 0x6c, 0x6e, 0x72, 0x70, - 0x63, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x41, 0x6c, 0x6c, 0x50, 0x61, 0x79, 0x6d, 0x65, - 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x6c, 0x6e, 0x72, - 0x70, 0x63, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x41, 0x6c, 0x6c, 0x50, 0x61, 0x79, 0x6d, - 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x40, 0x0a, 0x0d, - 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x47, 0x72, 0x61, 0x70, 0x68, 0x12, 0x1a, 0x2e, - 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x47, 0x72, 0x61, - 0x70, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x13, 0x2e, 0x6c, 0x6e, 0x72, 0x70, - 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x47, 0x72, 0x61, 0x70, 0x68, 0x12, 0x47, - 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, - 0x12, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x4d, 0x65, 0x74, - 0x72, 0x69, 0x63, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x6c, 0x6e, - 0x72, 0x70, 0x63, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x39, 0x0a, 0x0b, 0x47, 0x65, 0x74, 0x43, 0x68, - 0x61, 0x6e, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x16, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, - 0x68, 0x61, 0x6e, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x12, - 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x45, 0x64, - 0x67, 0x65, 0x12, 0x36, 0x0a, 0x0b, 0x47, 0x65, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, - 0x6f, 0x12, 0x16, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x6e, - 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x0f, 0x2e, 0x6c, 0x6e, 0x72, 0x70, - 0x63, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x44, 0x0a, 0x0b, 0x51, 0x75, - 0x65, 0x72, 0x79, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x12, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, - 0x63, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x51, 0x75, 0x65, - 0x72, 0x79, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x12, 0x3f, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x49, 0x6e, - 0x66, 0x6f, 0x12, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4e, 0x65, 0x74, 0x77, 0x6f, - 0x72, 0x6b, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x12, 0x2e, - 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x49, 0x6e, 0x66, - 0x6f, 0x12, 0x35, 0x0a, 0x0a, 0x53, 0x74, 0x6f, 0x70, 0x44, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x12, - 0x12, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x74, 0x6f, 0x70, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x13, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x74, 0x6f, 0x70, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x57, 0x0a, 0x15, 0x53, 0x75, 0x62, 0x73, - 0x63, 0x72, 0x69, 0x62, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x47, 0x72, 0x61, 0x70, - 0x68, 0x12, 0x20, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x72, 0x61, 0x70, 0x68, 0x54, - 0x6f, 0x70, 0x6f, 0x6c, 0x6f, 0x67, 0x79, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, - 0x69, 0x6f, 0x6e, 0x1a, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x72, 0x61, 0x70, - 0x68, 0x54, 0x6f, 0x70, 0x6f, 0x6c, 0x6f, 0x67, 0x79, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x30, - 0x01, 0x12, 0x41, 0x0a, 0x0a, 0x44, 0x65, 0x62, 0x75, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x12, - 0x18, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x44, 0x65, 0x62, 0x75, 0x67, 0x4c, 0x65, 0x76, - 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, - 0x63, 0x2e, 0x44, 0x65, 0x62, 0x75, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3e, 0x0a, 0x09, 0x46, 0x65, 0x65, 0x52, 0x65, 0x70, 0x6f, 0x72, - 0x74, 0x12, 0x17, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x65, 0x65, 0x52, 0x65, 0x70, - 0x6f, 0x72, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x6c, 0x6e, 0x72, - 0x70, 0x63, 0x2e, 0x46, 0x65, 0x65, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4e, 0x0a, 0x13, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x43, 0x68, - 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x1a, 0x2e, 0x6c, 0x6e, - 0x72, 0x70, 0x63, 0x2e, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, - 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x56, 0x0a, 0x11, 0x46, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x69, - 0x6e, 0x67, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x12, 0x1f, 0x2e, 0x6c, 0x6e, 0x72, 0x70, - 0x63, 0x2e, 0x46, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x48, 0x69, 0x73, 0x74, - 0x6f, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x6c, 0x6e, 0x72, - 0x70, 0x63, 0x2e, 0x46, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x48, 0x69, 0x73, - 0x74, 0x6f, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4e, 0x0a, 0x13, - 0x45, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x42, 0x61, 0x63, - 0x6b, 0x75, 0x70, 0x12, 0x21, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x45, 0x78, 0x70, 0x6f, - 0x72, 0x74, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x14, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, - 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x12, 0x54, 0x0a, 0x17, - 0x45, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x41, 0x6c, 0x6c, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, - 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x73, 0x12, 0x1e, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, - 0x43, 0x68, 0x61, 0x6e, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x45, 0x78, 0x70, 0x6f, 0x72, 0x74, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, - 0x43, 0x68, 0x61, 0x6e, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, - 0x6f, 0x74, 0x12, 0x4e, 0x0a, 0x10, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x43, 0x68, 0x61, 0x6e, - 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x12, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, - 0x68, 0x61, 0x6e, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, - 0x74, 0x1a, 0x1f, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, - 0x43, 0x68, 0x61, 0x6e, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x56, 0x0a, 0x15, 0x52, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x43, 0x68, 0x61, - 0x6e, 0x6e, 0x65, 0x6c, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x73, 0x12, 0x1f, 0x2e, 0x6c, 0x6e, - 0x72, 0x70, 0x63, 0x2e, 0x52, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x42, - 0x61, 0x63, 0x6b, 0x75, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x6c, - 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x42, 0x61, 0x63, 0x6b, - 0x75, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x58, 0x0a, 0x17, 0x53, 0x75, - 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x42, 0x61, - 0x63, 0x6b, 0x75, 0x70, 0x73, 0x12, 0x20, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, - 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x53, 0x75, 0x62, 0x73, 0x63, - 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x1a, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, - 0x43, 0x68, 0x61, 0x6e, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, - 0x6f, 0x74, 0x30, 0x01, 0x12, 0x47, 0x0a, 0x0c, 0x42, 0x61, 0x6b, 0x65, 0x4d, 0x61, 0x63, 0x61, - 0x72, 0x6f, 0x6f, 0x6e, 0x12, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x42, 0x61, 0x6b, - 0x65, 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x1b, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x42, 0x61, 0x6b, 0x65, 0x4d, 0x61, 0x63, - 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x50, 0x0a, - 0x0f, 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x49, 0x44, 0x73, - 0x12, 0x1d, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x61, 0x63, - 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x49, 0x44, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x1e, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x61, 0x63, 0x61, - 0x72, 0x6f, 0x6f, 0x6e, 0x49, 0x44, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, - 0x53, 0x0a, 0x10, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, - 0x6e, 0x49, 0x44, 0x12, 0x1e, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x44, 0x65, 0x6c, 0x65, - 0x74, 0x65, 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x49, 0x44, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x44, 0x65, 0x6c, 0x65, - 0x74, 0x65, 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x49, 0x44, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x50, 0x0a, 0x0f, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x65, 0x72, 0x6d, - 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x1d, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, - 0x4c, 0x69, 0x73, 0x74, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4c, - 0x69, 0x73, 0x74, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x53, 0x0a, 0x18, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x4d, - 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, - 0x6e, 0x73, 0x12, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x65, 0x63, 0x6b, - 0x4d, 0x61, 0x63, 0x50, 0x65, 0x72, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, + 0x4c, 0x69, 0x73, 0x74, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4a, 0x0a, 0x0d, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x50, 0x61, + 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x1b, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x44, 0x65, + 0x6c, 0x65, 0x74, 0x65, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, + 0x65, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x56, 0x0a, 0x11, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x41, 0x6c, 0x6c, 0x50, 0x61, 0x79, + 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x1f, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x44, 0x65, + 0x6c, 0x65, 0x74, 0x65, 0x41, 0x6c, 0x6c, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x44, + 0x65, 0x6c, 0x65, 0x74, 0x65, 0x41, 0x6c, 0x6c, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x73, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x40, 0x0a, 0x0d, 0x44, 0x65, 0x73, 0x63, + 0x72, 0x69, 0x62, 0x65, 0x47, 0x72, 0x61, 0x70, 0x68, 0x12, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, + 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x47, 0x72, 0x61, 0x70, 0x68, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x13, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, + 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x47, 0x72, 0x61, 0x70, 0x68, 0x12, 0x47, 0x0a, 0x0e, 0x47, 0x65, + 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x12, 0x19, 0x2e, 0x6c, + 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, + 0x4e, 0x6f, 0x64, 0x65, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x39, 0x0a, 0x0b, 0x47, 0x65, 0x74, 0x43, 0x68, 0x61, 0x6e, 0x49, 0x6e, + 0x66, 0x6f, 0x12, 0x16, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x49, + 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x12, 0x2e, 0x6c, 0x6e, 0x72, + 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x45, 0x64, 0x67, 0x65, 0x12, 0x36, + 0x0a, 0x0b, 0x47, 0x65, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x16, 0x2e, + 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x0f, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4e, 0x6f, + 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x44, 0x0a, 0x0b, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, + 0x6f, 0x75, 0x74, 0x65, 0x73, 0x12, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x51, 0x75, + 0x65, 0x72, 0x79, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x6f, + 0x75, 0x74, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3f, 0x0a, 0x0e, + 0x47, 0x65, 0x74, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x19, + 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x49, 0x6e, + 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x12, 0x2e, 0x6c, 0x6e, 0x72, 0x70, + 0x63, 0x2e, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x35, 0x0a, + 0x0a, 0x53, 0x74, 0x6f, 0x70, 0x44, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x12, 0x12, 0x2e, 0x6c, 0x6e, + 0x72, 0x70, 0x63, 0x2e, 0x53, 0x74, 0x6f, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x13, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x74, 0x6f, 0x70, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x57, 0x0a, 0x15, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, + 0x65, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x47, 0x72, 0x61, 0x70, 0x68, 0x12, 0x20, 0x2e, + 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x72, 0x61, 0x70, 0x68, 0x54, 0x6f, 0x70, 0x6f, 0x6c, + 0x6f, 0x67, 0x79, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x1a, + 0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x72, 0x61, 0x70, 0x68, 0x54, 0x6f, 0x70, + 0x6f, 0x6c, 0x6f, 0x67, 0x79, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x30, 0x01, 0x12, 0x41, 0x0a, + 0x0a, 0x44, 0x65, 0x62, 0x75, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x18, 0x2e, 0x6c, 0x6e, + 0x72, 0x70, 0x63, 0x2e, 0x44, 0x65, 0x62, 0x75, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x44, 0x65, + 0x62, 0x75, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x3e, 0x0a, 0x09, 0x46, 0x65, 0x65, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x17, 0x2e, + 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x65, 0x65, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x46, + 0x65, 0x65, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x4e, 0x0a, 0x13, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, + 0x6c, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, + 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x6f, 0x6c, 0x69, + 0x63, 0x79, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x56, 0x0a, 0x11, 0x46, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x48, 0x69, + 0x73, 0x74, 0x6f, 0x72, 0x79, 0x12, 0x1f, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x6f, + 0x72, 0x77, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x46, + 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4e, 0x0a, 0x13, 0x45, 0x78, 0x70, 0x6f, + 0x72, 0x74, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x12, + 0x21, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x45, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x43, 0x68, + 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x14, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, + 0x65, 0x6c, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x12, 0x54, 0x0a, 0x17, 0x45, 0x78, 0x70, 0x6f, + 0x72, 0x74, 0x41, 0x6c, 0x6c, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x42, 0x61, 0x63, 0x6b, + 0x75, 0x70, 0x73, 0x12, 0x1e, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, + 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x45, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, + 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x12, 0x4e, + 0x0a, 0x10, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x43, 0x68, 0x61, 0x6e, 0x42, 0x61, 0x63, 0x6b, + 0x75, 0x70, 0x12, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x42, + 0x61, 0x63, 0x6b, 0x75, 0x70, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x1a, 0x1f, 0x2e, + 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x43, 0x68, 0x61, 0x6e, + 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x56, + 0x0a, 0x15, 0x52, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, + 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x73, 0x12, 0x1f, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, + 0x52, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x42, 0x61, 0x63, 0x6b, 0x75, + 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, + 0x2e, 0x52, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x58, 0x0a, 0x17, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, + 0x69, 0x62, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, + 0x73, 0x12, 0x20, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, + 0x6c, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, + 0x69, 0x6f, 0x6e, 0x1a, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, + 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x30, 0x01, + 0x12, 0x47, 0x0a, 0x0c, 0x42, 0x61, 0x6b, 0x65, 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, + 0x12, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x42, 0x61, 0x6b, 0x65, 0x4d, 0x61, 0x63, + 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x6c, + 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x42, 0x61, 0x6b, 0x65, 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, + 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x50, 0x0a, 0x0f, 0x4c, 0x69, 0x73, + 0x74, 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x49, 0x44, 0x73, 0x12, 0x1d, 0x2e, 0x6c, + 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, + 0x6e, 0x49, 0x44, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x6c, 0x6e, + 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, + 0x49, 0x44, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x53, 0x0a, 0x10, 0x44, + 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4d, 0x61, 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x49, 0x44, 0x12, + 0x1e, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4d, 0x61, + 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x49, 0x44, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x1f, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4d, 0x61, + 0x63, 0x61, 0x72, 0x6f, 0x6f, 0x6e, 0x49, 0x44, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x50, 0x0a, 0x0f, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, + 0x6f, 0x6e, 0x73, 0x12, 0x1d, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, + 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x50, + 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x53, 0x0a, 0x18, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x4d, 0x61, 0x63, 0x61, 0x72, + 0x6f, 0x6f, 0x6e, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x4d, 0x61, 0x63, 0x50, - 0x65, 0x72, 0x6d, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x56, 0x0a, 0x15, 0x52, - 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x52, 0x50, 0x43, 0x4d, 0x69, 0x64, 0x64, 0x6c, 0x65, - 0x77, 0x61, 0x72, 0x65, 0x12, 0x1c, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x50, 0x43, - 0x4d, 0x69, 0x64, 0x64, 0x6c, 0x65, 0x77, 0x61, 0x72, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x1a, 0x1b, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x50, 0x43, 0x4d, 0x69, - 0x64, 0x64, 0x6c, 0x65, 0x77, 0x61, 0x72, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x28, - 0x01, 0x30, 0x01, 0x12, 0x56, 0x0a, 0x11, 0x53, 0x65, 0x6e, 0x64, 0x43, 0x75, 0x73, 0x74, 0x6f, - 0x6d, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x1f, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, - 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x4d, 0x65, 0x73, 0x73, 0x61, - 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x6c, 0x6e, 0x72, 0x70, - 0x63, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x4d, 0x65, 0x73, 0x73, - 0x61, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x58, 0x0a, 0x17, 0x53, - 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x4d, 0x65, - 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x12, 0x25, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, - 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x4d, 0x65, - 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x14, 0x2e, - 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x4d, 0x65, 0x73, 0x73, - 0x61, 0x67, 0x65, 0x30, 0x01, 0x12, 0x44, 0x0a, 0x0b, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x6c, 0x69, - 0x61, 0x73, 0x65, 0x73, 0x12, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, - 0x74, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x1a, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x6c, 0x69, 0x61, - 0x73, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5f, 0x0a, 0x14, 0x4c, - 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x48, 0x74, 0x6c, 0x63, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, - 0x69, 0x6f, 0x6e, 0x12, 0x22, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x6f, 0x6f, 0x6b, + 0x65, 0x72, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x6c, 0x6e, 0x72, + 0x70, 0x63, 0x2e, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x4d, 0x61, 0x63, 0x50, 0x65, 0x72, 0x6d, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x56, 0x0a, 0x15, 0x52, 0x65, 0x67, 0x69, 0x73, + 0x74, 0x65, 0x72, 0x52, 0x50, 0x43, 0x4d, 0x69, 0x64, 0x64, 0x6c, 0x65, 0x77, 0x61, 0x72, 0x65, + 0x12, 0x1c, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x50, 0x43, 0x4d, 0x69, 0x64, 0x64, + 0x6c, 0x65, 0x77, 0x61, 0x72, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x1a, 0x1b, + 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x50, 0x43, 0x4d, 0x69, 0x64, 0x64, 0x6c, 0x65, + 0x77, 0x61, 0x72, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x28, 0x01, 0x30, 0x01, 0x12, + 0x56, 0x0a, 0x11, 0x53, 0x65, 0x6e, 0x64, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x4d, 0x65, 0x73, + 0x73, 0x61, 0x67, 0x65, 0x12, 0x1f, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x6e, + 0x64, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, + 0x6e, 0x64, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x58, 0x0a, 0x17, 0x53, 0x75, 0x62, 0x73, 0x63, + 0x72, 0x69, 0x62, 0x65, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, + 0x65, 0x73, 0x12, 0x25, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x75, 0x62, 0x73, 0x63, + 0x72, 0x69, 0x62, 0x65, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, + 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x14, 0x2e, 0x6c, 0x6e, 0x72, 0x70, + 0x63, 0x2e, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x30, + 0x01, 0x12, 0x44, 0x0a, 0x0b, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x65, 0x73, + 0x12, 0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x6c, 0x69, + 0x61, 0x73, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x6c, 0x6e, + 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x65, 0x73, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5f, 0x0a, 0x14, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, + 0x70, 0x48, 0x74, 0x6c, 0x63, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x12, + 0x22, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x48, 0x74, + 0x6c, 0x63, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x48, 0x74, 0x6c, 0x63, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, - 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x48, 0x74, 0x6c, 0x63, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, - 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x27, 0x5a, 0x25, - 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6c, 0x69, 0x67, 0x68, 0x74, - 0x6e, 0x69, 0x6e, 0x67, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x2f, 0x6c, 0x6e, 0x64, 0x2f, - 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x27, 0x5a, 0x25, 0x67, 0x69, 0x74, 0x68, + 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x6e, 0x69, 0x6e, 0x67, + 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x2f, 0x6c, 0x6e, 0x64, 0x2f, 0x6c, 0x6e, 0x72, 0x70, + 0x63, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/lnrpc/lightning.proto b/lnrpc/lightning.proto index 1d481650e5..6449305159 100644 --- a/lnrpc/lightning.proto +++ b/lnrpc/lightning.proto @@ -1388,8 +1388,14 @@ enum CommitmentType { A channel that uses musig2 for the funding output, and the new tapscript features where relevant. */ - // TODO(roasbeef): need script enforce mirror type for the above as well? SIMPLE_TAPROOT = 5; + + /* + Identical to the SIMPLE_TAPROOT channel type, but with extra functionality. + This channel type also commits to additional meta data in the tapscript + leaves for the scripts in a channel. + */ + SIMPLE_TAPROOT_OVERLAY = 6; } message ChannelConstraints { diff --git a/lnrpc/lightning.swagger.json b/lnrpc/lightning.swagger.json index db3ec2cb36..5531d1ebb0 100644 --- a/lnrpc/lightning.swagger.json +++ b/lnrpc/lightning.swagger.json @@ -4568,10 +4568,11 @@ "STATIC_REMOTE_KEY", "ANCHORS", "SCRIPT_ENFORCED_LEASE", - "SIMPLE_TAPROOT" + "SIMPLE_TAPROOT", + "SIMPLE_TAPROOT_OVERLAY" ], "default": "UNKNOWN_COMMITMENT_TYPE", - "title": "- UNKNOWN_COMMITMENT_TYPE: Returned when the commitment type isn't known or unavailable.\n - LEGACY: A channel using the legacy commitment format having tweaked to_remote\nkeys.\n - STATIC_REMOTE_KEY: A channel that uses the modern commitment format where the key in the\noutput of the remote party does not change each state. This makes back\nup and recovery easier as when the channel is closed, the funds go\ndirectly to that key.\n - ANCHORS: A channel that uses a commitment format that has anchor outputs on the\ncommitments, allowing fee bumping after a force close transaction has\nbeen broadcast.\n - SCRIPT_ENFORCED_LEASE: A channel that uses a commitment type that builds upon the anchors\ncommitment format, but in addition requires a CLTV clause to spend outputs\npaying to the channel initiator. This is intended for use on leased channels\nto guarantee that the channel initiator has no incentives to close a leased\nchannel before its maturity date.\n - SIMPLE_TAPROOT: TODO(roasbeef): need script enforce mirror type for the above as well?" + "description": " - UNKNOWN_COMMITMENT_TYPE: Returned when the commitment type isn't known or unavailable.\n - LEGACY: A channel using the legacy commitment format having tweaked to_remote\nkeys.\n - STATIC_REMOTE_KEY: A channel that uses the modern commitment format where the key in the\noutput of the remote party does not change each state. This makes back\nup and recovery easier as when the channel is closed, the funds go\ndirectly to that key.\n - ANCHORS: A channel that uses a commitment format that has anchor outputs on the\ncommitments, allowing fee bumping after a force close transaction has\nbeen broadcast.\n - SCRIPT_ENFORCED_LEASE: A channel that uses a commitment type that builds upon the anchors\ncommitment format, but in addition requires a CLTV clause to spend outputs\npaying to the channel initiator. This is intended for use on leased channels\nto guarantee that the channel initiator has no incentives to close a leased\nchannel before its maturity date.\n - SIMPLE_TAPROOT: A channel that uses musig2 for the funding output, and the new tapscript\nfeatures where relevant.\n - SIMPLE_TAPROOT_OVERLAY: Identical to the SIMPLE_TAPROOT channel type, but with extra functionality.\nThis channel type also commits to additional meta data in the tapscript\nleaves for the scripts in a channel." }, "lnrpcConnectPeerRequest": { "type": "object", From 3031f21182602fb7c60e229df831793d2f9a6969 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Tue, 25 Jun 2024 15:32:05 -0700 Subject: [PATCH 164/218] rpc+funding: add taproot overlay as RPC chan type --- funding/manager_test.go | 5 +++-- rpcserver.go | 27 ++++++++++++++++++++++ rpcserver_test.go | 50 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 80 insertions(+), 2 deletions(-) diff --git a/funding/manager_test.go b/funding/manager_test.go index 898d2c3629..633b3b89ae 100644 --- a/funding/manager_test.go +++ b/funding/manager_test.go @@ -4653,8 +4653,8 @@ func testZeroConf(t *testing.T, chanType *lnwire.ChannelType) { // opening behavior with a specified fundmax flag. To give a hypothetical // example, if ANCHOR types had been introduced after the fundmax flag had been // activated, the developer would have had to code for the anchor reserve in the -// funding manager in the context of public and private channels. Otherwise -// inconsistent bahvior would have resulted when specifying fundmax for +// funding manager in the context of public and private channels. Otherwise, +// inconsistent behavior would have resulted when specifying fundmax for // different types of channel openings. // To ensure consistency this test compares a map of locally defined channel // commitment types to the list of channel types that are defined in the proto @@ -4670,6 +4670,7 @@ func TestCommitmentTypeFundmaxSanityCheck(t *testing.T) { "ANCHORS": 3, "SCRIPT_ENFORCED_LEASE": 4, "SIMPLE_TAPROOT": 5, + "SIMPLE_TAPROOT_OVERLAY": 6, } for commitmentType := range lnrpc.CommitmentType_value { diff --git a/rpcserver.go b/rpcserver.go index ea60380eff..b042e79e37 100644 --- a/rpcserver.go +++ b/rpcserver.go @@ -2309,6 +2309,29 @@ func (r *rpcServer) parseOpenChannelReq(in *lnrpc.OpenChannelRequest, *channelType = lnwire.ChannelType(*fv) + case lnrpc.CommitmentType_SIMPLE_TAPROOT_OVERLAY: + // If the taproot overlay channel type is being set, then the + // channel MUST be private. + if !in.Private { + return nil, fmt.Errorf("taproot overlay channels " + + "must be private") + } + + channelType = new(lnwire.ChannelType) + fv := lnwire.NewRawFeatureVector( + lnwire.SimpleTaprootOverlayChansRequired, + ) + + if in.ZeroConf { + fv.Set(lnwire.ZeroConfRequired) + } + + if in.ScidAlias { + fv.Set(lnwire.ScidAliasRequired) + } + + *channelType = lnwire.ChannelType(*fv) + default: return nil, fmt.Errorf("unhandled request channel type %v", in.CommitmentType) @@ -4540,6 +4563,9 @@ func rpcCommitmentType(chanType channeldb.ChannelType) lnrpc.CommitmentType { // first check whether it has anchors, since in that case it would also // be tweakless. switch { + case chanType.HasTapscriptRoot(): + return lnrpc.CommitmentType_SIMPLE_TAPROOT_OVERLAY + case chanType.IsTaproot(): return lnrpc.CommitmentType_SIMPLE_TAPROOT @@ -4551,6 +4577,7 @@ func rpcCommitmentType(chanType channeldb.ChannelType) lnrpc.CommitmentType { case chanType.IsTweakless(): return lnrpc.CommitmentType_STATIC_REMOTE_KEY + default: return lnrpc.CommitmentType_LEGACY diff --git a/rpcserver_test.go b/rpcserver_test.go index 9dd3c3f86f..53ec6d0ac3 100644 --- a/rpcserver_test.go +++ b/rpcserver_test.go @@ -77,3 +77,53 @@ func TestAuxDataParser(t *testing.T) { require.NotNil(t, resp) require.Equal(t, []byte{0x00, 0x00}, resp.CustomChannelData) } + +// TestRpcCommitmentType tests the rpcCommitmentType returns the corect +// commitment type given a channel type. +func TestRpcCommitmentType(t *testing.T) { + tests := []struct { + name string + chanType channeldb.ChannelType + want lnrpc.CommitmentType + }{ + { + name: "tapscript overlay", + chanType: channeldb.SimpleTaprootFeatureBit | + channeldb.TapscriptRootBit, + want: lnrpc.CommitmentType_SIMPLE_TAPROOT_OVERLAY, + }, + { + name: "simple taproot", + chanType: channeldb.SimpleTaprootFeatureBit, + want: lnrpc.CommitmentType_SIMPLE_TAPROOT, + }, + { + name: "lease expiration", + chanType: channeldb.LeaseExpirationBit, + want: lnrpc.CommitmentType_SCRIPT_ENFORCED_LEASE, + }, + { + name: "anchors", + chanType: channeldb.AnchorOutputsBit, + want: lnrpc.CommitmentType_ANCHORS, + }, + { + name: "tweakless", + chanType: channeldb.SingleFunderTweaklessBit, + want: lnrpc.CommitmentType_STATIC_REMOTE_KEY, + }, + { + name: "legacy", + chanType: channeldb.SingleFunderBit, + want: lnrpc.CommitmentType_LEGACY, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + require.Equal( + t, tt.want, rpcCommitmentType(tt.chanType), + ) + }) + } +} From 661964608880b3a3a2e00da904bb9bba8e1575cf Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Wed, 26 Jun 2024 17:27:57 -0700 Subject: [PATCH 165/218] lncfg: add new config option for taproot overlay chans --- lncfg/protocol.go | 4 ++++ lncfg/protocol_integration.go | 4 ++++ sample-lnd.conf | 3 +++ 3 files changed, 11 insertions(+) diff --git a/lncfg/protocol.go b/lncfg/protocol.go index d86613188a..c670b18947 100644 --- a/lncfg/protocol.go +++ b/lncfg/protocol.go @@ -31,6 +31,10 @@ type ProtocolOptions struct { // experimental simple taproot chans commitment type. TaprootChans bool `long:"simple-taproot-chans" description:"if set, then lnd will create and accept requests for channels using the simple taproot commitment type"` + // TaprootOverlayChans should be set if we want to enable support for + // the experimental taproot overlay chan type. + TaprootOverlayChans bool `long:"simple-taproot-overlay-chans" description:"if set, then lnd will create and accept requests for channels using the taproot overlay commitment type"` + // NoAnchors should be set if we don't want to support opening or accepting // channels having the anchor commitment type. NoAnchors bool `long:"no-anchors" description:"disable support for anchor commitments"` diff --git a/lncfg/protocol_integration.go b/lncfg/protocol_integration.go index e568b6b9aa..5c5150a0ef 100644 --- a/lncfg/protocol_integration.go +++ b/lncfg/protocol_integration.go @@ -33,6 +33,10 @@ type ProtocolOptions struct { // experimental simple taproot chans commitment type. TaprootChans bool `long:"simple-taproot-chans" description:"if set, then lnd will create and accept requests for channels using the simple taproot commitment type"` + // TaprootOverlayChans should be set if we want to enable support for + // the experimental taproot overlay chan type. + TaprootOverlayChans bool `long:"simple-taproot-overlay-chans" description:"if set, then lnd will create and accept requests for channels using the taproot overlay commitment type"` + // Anchors enables anchor commitments. // TODO(halseth): transition itests to anchors instead! Anchors bool `long:"anchors" description:"enable support for anchor commitments"` diff --git a/sample-lnd.conf b/sample-lnd.conf index c9fb0e4f8c..865b5310fd 100644 --- a/sample-lnd.conf +++ b/sample-lnd.conf @@ -1321,6 +1321,9 @@ ; Set to enable support for the experimental taproot channel type. ; protocol.simple-taproot-chans=false +; Set to enable support for the experimental taproot overlay channel type. +; protocol.simple-taproot-overlay-chans=false + ; Set to disable blinded route forwarding. ; protocol.no-route-blinding=false From 9f048b0ea32a93499c853add8c3a281a5b562942 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Tue, 25 Jun 2024 15:32:21 -0700 Subject: [PATCH 166/218] lnd: signal taproot overlay chans based on config We also add a sanity check to make sure they can't be signaled without the aux interfaces. --- server.go | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/server.go b/server.go index ce9f9dd401..3dd8040e0a 100644 --- a/server.go +++ b/server.go @@ -547,6 +547,15 @@ func newServer(cfg *Config, listenAddrs []net.Addr, readBufferPool, cfg.Workers.Read, pool.DefaultWorkerTimeout, ) + // If the taproot overlay flag is set, but we don't have an aux funding + // controller, then we'll exit as this is incompatible. + if cfg.ProtocolOptions.TaprootOverlayChans && + implCfg.AuxFundingController.IsNone() { + + return nil, fmt.Errorf("taproot overlay flag set, but not " + + "aux controllers") + } + //nolint:lll featureMgr, err := feature.NewManager(feature.Config{ NoTLVOnion: cfg.ProtocolOptions.LegacyOnion(), @@ -560,6 +569,7 @@ func newServer(cfg *Config, listenAddrs []net.Addr, NoAnySegwit: cfg.ProtocolOptions.NoAnySegwit(), CustomFeatures: cfg.ProtocolOptions.CustomFeatures(), NoTaprootChans: !cfg.ProtocolOptions.TaprootChans, + NoTaprootOverlay: !cfg.ProtocolOptions.TaprootOverlayChans, NoRouteBlinding: cfg.ProtocolOptions.NoRouteBlinding(), }) if err != nil { From 00ed751ee53ffa85b92bfb1bc231738e76b93ccc Mon Sep 17 00:00:00 2001 From: George Tsagkarelis Date: Wed, 13 Mar 2024 21:01:34 +0100 Subject: [PATCH 167/218] docs: update release notes --- docs/release-notes/release-notes-0.18.4.md | 49 ++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/docs/release-notes/release-notes-0.18.4.md b/docs/release-notes/release-notes-0.18.4.md index 74d0198281..727aff9807 100644 --- a/docs/release-notes/release-notes-0.18.4.md +++ b/docs/release-notes/release-notes-0.18.4.md @@ -24,9 +24,52 @@ does not contain a payment address. # New Features + +The main channel state machine and database now allow for processing and storing +custom Taproot script leaves, [allowing the implementation of custom channel +types](https://github.com/lightningnetwork/lnd/pull/8960). + ## Functional Enhancements + +* A new `protocol.simple-taproot-overlay-chans` configuration item/CLI flag was + added [to turn on custom channel + functionality](https://github.com/lightningnetwork/lnd/pull/8960). + ## RPC Additions +* Some new experimental [RPCs for managing SCID + aliases](https://github.com/lightningnetwork/lnd/pull/8960) were added under + the `routerrpc` package. These methods allow manually adding and deleting SCID + aliases locally to your node. + > NOTE: these new RPC methods are marked as experimental + (`XAddLocalChanAliases` & `XDeleteLocalChanAliases`) and upon calling + them the aliases will not be communicated with the channel peer. + +* The responses for the `ListChannels`, `PendingChannels` and `ChannelBalance` + RPCs now include [a new `custom_channel_data` field that is only set for + custom channels](https://github.com/lightningnetwork/lnd/pull/8960). + +* The `routerrpc.SendPaymentV2` RPC has a new field [`first_hop_custom_records` + that allows the user to send custom p2p wire message TLV types to the first + hop of a payment](https://github.com/lightningnetwork/lnd/pull/8960). + That new field is also exposed in the `routerrpc.HtlcInterceptor`, so it can + be read and interpreted by external software. + +* The `routerrpc.HtlcInterceptor` now [allows some values of the HTLC to be + modified before they're validated by the state + machine](https://github.com/lightningnetwork/lnd/pull/8960). The fields that + can be modified are `outgoing_amount_msat` (if transported overlaid value of + HTLC doesn't match the actual BTC amount being transferred) and + `outgoing_htlc_wire_custom_records` (allow adding custom TLV values to the + p2p wire message of the forwarded HTLC). + +* A new [`invoicesrpc.HtlcModifier` RPC now allows incoming HTLCs that attempt + to satisfy an invoice to be modified before they're + validated](https://github.com/lightningnetwork/lnd/pull/8960). This allows + custom channels to determine what the actual (overlaid) value of an HTLC is, + even if that value doesn't match the actual BTC amount being transferred by + the HTLC. + ## lncli Additions # Improvements @@ -36,6 +79,7 @@ ## lncli Updates + ## Code Health ## Breaking Changes @@ -54,3 +98,8 @@ # Contributors (Alphabetical Order) * Elle Mouton +* ffranr +* George Tsagkarelis +* Olaoluwa Osuntokun +* Oliver Gugger + From 16edbf30260bf62023f3bce44bb0cd03360feafe Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Fri, 27 Sep 2024 17:07:53 +0900 Subject: [PATCH 168/218] sweep: ensure we factor in extra change addrs in MaxFeeRateAllowed --- sweep/fee_bumper.go | 45 +++++++++++++++++++++++++++++++++----- sweep/fee_bumper_test.go | 8 ++++--- sweep/tx_input_set_test.go | 2 +- sweep/txgenerator.go | 3 ++- 4 files changed, 48 insertions(+), 10 deletions(-) diff --git a/sweep/fee_bumper.go b/sweep/fee_bumper.go index b1e34c0bfe..fd3dfba9e1 100644 --- a/sweep/fee_bumper.go +++ b/sweep/fee_bumper.go @@ -20,6 +20,7 @@ import ( "github.com/lightningnetwork/lnd/lnutils" "github.com/lightningnetwork/lnd/lnwallet" "github.com/lightningnetwork/lnd/lnwallet/chainfee" + "github.com/lightningnetwork/lnd/tlv" ) var ( @@ -43,6 +44,19 @@ var ( ErrThirdPartySpent = errors.New("third party spent the output") ) +var ( + // dummyChangePkScript is a dummy tapscript change script that's used + // when we don't need a real address, just something that can be used + // for fee estimation. + dummyChangePkScript = []byte{ + 0x51, 0x20, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + } +) + // Bumper defines an interface that can be used by other subsystems for fee // bumping. type Bumper interface { @@ -129,12 +143,33 @@ type BumpRequest struct { // compares it with the specified MaxFeeRate, and returns the smaller of the // two. func (r *BumpRequest) MaxFeeRateAllowed() (chainfee.SatPerKWeight, error) { + // We'll want to know if we have any blobs, as we need to factor this + // into the max fee rate for this bump request. + hasBlobs := fn.Any(func(i input.Input) bool { + return fn.MapOptionZ( + i.ResolutionBlob(), func(b tlv.Blob) bool { + return len(b) > 0 + }, + ) + }, r.Inputs) + + sweepAddrs := [][]byte{ + r.DeliveryAddress.DeliveryAddress, + } + + // If we have blobs, then we'll add an extra sweep addr for the size + // estimate below. We know that these blobs will also always be based on + // p2tr addrs. + if hasBlobs { + // We need to pass in a real address, so we'll use a dummy + // tapscript change script that's used elsewhere for tests. + sweepAddrs = append(sweepAddrs, dummyChangePkScript) + } + // Get the size of the sweep tx, which will be used to calculate the // budget fee rate. - // - // TODO(roasbeef): also wants the extra change output? size, err := calcSweepTxWeight( - r.Inputs, r.DeliveryAddress.DeliveryAddress, + r.Inputs, sweepAddrs, ) if err != nil { return 0, err @@ -163,7 +198,7 @@ func (r *BumpRequest) MaxFeeRateAllowed() (chainfee.SatPerKWeight, error) { // calcSweepTxWeight calculates the weight of the sweep tx. It assumes a // sweeping tx always has a single output(change). func calcSweepTxWeight(inputs []input.Input, - outputPkScript []byte) (lntypes.WeightUnit, error) { + outputPkScript [][]byte) (lntypes.WeightUnit, error) { // Use a const fee rate as we only use the weight estimator to // calculate the size. @@ -177,7 +212,7 @@ func calcSweepTxWeight(inputs []input.Input, // TODO(yy): we should refactor the weight estimator to not require a // fee rate and max fee rate and make it a pure tx weight calculator. _, estimator, err := getWeightEstimate( - inputs, nil, feeRate, 0, [][]byte{outputPkScript}, + inputs, nil, feeRate, 0, outputPkScript, ) if err != nil { return 0, err diff --git a/sweep/fee_bumper_test.go b/sweep/fee_bumper_test.go index db9dd40457..53b38607f7 100644 --- a/sweep/fee_bumper_test.go +++ b/sweep/fee_bumper_test.go @@ -115,13 +115,15 @@ func TestCalcSweepTxWeight(t *testing.T) { inp := createTestInput(100, input.WitnessKeyHash) // Use a wrong change script to test the error case. - weight, err := calcSweepTxWeight([]input.Input{&inp}, []byte{0}) + weight, err := calcSweepTxWeight( + []input.Input{&inp}, [][]byte{{0x00}}, + ) require.Error(t, err) require.Zero(t, weight) // Use a correct change script to test the success case. weight, err = calcSweepTxWeight( - []input.Input{&inp}, changePkScript.DeliveryAddress, + []input.Input{&inp}, [][]byte{changePkScript.DeliveryAddress}, ) require.NoError(t, err) @@ -143,7 +145,7 @@ func TestBumpRequestMaxFeeRateAllowed(t *testing.T) { // The weight is 487. weight, err := calcSweepTxWeight( - []input.Input{&inp}, changePkScript.DeliveryAddress, + []input.Input{&inp}, [][]byte{changePkScript.DeliveryAddress}, ) require.NoError(t, err) diff --git a/sweep/tx_input_set_test.go b/sweep/tx_input_set_test.go index b774eedff2..8d0850b20d 100644 --- a/sweep/tx_input_set_test.go +++ b/sweep/tx_input_set_test.go @@ -92,7 +92,7 @@ func TestNewBudgetInputSet(t *testing.T) { rt.ErrorContains(err, "duplicate inputs") rt.Nil(set) - // Pass a slice of inputs that only one input has the deadline height, + // Pass a slice of inputs that only one input has the deadline height. set, err = NewBudgetInputSet( []SweeperInput{input0, input3}, testHeight, fn.None[AuxSweeper](), diff --git a/sweep/txgenerator.go b/sweep/txgenerator.go index 43fc802ba7..993ee9e59d 100644 --- a/sweep/txgenerator.go +++ b/sweep/txgenerator.go @@ -262,7 +262,8 @@ func getWeightEstimate(inputs []input.Input, outputs []*wire.TxOut, default: // Unknown script type. - return nil, nil, errors.New("unknown script type") + return nil, nil, fmt.Errorf("unknown script "+ + "type: %x", outputPkScript) } } From a5eca916024aea806b7e1849e8483032b9a1fa2e Mon Sep 17 00:00:00 2001 From: ziggie Date: Thu, 3 Oct 2024 16:52:57 +0200 Subject: [PATCH 169/218] multi: bump btcd version. The new SignCompact return values had to be adopted across the code base. --- .../migration_01_to_11/migration_11_invoices_test.go | 8 +------- go.mod | 4 ++-- go.sum | 8 ++++---- invoices/test_utils.go | 7 +------ invoices/test_utils_test.go | 8 ++------ keychain/btcwallet.go | 3 ++- keychain/signer.go | 3 ++- lnrpc/invoicesrpc/addinvoice.go | 2 +- lnrpc/walletrpc/walletkit_server.go | 5 +---- lntest/mock/secretkeyring.go | 3 ++- zpay32/invoice_test.go | 10 +++------- 11 files changed, 21 insertions(+), 40 deletions(-) diff --git a/channeldb/migration_01_to_11/migration_11_invoices_test.go b/channeldb/migration_01_to_11/migration_11_invoices_test.go index 553a8cfeee..55c188bd95 100644 --- a/channeldb/migration_01_to_11/migration_11_invoices_test.go +++ b/channeldb/migration_01_to_11/migration_11_invoices_test.go @@ -2,7 +2,6 @@ package migration_01_to_11 import ( "bytes" - "fmt" "testing" "time" @@ -154,12 +153,7 @@ func signDigestCompact(hash []byte) ([]byte, error) { privKey, _ := btcec.PrivKeyFromBytes(testPrivKeyBytes) // ecdsa.SignCompact returns a pubkey-recoverable signature - sig, err := ecdsa.SignCompact(privKey, hash, isCompressedKey) - if err != nil { - return nil, fmt.Errorf("can't sign the hash: %w", err) - } - - return sig, nil + return ecdsa.SignCompact(privKey, hash, isCompressedKey), nil } // getPayReq creates a payment request for the given net. diff --git a/go.mod b/go.mod index d1e78e485e..2726bd781c 100644 --- a/go.mod +++ b/go.mod @@ -4,8 +4,8 @@ require ( github.com/NebulousLabs/go-upnp v0.0.0-20180202185039-29b680b06c82 github.com/Yawning/aez v0.0.0-20211027044916-e49e68abd344 github.com/andybalholm/brotli v1.0.4 - github.com/btcsuite/btcd v0.24.2-beta.rc1.0.20240625142744-cc26860b4026 - github.com/btcsuite/btcd/btcec/v2 v2.3.3 + github.com/btcsuite/btcd v0.24.3-0.20240921052913-67b8efd3ba53 + github.com/btcsuite/btcd/btcec/v2 v2.3.4 github.com/btcsuite/btcd/btcutil v1.1.5 github.com/btcsuite/btcd/btcutil/psbt v1.1.8 github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0 diff --git a/go.sum b/go.sum index 4332b1032d..f0529546b0 100644 --- a/go.sum +++ b/go.sum @@ -73,12 +73,12 @@ github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6r github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ= github.com/btcsuite/btcd v0.22.0-beta.0.20220111032746-97732e52810c/go.mod h1:tjmYdS6MLJ5/s0Fj4DbLgSbDHbEqLJrtnHecBFkdz5M= github.com/btcsuite/btcd v0.23.5-0.20231215221805-96c9fd8078fd/go.mod h1:nm3Bko6zh6bWP60UxwoT5LzdGJsQJaPo6HjduXq9p6A= -github.com/btcsuite/btcd v0.24.2-beta.rc1.0.20240625142744-cc26860b4026 h1:s8/96vQSj05bqLl9RyM/eMX8gLtiayEj520TVE4YGy0= -github.com/btcsuite/btcd v0.24.2-beta.rc1.0.20240625142744-cc26860b4026/go.mod h1:5C8ChTkl5ejr3WHj8tkQSCmydiMEPB0ZhQhehpq7Dgg= +github.com/btcsuite/btcd v0.24.3-0.20240921052913-67b8efd3ba53 h1:XOZ/wRGHkKv0AqxfDks5IkzaQ1Ge6fq322ZOOG5VIkU= +github.com/btcsuite/btcd v0.24.3-0.20240921052913-67b8efd3ba53/go.mod h1:zHK7t7sw8XbsCkD64WePHE3r3k9/XoGAcf6mXV14c64= github.com/btcsuite/btcd/btcec/v2 v2.1.0/go.mod h1:2VzYrv4Gm4apmbVVsSq5bqf1Ec8v56E48Vt0Y/umPgA= github.com/btcsuite/btcd/btcec/v2 v2.1.3/go.mod h1:ctjw4H1kknNJmRN4iP1R7bTQ+v3GJkZBd6mui8ZsAZE= -github.com/btcsuite/btcd/btcec/v2 v2.3.3 h1:6+iXlDKE8RMtKsvK0gshlXIuPbyWM/h84Ensb7o3sC0= -github.com/btcsuite/btcd/btcec/v2 v2.3.3/go.mod h1:zYzJ8etWJQIv1Ogk7OzpWjowwOdXY1W/17j2MW85J04= +github.com/btcsuite/btcd/btcec/v2 v2.3.4 h1:3EJjcN70HCu/mwqlUsGK8GcNVyLVxFDlWurTXGPFfiQ= +github.com/btcsuite/btcd/btcec/v2 v2.3.4/go.mod h1:zYzJ8etWJQIv1Ogk7OzpWjowwOdXY1W/17j2MW85J04= github.com/btcsuite/btcd/btcutil v1.0.0/go.mod h1:Uoxwv0pqYWhD//tfTiipkxNfdhG9UrLwaeswfjfdF0A= github.com/btcsuite/btcd/btcutil v1.1.0/go.mod h1:5OapHB7A2hBBWLm48mmw4MOHNJCcUBTwmWH/0Jn8VHE= github.com/btcsuite/btcd/btcutil v1.1.5 h1:+wER79R5670vs/ZusMTF1yTcRYE5GUsFbdjdisflzM8= diff --git a/invoices/test_utils.go b/invoices/test_utils.go index c6e226083d..b21804346c 100644 --- a/invoices/test_utils.go +++ b/invoices/test_utils.go @@ -4,7 +4,6 @@ import ( "crypto/rand" "encoding/binary" "encoding/hex" - "fmt" "sync" "testing" "time" @@ -69,11 +68,7 @@ var ( testMessageSigner = zpay32.MessageSigner{ SignCompact: func(msg []byte) ([]byte, error) { hash := chainhash.HashB(msg) - sig, err := ecdsa.SignCompact(testPrivKey, hash, true) - if err != nil { - return nil, fmt.Errorf("can't sign the "+ - "message: %v", err) - } + sig := ecdsa.SignCompact(testPrivKey, hash, true) return sig, nil }, diff --git a/invoices/test_utils_test.go b/invoices/test_utils_test.go index fe69cad6f1..509b92d85c 100644 --- a/invoices/test_utils_test.go +++ b/invoices/test_utils_test.go @@ -122,12 +122,8 @@ var ( testMessageSigner = zpay32.MessageSigner{ SignCompact: func(msg []byte) ([]byte, error) { hash := chainhash.HashB(msg) - sig, err := ecdsa.SignCompact(testPrivKey, hash, true) - if err != nil { - return nil, fmt.Errorf("can't sign the "+ - "message: %v", err) - } - return sig, nil + + return ecdsa.SignCompact(testPrivKey, hash, true), nil }, } diff --git a/keychain/btcwallet.go b/keychain/btcwallet.go index 0df679df99..677de20da3 100644 --- a/keychain/btcwallet.go +++ b/keychain/btcwallet.go @@ -448,7 +448,8 @@ func (b *BtcWalletKeyRing) SignMessageCompact(keyLoc KeyLocator, } else { digest = chainhash.HashB(msg) } - return ecdsa.SignCompact(privKey, digest, true) + + return ecdsa.SignCompact(privKey, digest, true), nil } // SignMessageSchnorr uses the Schnorr signature algorithm to sign the given diff --git a/keychain/signer.go b/keychain/signer.go index 9605e72ec1..fa2b70762d 100644 --- a/keychain/signer.go +++ b/keychain/signer.go @@ -85,7 +85,8 @@ func (p *PrivKeyMessageSigner) SignMessageCompact(msg []byte, } else { digest = chainhash.HashB(msg) } - return ecdsa.SignCompact(p.privKey, digest, true) + + return ecdsa.SignCompact(p.privKey, digest, true), nil } var _ SingleKeyMessageSigner = (*PubKeyMessageSigner)(nil) diff --git a/lnrpc/invoicesrpc/addinvoice.go b/lnrpc/invoicesrpc/addinvoice.go index dcb1bef71e..df274f2da7 100644 --- a/lnrpc/invoicesrpc/addinvoice.go +++ b/lnrpc/invoicesrpc/addinvoice.go @@ -587,7 +587,7 @@ func AddInvoice(ctx context.Context, cfg *AddInvoiceConfig, return ecdsa.SignCompact( ephemKey, chainhash.HashB(msg), true, - ) + ), nil }, }) if err != nil { diff --git a/lnrpc/walletrpc/walletkit_server.go b/lnrpc/walletrpc/walletkit_server.go index e70a1a7712..e197a745d6 100644 --- a/lnrpc/walletrpc/walletkit_server.go +++ b/lnrpc/walletrpc/walletkit_server.go @@ -2493,10 +2493,7 @@ func (w *WalletKit) SignMessageWithAddr(_ context.Context, "fetched from wallet database: %w", err) } - sigBytes, err := ecdsa.SignCompact(privKey, digest, pubKey.Compressed()) - if err != nil { - return nil, fmt.Errorf("failed to create signature: %w", err) - } + sigBytes := ecdsa.SignCompact(privKey, digest, pubKey.Compressed()) // Bitcoin signatures are base64 encoded (being compatible with // bitcoin-core and btcd). diff --git a/lntest/mock/secretkeyring.go b/lntest/mock/secretkeyring.go index 56c0fac948..a5a39cc729 100644 --- a/lntest/mock/secretkeyring.go +++ b/lntest/mock/secretkeyring.go @@ -69,7 +69,8 @@ func (s *SecretKeyRing) SignMessageCompact(_ keychain.KeyLocator, } else { digest = chainhash.HashB(msg) } - return ecdsa.SignCompact(s.RootKey, digest, true) + + return ecdsa.SignCompact(s.RootKey, digest, true), nil } // SignMessageSchnorr signs the passed message and ignores the KeyDescriptor. diff --git a/zpay32/invoice_test.go b/zpay32/invoice_test.go index d982a14a2b..de59422aba 100644 --- a/zpay32/invoice_test.go +++ b/zpay32/invoice_test.go @@ -105,12 +105,8 @@ var ( testMessageSigner = MessageSigner{ SignCompact: func(msg []byte) ([]byte, error) { hash := chainhash.HashB(msg) - sig, err := ecdsa.SignCompact(testPrivKey, hash, true) - if err != nil { - return nil, fmt.Errorf("can't sign the "+ - "message: %v", err) - } - return sig, nil + + return ecdsa.SignCompact(testPrivKey, hash, true), nil }, } @@ -1043,7 +1039,7 @@ func TestInvoiceChecksumMalleability(t *testing.T) { msgSigner := MessageSigner{ SignCompact: func(msg []byte) ([]byte, error) { hash := chainhash.HashB(msg) - return ecdsa.SignCompact(privKey, hash, true) + return ecdsa.SignCompact(privKey, hash, true), nil }, } opts := []func(*Invoice){Description("test")} From 89c84f1da6ea2fab0ef249927698b97acaed2a82 Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Fri, 11 Oct 2024 14:46:09 +0200 Subject: [PATCH 170/218] lnwallet: fix import issue --- lnwallet/interface.go | 1 + 1 file changed, 1 insertion(+) diff --git a/lnwallet/interface.go b/lnwallet/interface.go index 0a27ece1ed..23475bcd4b 100644 --- a/lnwallet/interface.go +++ b/lnwallet/interface.go @@ -22,6 +22,7 @@ import ( "github.com/lightningnetwork/lnd/fn" "github.com/lightningnetwork/lnd/keychain" "github.com/lightningnetwork/lnd/lnwallet/chainfee" + "github.com/lightningnetwork/lnd/lnwire" ) const ( From ca3bde901eb86638256a72fd17b75ce5951d119f Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Fri, 11 Oct 2024 14:46:28 +0200 Subject: [PATCH 171/218] build: bump version to v0.18.4-beta.rc1 --- build/version.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build/version.go b/build/version.go index fe2486b8fe..ef0627529b 100644 --- a/build/version.go +++ b/build/version.go @@ -43,11 +43,11 @@ const ( AppMinor uint = 18 // AppPatch defines the application patch for this binary. - AppPatch uint = 3 + AppPatch uint = 4 // AppPreRelease MUST only contain characters from semanticAlphabet per // the semantic versioning spec. - AppPreRelease = "beta" + AppPreRelease = "beta.rc1" ) func init() { From 560b7286b560456bb175198dda3a138299988d25 Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Tue, 15 Oct 2024 09:56:32 +0200 Subject: [PATCH 172/218] rpcserver: don't write any custom channel data if empty --- rpcserver.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/rpcserver.go b/rpcserver.go index b042e79e37..d9fdb82bb2 100644 --- a/rpcserver.go +++ b/rpcserver.go @@ -4613,6 +4613,11 @@ func encodeCustomChanData(lnChan *channeldb.OpenChannel) ([]byte, error) { customOpenChanData := lnChan.CustomBlob.UnwrapOr(nil) customLocalCommitData := lnChan.LocalCommitment.CustomBlob.UnwrapOr(nil) + // Don't write any custom data if both blobs are empty. + if len(customOpenChanData) == 0 && len(customLocalCommitData) == 0 { + return nil, nil + } + // We'll encode our custom channel data as two blobs. The first is a // set of var bytes encoding of the open chan data, the second is an // encoding of the local commitment data. From 58bf721f2efdffa9876b7128fc5747ccdabba3e6 Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Tue, 15 Oct 2024 09:58:39 +0200 Subject: [PATCH 173/218] cmd/commands: don't error out on replacement failure --- cmd/commands/commands.go | 16 +++++++--------- cmd/commands/commands_test.go | 14 +++----------- 2 files changed, 10 insertions(+), 20 deletions(-) diff --git a/cmd/commands/commands.go b/cmd/commands/commands.go index 2c1191ab2b..06759990f1 100644 --- a/cmd/commands/commands.go +++ b/cmd/commands/commands.go @@ -54,10 +54,10 @@ var ( // replaceCustomData replaces the custom channel data hex string with the // decoded custom channel data in the JSON response. -func replaceCustomData(jsonBytes []byte) ([]byte, error) { +func replaceCustomData(jsonBytes []byte) []byte { // If there's nothing to replace, return the original JSON. if !customDataPattern.Match(jsonBytes) { - return jsonBytes, nil + return jsonBytes } replacedBytes := customDataPattern.ReplaceAllFunc( @@ -78,10 +78,12 @@ func replaceCustomData(jsonBytes []byte) ([]byte, error) { var buf bytes.Buffer err := json.Indent(&buf, replacedBytes, "", " ") if err != nil { - return nil, err + // If we can't indent the JSON, it likely means the replacement + // data wasn't correct, so we return the original JSON. + return jsonBytes } - return buf.Bytes(), nil + return buf.Bytes() } func getContext() context.Context { @@ -118,11 +120,7 @@ func printRespJSON(resp proto.Message) { return } - jsonBytesReplaced, err := replaceCustomData(jsonBytes) - if err != nil { - fmt.Println("unable to replace custom data: ", err) - jsonBytesReplaced = jsonBytes - } + jsonBytesReplaced := replaceCustomData(jsonBytes) fmt.Printf("%s\n", jsonBytesReplaced) } diff --git a/cmd/commands/commands_test.go b/cmd/commands/commands_test.go index bab2c8d2ba..cb9cbe0db8 100644 --- a/cmd/commands/commands_test.go +++ b/cmd/commands/commands_test.go @@ -131,7 +131,6 @@ func TestReplaceCustomData(t *testing.T) { data string replaceData string expected string - expectedErr string }{ { name: "no replacement necessary", @@ -175,7 +174,8 @@ func TestReplaceCustomData(t *testing.T) { name: "invalid json", data: "this ain't json, " + "\"custom_channel_data\":\"a\"", - expectedErr: "invalid character 'h' in literal true", + expected: "this ain't json, " + + "\"custom_channel_data\":\"a\"", }, { name: "valid json, invalid hex, just formatted", @@ -186,15 +186,7 @@ func TestReplaceCustomData(t *testing.T) { for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { - result, err := replaceCustomData([]byte(tc.data)) - - if tc.expectedErr != "" { - require.ErrorContains(t, err, tc.expectedErr) - return - } - - require.NoError(t, err) - + result := replaceCustomData([]byte(tc.data)) require.Equal(t, tc.expected, string(result)) }) } From fe8b89dc2c6829166b00bc9d6df14485d91d9d0f Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Mon, 2 Sep 2024 18:09:37 +0200 Subject: [PATCH 174/218] github+scripts: bump itests to bitcoind v28, allow testing with RCs --- .github/workflows/main.yml | 2 +- scripts/install_bitcoind.sh | 13 ++++++++++--- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index fe4a64d66a..0f101f866d 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -21,7 +21,7 @@ defaults: shell: bash env: - BITCOIN_VERSION: "27" + BITCOIN_VERSION: "28" TRANCHES: 8 diff --git a/scripts/install_bitcoind.sh b/scripts/install_bitcoind.sh index b4faa4becd..8f74efa46a 100755 --- a/scripts/install_bitcoind.sh +++ b/scripts/install_bitcoind.sh @@ -4,13 +4,20 @@ set -ev BITCOIND_VERSION=$1 +# Useful for testing RCs: e.g. TAG_SUFFIX=.0rc1, DIR_SUFFIX=.0rc1 +TAG_SUFFIX= +DIR_SUFFIX=.0 + +# Useful for testing against an image pushed to a different Docker repo. +REPO=lightninglabs/bitcoin-core + if [ -z "$BITCOIND_VERSION" ]; then echo "Must specify a version of bitcoind to install." echo "Usage: install_bitcoind.sh " exit 1 fi -docker pull lightninglabs/bitcoin-core:${BITCOIND_VERSION} -CONTAINER_ID=$(docker create lightninglabs/bitcoin-core:${BITCOIND_VERSION}) -sudo docker cp $CONTAINER_ID:/opt/bitcoin-${BITCOIND_VERSION}.0/bin/bitcoind /usr/local/bin/bitcoind +docker pull ${REPO}:${BITCOIND_VERSION}${TAG_SUFFIX} +CONTAINER_ID=$(docker create ${REPO}:${BITCOIND_VERSION}${TAG_SUFFIX}) +sudo docker cp $CONTAINER_ID:/opt/bitcoin-${BITCOIND_VERSION}${DIR_SUFFIX}/bin/bitcoind /usr/local/bin/bitcoind docker rm $CONTAINER_ID From 5966150db59bb42bcf5db0d5c0506a64b9e037c0 Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Tue, 3 Sep 2024 11:57:59 +0200 Subject: [PATCH 175/218] lntest: move debug flag, disable spammy libevent --- lntest/bitcoind.go | 1 - lntest/bitcoind_common.go | 2 ++ lntest/bitcoind_notxindex.go | 1 - lntest/bitcoind_rpcpolling.go | 1 - 4 files changed, 2 insertions(+), 3 deletions(-) diff --git a/lntest/bitcoind.go b/lntest/bitcoind.go index 37800169bb..6467164828 100644 --- a/lntest/bitcoind.go +++ b/lntest/bitcoind.go @@ -13,7 +13,6 @@ func NewBackend(miner string, netParams *chaincfg.Params) ( *BitcoindBackendConfig, func() error, error) { extraArgs := []string{ - "-debug", "-regtest", "-txindex", "-disablewallet", diff --git a/lntest/bitcoind_common.go b/lntest/bitcoind_common.go index 9cbcd23fde..ec26e6cc06 100644 --- a/lntest/bitcoind_common.go +++ b/lntest/bitcoind_common.go @@ -126,6 +126,8 @@ func newBackend(miner string, netParams *chaincfg.Params, extraArgs []string, fmt.Sprintf("-port=%d", p2pPort), "-zmqpubrawblock=" + zmqBlockAddr, "-zmqpubrawtx=" + zmqTxAddr, + "-debug", + "-debugexclude=libevent", "-debuglogfile=" + logFile, } cmdArgs = append(cmdArgs, extraArgs...) diff --git a/lntest/bitcoind_notxindex.go b/lntest/bitcoind_notxindex.go index de7959eba7..611b89f5ec 100644 --- a/lntest/bitcoind_notxindex.go +++ b/lntest/bitcoind_notxindex.go @@ -13,7 +13,6 @@ func NewBackend(miner string, netParams *chaincfg.Params) ( *BitcoindBackendConfig, func() error, error) { extraArgs := []string{ - "-debug", "-regtest", "-disablewallet", } diff --git a/lntest/bitcoind_rpcpolling.go b/lntest/bitcoind_rpcpolling.go index ca203ad2c2..1280e6f2f4 100644 --- a/lntest/bitcoind_rpcpolling.go +++ b/lntest/bitcoind_rpcpolling.go @@ -13,7 +13,6 @@ func NewBackend(miner string, netParams *chaincfg.Params) ( *BitcoindBackendConfig, func() error, error) { extraArgs := []string{ - "-debug", "-regtest", "-txindex", "-disablewallet", From 6aec5b00ad2005f353c94ed141053a86d566d61f Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Tue, 3 Sep 2024 11:58:34 +0200 Subject: [PATCH 176/218] lntest: avoid port collision on Tor listen port bitcoind now seems to listen on the -bind port at all times. So we need to make sure multiple instances don't collide by using a unique port. --- lntest/bitcoind_common.go | 2 ++ lntest/unittest/backend.go | 2 ++ 2 files changed, 4 insertions(+) diff --git a/lntest/bitcoind_common.go b/lntest/bitcoind_common.go index ec26e6cc06..c66532bbca 100644 --- a/lntest/bitcoind_common.go +++ b/lntest/bitcoind_common.go @@ -115,6 +115,7 @@ func newBackend(miner string, netParams *chaincfg.Params, extraArgs []string, zmqTxAddr := fmt.Sprintf("tcp://127.0.0.1:%d", port.NextAvailablePort()) rpcPort := port.NextAvailablePort() p2pPort := port.NextAvailablePort() + torBindPort := port.NextAvailablePort() cmdArgs := []string{ "-datadir=" + tempBitcoindDir, @@ -124,6 +125,7 @@ func newBackend(miner string, netParams *chaincfg.Params, extraArgs []string, "220110063096c221be9933c82d38e1", fmt.Sprintf("-rpcport=%d", rpcPort), fmt.Sprintf("-port=%d", p2pPort), + fmt.Sprintf("-bind=127.0.0.1:%d=onion", torBindPort), "-zmqpubrawblock=" + zmqBlockAddr, "-zmqpubrawtx=" + zmqTxAddr, "-debug", diff --git a/lntest/unittest/backend.go b/lntest/unittest/backend.go index be09b395c3..ac700044d2 100644 --- a/lntest/unittest/backend.go +++ b/lntest/unittest/backend.go @@ -82,6 +82,7 @@ func NewBitcoindBackend(t *testing.T, netParams *chaincfg.Params, tempBitcoindDir := t.TempDir() rpcPort := port.NextAvailablePort() + torBindPort := port.NextAvailablePort() zmqBlockPort := port.NextAvailablePort() zmqTxPort := port.NextAvailablePort() zmqBlockHost := fmt.Sprintf("tcp://127.0.0.1:%d", zmqBlockPort) @@ -94,6 +95,7 @@ func NewBitcoindBackend(t *testing.T, netParams *chaincfg.Params, "-rpcauth=weks:469e9bb14ab2360f8e226efed5ca6fd$507c670e800a95" + "284294edb5773b05544b220110063096c221be9933c82d38e1", fmt.Sprintf("-rpcport=%d", rpcPort), + fmt.Sprintf("-bind=127.0.0.1:%d=onion", torBindPort), "-disablewallet", "-zmqpubrawblock=" + zmqBlockHost, "-zmqpubrawtx=" + zmqTxHost, From 57e7b415105cc4728f0fd85edda26335a0132761 Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Tue, 3 Sep 2024 13:10:54 +0200 Subject: [PATCH 177/218] lnwallet: turn off RBF detection in test --- lnwallet/test/test_interface.go | 151 +++++++++++++++----------------- 1 file changed, 71 insertions(+), 80 deletions(-) diff --git a/lnwallet/test/test_interface.go b/lnwallet/test/test_interface.go index 4a02f0324a..9910caefe5 100644 --- a/lnwallet/test/test_interface.go +++ b/lnwallet/test/test_interface.go @@ -1758,97 +1758,88 @@ func testPublishTransaction(r *rpctest.Harness, tx3, tx3Spend *wire.MsgTx ) t.Run("rbf_tests", func(t *testing.T) { - for _, rbf := range []bool{false, true} { - // Now we'll try to double spend an output with a - // different transaction. Create a new tx and publish - // it. This is the output we'll try to double spend. - tx3 = newTx(t, r, keyDesc.PubKey, alice, false) - err := alice.PublishTransaction(tx3, labels.External) - require.NoError(t, err) - - // Mine the transaction. - err = mineAndAssert(r, tx3) - require.NoError(t, err) + // Starting with bitcoind v28.0 and later, mempool full RBF is + // turned on, so there's no way to _not_ signal RBF anymore. + const rbf = true + + // Now we'll try to double spend an output with a + // different transaction. Create a new tx and publish + // it. This is the output we'll try to double spend. + tx3 = newTx(t, r, keyDesc.PubKey, alice, false) + err := alice.PublishTransaction(tx3, labels.External) + require.NoError(t, err) - // Now we create a transaction that spends the output - // from the tx just mined. - tx4, err := txFromOutput( - tx3, alice.Cfg.Signer, keyDesc.PubKey, - keyDesc.PubKey, txFee, rbf, - ) - require.NoError(t, err) + // Mine the transaction. + err = mineAndAssert(r, tx3) + require.NoError(t, err) - // This should be accepted into the mempool. - err = alice.PublishTransaction(tx4, labels.External) - require.NoError(t, err) + // Now we create a transaction that spends the output + // from the tx just mined. + tx4, err := txFromOutput( + tx3, alice.Cfg.Signer, keyDesc.PubKey, + keyDesc.PubKey, txFee, rbf, + ) + require.NoError(t, err) - // Keep track of the last successfully published tx to - // spend tx3. - tx3Spend = tx4 + // This should be accepted into the mempool. + err = alice.PublishTransaction(tx4, labels.External) + require.NoError(t, err) - txid4 := tx4.TxHash() - err = waitForMempoolTx(r, &txid4) - require.NoError(t, err, "tx not relayed to miner") + // Keep track of the last successfully published tx to + // spend tx3. + tx3Spend = tx4 - // Create a new key we'll pay to, to ensure we create a - // unique transaction. - keyDesc2, err := alice.DeriveNextKey( - keychain.KeyFamilyMultiSig, - ) - require.NoError(t, err, "unable to obtain public key") + txid4 := tx4.TxHash() + err = waitForMempoolTx(r, &txid4) + require.NoError(t, err, "tx not relayed to miner") - // Create a new transaction that spends the output from - // tx3, and that pays to a different address. - tx5, err := txFromOutput( - tx3, alice.Cfg.Signer, keyDesc.PubKey, - keyDesc2.PubKey, txFee, rbf, - ) - require.NoError(t, err) + // Create a new key we'll pay to, to ensure we create a + // unique transaction. + keyDesc2, err := alice.DeriveNextKey( + keychain.KeyFamilyMultiSig, + ) + require.NoError(t, err, "unable to obtain public key") - err = alice.PublishTransaction(tx5, labels.External) + // Create a new transaction that spends the output from + // tx3, and that pays to a different address. + tx5, err := txFromOutput( + tx3, alice.Cfg.Signer, keyDesc.PubKey, + keyDesc2.PubKey, txFee, rbf, + ) + require.NoError(t, err) - // If RBF is not enabled, we expect this to be rejected - // because it is a double spend. - expectedErr := lnwallet.ErrDoubleSpend + err = alice.PublishTransaction(tx5, labels.External) - // If RBF is enabled, we expect it to be rejected - // because it doesn't pay enough fees. - if rbf { - expectedErr = chain.ErrInsufficientFee - } + // We expect it to be rejected/ because it doesn't pay enough + // fees. + expectedErr := chain.ErrInsufficientFee - // Assert the expected error. - require.ErrorIsf(t, err, expectedErr, "has rbf=%v", rbf) + // Assert the expected error. + require.ErrorIsf(t, err, expectedErr, "has rbf=%v", rbf) - // Create another transaction that spends the same - // output, but has a higher fee. We expect also this tx - // to be rejected for non-RBF enabled transactions, - // while it should succeed otherwise. - pubKey3, err := alice.DeriveNextKey( - keychain.KeyFamilyMultiSig, - ) - require.NoError(t, err, "unable to obtain public key") + // Create another transaction that spends the same + // output, but has a higher fee. We expect also this tx + // to be rejected for non-RBF enabled transactions, + // while it should succeed otherwise. + pubKey3, err := alice.DeriveNextKey( + keychain.KeyFamilyMultiSig, + ) + require.NoError(t, err, "unable to obtain public key") - tx6, err := txFromOutput( - tx3, alice.Cfg.Signer, keyDesc.PubKey, - pubKey3.PubKey, 2*txFee, rbf, - ) - require.NoError(t, err) + tx6, err := txFromOutput( + tx3, alice.Cfg.Signer, keyDesc.PubKey, + pubKey3.PubKey, 2*txFee, rbf, + ) + require.NoError(t, err) - // Expect rejection in non-RBF case. - expErr := lnwallet.ErrDoubleSpend - if rbf { - // Expect success in rbf case. - expErr = nil - tx3Spend = tx6 - } - err = alice.PublishTransaction(tx6, labels.External) - require.ErrorIs(t, err, expErr) + // Expect rejection in non-RBF case. + tx3Spend = tx6 + err = alice.PublishTransaction(tx6, labels.External) + require.NoError(t, err) - // Mine the tx spending tx3. - err = mineAndAssert(r, tx3Spend) - require.NoError(t, err) - } + // Mine the tx spending tx3. + err = mineAndAssert(r, tx3Spend) + require.NoError(t, err) }) t.Run("tx_double_spend", func(t *testing.T) { @@ -3071,9 +3062,9 @@ func testSingleFunderExternalFundingTx(miner *rpctest.Harness, ) } -// TestInterfaces tests all registered interfaces with a unified set of tests -// which exercise each of the required methods found within the WalletController -// interface. +// TestLightningWallet tests all registered interfaces with a unified set of +// tests which exercise each of the required methods found within the +// WalletController interface. // // NOTE: In the future, when additional implementations of the WalletController // interface have been implemented, in order to ensure the new concrete From 34ea7695016591ea7ee7fb673f9f40894d915e6d Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Tue, 3 Sep 2024 13:15:31 +0200 Subject: [PATCH 178/218] mod: fix unit tests by updating error matching in btcwallet --- go.mod | 18 +++++++++--------- go.sum | 35 ++++++++++++++++++----------------- 2 files changed, 27 insertions(+), 26 deletions(-) diff --git a/go.mod b/go.mod index 2726bd781c..28259605c3 100644 --- a/go.mod +++ b/go.mod @@ -10,11 +10,11 @@ require ( github.com/btcsuite/btcd/btcutil/psbt v1.1.8 github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0 github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f - github.com/btcsuite/btcwallet v0.16.10-0.20240718224643-db3a4a2543bd - github.com/btcsuite/btcwallet/wallet/txauthor v1.3.4 - github.com/btcsuite/btcwallet/wallet/txrules v1.2.1 - github.com/btcsuite/btcwallet/walletdb v1.4.2 - github.com/btcsuite/btcwallet/wtxmgr v1.5.3 + github.com/btcsuite/btcwallet v0.16.10-0.20240912233857-ffb143c77cc5 + github.com/btcsuite/btcwallet/wallet/txauthor v1.3.5 + github.com/btcsuite/btcwallet/wallet/txrules v1.2.2 + github.com/btcsuite/btcwallet/walletdb v1.4.4 + github.com/btcsuite/btcwallet/wtxmgr v1.5.4 github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f github.com/davecgh/go-spew v1.1.1 github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 @@ -28,7 +28,7 @@ require ( github.com/jackpal/go-nat-pmp v0.0.0-20170405195558-28a68d0c24ad github.com/jedib0t/go-pretty/v6 v6.2.7 github.com/jessevdk/go-flags v1.4.0 - github.com/jrick/logrotate v1.0.0 + github.com/jrick/logrotate v1.1.2 github.com/kkdai/bstream v1.0.0 github.com/lightninglabs/neutrino v0.16.1-0.20240425105051-602843d34ffd github.com/lightninglabs/neutrino/cache v1.1.2 @@ -39,7 +39,7 @@ require ( github.com/lightningnetwork/lnd/healthcheck v1.2.5 github.com/lightningnetwork/lnd/kvdb v1.4.10 github.com/lightningnetwork/lnd/queue v1.1.1 - github.com/lightningnetwork/lnd/sqldb v1.0.3 + github.com/lightningnetwork/lnd/sqldb v1.0.4 github.com/lightningnetwork/lnd/ticker v1.1.1 github.com/lightningnetwork/lnd/tlv v1.2.6 github.com/lightningnetwork/lnd/tor v1.1.2 @@ -72,7 +72,7 @@ require ( github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da // indirect github.com/aead/siphash v1.0.1 // indirect github.com/beorn7/perks v1.0.1 // indirect - github.com/btcsuite/btcwallet/wallet/txsizes v1.2.4 // indirect + github.com/btcsuite/btcwallet/wallet/txsizes v1.2.5 // indirect github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd // indirect github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792 // indirect github.com/btcsuite/winsvc v1.0.0 // indirect @@ -156,7 +156,7 @@ require ( github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 // indirect github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 // indirect gitlab.com/yawning/bsaes.git v0.0.0-20190805113838-0a714cd429ec // indirect - go.etcd.io/bbolt v1.3.7 // indirect + go.etcd.io/bbolt v1.3.11 // indirect go.etcd.io/etcd/api/v3 v3.5.7 // indirect go.etcd.io/etcd/client/v2 v2.305.7 // indirect go.etcd.io/etcd/pkg/v3 v3.5.7 // indirect diff --git a/go.sum b/go.sum index f0529546b0..1677c6a3d2 100644 --- a/go.sum +++ b/go.sum @@ -92,18 +92,18 @@ github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0/go.mod h1:7SFka0XMvUgj3hfZtyd github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f h1:bAs4lUbRJpnnkd9VhRV3jjAVU7DJVjMaK+IsvSeZvFo= github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA= github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= -github.com/btcsuite/btcwallet v0.16.10-0.20240718224643-db3a4a2543bd h1:QDb8foTCRoXrfoZVEzSYgSde16MJh4gCtCin8OCS0kI= -github.com/btcsuite/btcwallet v0.16.10-0.20240718224643-db3a4a2543bd/go.mod h1:X2xDre+j1QphTRo54y2TikUzeSvreL1t1aMXrD8Kc5A= -github.com/btcsuite/btcwallet/wallet/txauthor v1.3.4 h1:poyHFf7+5+RdxNp5r2T6IBRD7RyraUsYARYbp/7t4D8= -github.com/btcsuite/btcwallet/wallet/txauthor v1.3.4/go.mod h1:GETGDQuyq+VFfH1S/+/7slLM/9aNa4l7P4ejX6dJfb0= -github.com/btcsuite/btcwallet/wallet/txrules v1.2.1 h1:UZo7YRzdHbwhK7Rhv3PO9bXgTxiOH45edK5qdsdiatk= -github.com/btcsuite/btcwallet/wallet/txrules v1.2.1/go.mod h1:MVSqRkju/IGxImXYPfBkG65FgEZYA4fXchheILMVl8g= -github.com/btcsuite/btcwallet/wallet/txsizes v1.2.4 h1:nmcKAVTv/cmYrs0A4hbiC6Qw+WTLYy/14SmTt3mLnCo= -github.com/btcsuite/btcwallet/wallet/txsizes v1.2.4/go.mod h1:YqJR8WAAHiKIPesZTr9Cx9Az4fRhRLcJ6GcxzRUZCAc= -github.com/btcsuite/btcwallet/walletdb v1.4.2 h1:zwZZ+zaHo4mK+FAN6KeK85S3oOm+92x2avsHvFAhVBE= -github.com/btcsuite/btcwallet/walletdb v1.4.2/go.mod h1:7ZQ+BvOEre90YT7eSq8bLoxTsgXidUzA/mqbRS114CQ= -github.com/btcsuite/btcwallet/wtxmgr v1.5.3 h1:QrWCio9Leh3DwkWfp+A1SURj8pYn3JuTLv3waP5uEro= -github.com/btcsuite/btcwallet/wtxmgr v1.5.3/go.mod h1:M4nQpxGTXiDlSOODKXboXX7NFthmiBNjzAKKNS7Fhjg= +github.com/btcsuite/btcwallet v0.16.10-0.20240912233857-ffb143c77cc5 h1:zYy233eUBvkF3lq2MUkybEhxhDsrRDSgiToIKN57mtk= +github.com/btcsuite/btcwallet v0.16.10-0.20240912233857-ffb143c77cc5/go.mod h1:1HJXYbjJzgumlnxOC2+ViR1U+gnHWoOn7WeK5OfY1eU= +github.com/btcsuite/btcwallet/wallet/txauthor v1.3.5 h1:Rr0njWI3r341nhSPesKQ2JF+ugDSzdPoeckS75SeDZk= +github.com/btcsuite/btcwallet/wallet/txauthor v1.3.5/go.mod h1:+tXJ3Ym0nlQc/iHSwW1qzjmPs3ev+UVWMbGgfV1OZqU= +github.com/btcsuite/btcwallet/wallet/txrules v1.2.2 h1:YEO+Lx1ZJJAtdRrjuhXjWrYsmAk26wLTlNzxt2q0lhk= +github.com/btcsuite/btcwallet/wallet/txrules v1.2.2/go.mod h1:4v+grppsDpVn91SJv+mZT7B8hEV4nSmpREM4I8Uohws= +github.com/btcsuite/btcwallet/wallet/txsizes v1.2.5 h1:93o5Xz9dYepBP4RMFUc9RGIFXwqP2volSWRkYJFrNtI= +github.com/btcsuite/btcwallet/wallet/txsizes v1.2.5/go.mod h1:lQ+e9HxZ85QP7r3kdxItkiMSloSLg1PEGis5o5CXUQw= +github.com/btcsuite/btcwallet/walletdb v1.4.4 h1:BDel6iT/ltYSIYKs0YbjwnEDi7xR3yzABIsQxN2F1L8= +github.com/btcsuite/btcwallet/walletdb v1.4.4/go.mod h1:jk/hvpLFINF0C1kfTn0bfx2GbnFT+Nvnj6eblZALfjs= +github.com/btcsuite/btcwallet/wtxmgr v1.5.4 h1:hJjHy1h/dJwSfD9uDsCwcH21D1iOrus6OrI5gR9E/O0= +github.com/btcsuite/btcwallet/wtxmgr v1.5.4/go.mod h1:lAv0b1Vj9Ig5U8QFm0yiJ9WqPl8yGO/6l7JxdHY1PKE= github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd h1:R/opQEbFEy9JGkIguV40SvRY1uliPX8ifOvi6ICsFCw= github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg= github.com/btcsuite/golangcrypto v0.0.0-20150304025918-53f62d9b43e8/go.mod h1:tYvUd8KLhm/oXvUeSEs2VlLghFjQt9+ZaF9ghH0JNjc= @@ -274,8 +274,8 @@ github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= -github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= @@ -384,8 +384,9 @@ github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJS github.com/jonboulle/clockwork v0.2.2 h1:UOGuzwb1PwsrDAObMuhUnj0p5ULPj8V/xJ7Kx9qUBdQ= github.com/jonboulle/clockwork v0.2.2/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8= github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= -github.com/jrick/logrotate v1.0.0 h1:lQ1bL/n9mBNeIXoTUoYRlK4dHuNJVofX9oWqBtPnSzI= github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ= +github.com/jrick/logrotate v1.1.2 h1:6ePk462NCX7TfKtNp5JJ7MbA2YIslkpfgP03TlTYMN0= +github.com/jrick/logrotate v1.1.2/go.mod h1:f9tdWggSVK3iqavGpyvegq5IhNois7KXmasU6/N96OQ= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.11 h1:uVUAXhF2To8cbw/3xN3pxj6kk7TYKs98NIrTqPlMWAQ= @@ -618,8 +619,8 @@ github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1 github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q= gitlab.com/yawning/bsaes.git v0.0.0-20190805113838-0a714cd429ec h1:FpfFs4EhNehiVfzQttTuxanPIT43FtkkCFypIod8LHo= gitlab.com/yawning/bsaes.git v0.0.0-20190805113838-0a714cd429ec/go.mod h1:BZ1RAoRPbCxum9Grlv5aeksu2H8BiKehBYooU2LFiOQ= -go.etcd.io/bbolt v1.3.7 h1:j+zJOnnEjF/kyHlDDgGnVL/AIqIJPq8UoB2GSNfkUfQ= -go.etcd.io/bbolt v1.3.7/go.mod h1:N9Mkw9X8x5fupy0IKsmuqVtoGDyxsaDlbk4Rd05IAQw= +go.etcd.io/bbolt v1.3.11 h1:yGEzV1wPz2yVCLsD8ZAiGHhHVlczyC9d1rP43/VCRJ0= +go.etcd.io/bbolt v1.3.11/go.mod h1:dksAq7YMXoljX0xu6VF5DMZGbhYYoLUalEiSySYAS4I= go.etcd.io/etcd/api/v3 v3.5.7 h1:sbcmosSVesNrWOJ58ZQFitHMdncusIifYcrBfwrlJSY= go.etcd.io/etcd/api/v3 v3.5.7/go.mod h1:9qew1gCdDDLu+VwmeG+iFpL+QlpHTo7iubavdVDgCAA= go.etcd.io/etcd/client/pkg/v3 v3.5.7 h1:y3kf5Gbp4e4q7egZdn5T7W9TSHUvkClN6u+Rq9mEOmg= From 55e64607078a3c3e1090698655481359e52323fa Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Tue, 3 Sep 2024 13:15:44 +0200 Subject: [PATCH 179/218] docs: add release notes --- docs/release-notes/release-notes-0.18.4.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/release-notes/release-notes-0.18.4.md b/docs/release-notes/release-notes-0.18.4.md index 727aff9807..dd3c979829 100644 --- a/docs/release-notes/release-notes-0.18.4.md +++ b/docs/release-notes/release-notes-0.18.4.md @@ -35,6 +35,10 @@ types](https://github.com/lightningnetwork/lnd/pull/8960). added [to turn on custom channel functionality](https://github.com/lightningnetwork/lnd/pull/8960). +* Compatibility with [`bitcoind + v28.0`](https://github.com/lightningnetwork/lnd/pull/9059) was ensured by + updating the version the CI pipeline is running against. + ## RPC Additions * Some new experimental [RPCs for managing SCID From 1aae94f17d730a5cec8bd065a7dac26640a33ec3 Mon Sep 17 00:00:00 2001 From: yyforyongyu Date: Thu, 17 Oct 2024 07:14:51 +0800 Subject: [PATCH 180/218] itest+lntest: stop using pointer to `chainhash.Hash` This commit fixes the methods used in `lntest` so they stop using pointers to chainhash. --- itest/lnd_channel_force_close_test.go | 2 +- itest/lnd_coop_close_with_htlcs_test.go | 2 +- itest/lnd_funding_test.go | 6 ++--- itest/lnd_multi-hop_test.go | 30 +++++++++++------------ itest/lnd_nonstd_sweep_test.go | 2 +- itest/lnd_open_channel_test.go | 4 ++-- itest/lnd_psbt_test.go | 16 ++++++------- itest/lnd_revocation_test.go | 24 +++++++++---------- itest/lnd_signer_test.go | 2 +- itest/lnd_sweep_test.go | 16 ++++++------- itest/lnd_taproot_test.go | 8 +++---- itest/lnd_wallet_import_test.go | 4 ++-- lntest/harness.go | 21 ++++++++-------- lntest/harness_assertion.go | 8 +++---- lntest/harness_miner.go | 12 +++++----- lntest/miner/miner.go | 32 +++++++++++++++---------- 16 files changed, 98 insertions(+), 91 deletions(-) diff --git a/itest/lnd_channel_force_close_test.go b/itest/lnd_channel_force_close_test.go index 34df5ab591..6d02804012 100644 --- a/itest/lnd_channel_force_close_test.go +++ b/itest/lnd_channel_force_close_test.go @@ -634,7 +634,7 @@ func channelForceClosureTest(ht *lntest.HarnessTest, // Recorf the HTLC outpoint, such that we can later // check whether it gets swept op := wire.OutPoint{ - Hash: *htlcTxID, + Hash: htlcTxID, Index: uint32(i), } htlcTxOutpointSet[op] = 0 diff --git a/itest/lnd_coop_close_with_htlcs_test.go b/itest/lnd_coop_close_with_htlcs_test.go index 56f9e801b0..55d4d6d207 100644 --- a/itest/lnd_coop_close_with_htlcs_test.go +++ b/itest/lnd_coop_close_with_htlcs_test.go @@ -117,7 +117,7 @@ func coopCloseWithHTLCs(ht *lntest.HarnessTest) { ) // Wait for the close tx to be in the Mempool. - ht.AssertTxInMempool(&closeTxid) + ht.AssertTxInMempool(closeTxid) // Wait for it to get mined and finish tearing down. ht.AssertStreamChannelCoopClosed(alice, chanPoint, false, closeClient) diff --git a/itest/lnd_funding_test.go b/itest/lnd_funding_test.go index a1c2e292d3..8d715067d9 100644 --- a/itest/lnd_funding_test.go +++ b/itest/lnd_funding_test.go @@ -874,7 +874,7 @@ func testChannelFundingPersistence(ht *lntest.HarnessTest) { // channel has been opened. The funding transaction should be found // within the newly mined block. block := ht.MineBlocksAndAssertNumTxes(1, 1)[0] - ht.AssertTxInBlock(block, fundingTxID) + ht.AssertTxInBlock(block, *fundingTxID) // Get the height that our transaction confirmed at. height := int32(ht.CurrentHeight()) @@ -1067,13 +1067,13 @@ func testBatchChanFunding(ht *lntest.HarnessTest) { // Mine the batch transaction and check the network topology. block := ht.MineBlocksAndAssertNumTxes(6, 1)[0] - ht.AssertTxInBlock(block, txHash) + ht.AssertTxInBlock(block, *txHash) ht.AssertTopologyChannelOpen(alice, chanPoint1) ht.AssertTopologyChannelOpen(alice, chanPoint2) ht.AssertTopologyChannelOpen(alice, chanPoint3) // Check if the change type from the batch_open_channel funding is P2TR. - rawTx := ht.GetRawTransaction(txHash) + rawTx := ht.GetRawTransaction(*txHash) require.Len(ht, rawTx.MsgTx().TxOut, 5) // Check the fee rate of the batch-opening transaction. We expect slight diff --git a/itest/lnd_multi-hop_test.go b/itest/lnd_multi-hop_test.go index d095936196..997c22bc39 100644 --- a/itest/lnd_multi-hop_test.go +++ b/itest/lnd_multi-hop_test.go @@ -736,8 +736,8 @@ func runMultiHopLocalForceCloseOnChainHtlcTimeout(ht *lntest.HarnessTest, ht.MineBlocksAndAssertNumTxes(1, 1) blocksMined++ - htlcOutpoint := wire.OutPoint{Hash: *closeTx, Index: 2} - bobCommitOutpoint := wire.OutPoint{Hash: *closeTx, Index: 3} + htlcOutpoint := wire.OutPoint{Hash: closeTx, Index: 2} + bobCommitOutpoint := wire.OutPoint{Hash: closeTx, Index: 3} // Before the HTLC times out, we'll need to assert that Bob broadcasts // a sweep transaction for his commit output. Note that if the channel @@ -761,7 +761,7 @@ func runMultiHopLocalForceCloseOnChainHtlcTimeout(ht *lntest.HarnessTest, ) txid := commitSweepTx.TxHash() block := ht.MineBlocksAndAssertNumTxes(1, 1)[0] - ht.AssertTxInBlock(block, &txid) + ht.AssertTxInBlock(block, txid) blocksMined++ } @@ -789,7 +789,7 @@ func runMultiHopLocalForceCloseOnChainHtlcTimeout(ht *lntest.HarnessTest, // Next, we'll mine an additional block. This should serve to confirm // the second layer timeout transaction. block := ht.MineBlocksAndAssertNumTxes(1, 1)[0] - ht.AssertTxInBlock(block, &timeoutTx) + ht.AssertTxInBlock(block, timeoutTx) // With the second layer timeout transaction confirmed, Bob should have // canceled backwards the HTLC that carol sent. @@ -1041,13 +1041,13 @@ func runMultiHopRemoteForceCloseOnChainHtlcTimeout(ht *lntest.HarnessTest, // Mine a block to trigger the sweep. ht.MineEmptyBlocks(1) - bobCommitOutpoint := wire.OutPoint{Hash: *closeTx, Index: 3} + bobCommitOutpoint := wire.OutPoint{Hash: closeTx, Index: 3} bobCommitSweep := ht.AssertOutpointInMempool( bobCommitOutpoint, ) bobCommitSweepTxid := bobCommitSweep.TxHash() block := ht.MineBlocksAndAssertNumTxes(1, 1)[0] - ht.AssertTxInBlock(block, &bobCommitSweepTxid) + ht.AssertTxInBlock(block, bobCommitSweepTxid) } ht.AssertNumPendingForceClose(bob, 0) @@ -1226,7 +1226,7 @@ func runMultiHopHtlcLocalChainClaim(ht *lntest.HarnessTest, // Mine a block that should confirm the commit tx. block := ht.MineBlocksAndAssertNumTxes(1, 1)[0] - ht.AssertTxInBlock(block, &closingTxid) + ht.AssertTxInBlock(block, closingTxid) // After the force close transaction is mined, Carol should offer her // second-level success HTLC tx and anchor to the sweeper. @@ -1303,7 +1303,7 @@ func runMultiHopHtlcLocalChainClaim(ht *lntest.HarnessTest, bobSecondLvlTx := ht.GetNumTxsFromMempool(1)[0] // It should spend from the commitment in the channel with Alice. - ht.AssertTxSpendFrom(bobSecondLvlTx, *bobForceClose) + ht.AssertTxSpendFrom(bobSecondLvlTx, bobForceClose) // At this point, Bob should have broadcast his second layer success // transaction, and should have sent it to the nursery for incubation. @@ -1362,7 +1362,7 @@ func runMultiHopHtlcLocalChainClaim(ht *lntest.HarnessTest, // Now Bob should have no pending channels anymore, as this just // resolved it by the confirmation of the sweep transaction. block = ht.MineBlocksAndAssertNumTxes(1, 1)[0] - ht.AssertTxInBlock(block, &bobSweepTxid) + ht.AssertTxInBlock(block, bobSweepTxid) // With the script-enforced lease commitment type, Alice and Bob still // haven't been able to sweep their respective commit outputs due to the @@ -1397,7 +1397,7 @@ func runMultiHopHtlcLocalChainClaim(ht *lntest.HarnessTest, // Both Alice and Bob show broadcast their commit sweeps. aliceCommitOutpoint := wire.OutPoint{ - Hash: *bobForceClose, Index: 3, + Hash: bobForceClose, Index: 3, } ht.AssertOutpointInMempool( aliceCommitOutpoint, @@ -1574,7 +1574,7 @@ func runMultiHopHtlcRemoteChainClaim(ht *lntest.HarnessTest, // Mine a block, which should contain: the commitment. block := ht.MineBlocksAndAssertNumTxes(1, 1)[0] - ht.AssertTxInBlock(block, &closingTxid) + ht.AssertTxInBlock(block, closingTxid) // After the force close transaction is mined, Carol should offer her // second level HTLC tx to the sweeper, along with her anchor output. @@ -1628,12 +1628,12 @@ func runMultiHopHtlcRemoteChainClaim(ht *lntest.HarnessTest, bobHtlcSweepTxid := bobHtlcSweep.TxHash() // It should spend from the commitment in the channel with Alice. - ht.AssertTxSpendFrom(bobHtlcSweep, *aliceForceClose) + ht.AssertTxSpendFrom(bobHtlcSweep, aliceForceClose) // We'll now mine a block which should confirm Bob's HTLC sweep // transaction. block = ht.MineBlocksAndAssertNumTxes(1, 1)[0] - ht.AssertTxInBlock(block, &bobHtlcSweepTxid) + ht.AssertTxInBlock(block, bobHtlcSweepTxid) carolSecondLevelCSV-- // Now that the sweeping transaction has been confirmed, Bob should now @@ -1690,7 +1690,7 @@ func runMultiHopHtlcRemoteChainClaim(ht *lntest.HarnessTest, // Both Alice and Bob should broadcast their commit sweeps. aliceCommitOutpoint := wire.OutPoint{ - Hash: *aliceForceClose, Index: 3, + Hash: aliceForceClose, Index: 3, } ht.AssertOutpointInMempool(aliceCommitOutpoint) bobCommitOutpoint := wire.OutPoint{Hash: closingTxid, Index: 3} @@ -2162,7 +2162,7 @@ func runMultiHopHtlcAggregation(ht *lntest.HarnessTest, // level sweep. Now Bob should have no pending channels anymore, as // this just resolved it by the confirmation of the sweep transaction. block := ht.MineBlocksAndAssertNumTxes(1, numExpected)[0] - ht.AssertTxInBlock(block, &bobSweep) + ht.AssertTxInBlock(block, bobSweep) // For leased channels, we need to mine one more block to confirm Bob's // commit output sweep. diff --git a/itest/lnd_nonstd_sweep_test.go b/itest/lnd_nonstd_sweep_test.go index f83daa669f..1e20e2bfe7 100644 --- a/itest/lnd_nonstd_sweep_test.go +++ b/itest/lnd_nonstd_sweep_test.go @@ -111,7 +111,7 @@ func testNonStdSweepInner(ht *lntest.HarnessTest, address string) { for _, inp := range msgTx.TxIn { // Fetch the previous outpoint's value. prevOut := inp.PreviousOutPoint - ptx := ht.GetRawTransaction(&prevOut.Hash) + ptx := ht.GetRawTransaction(prevOut.Hash) pout := ptx.MsgTx().TxOut[prevOut.Index] inputVal += int(pout.Value) diff --git a/itest/lnd_open_channel_test.go b/itest/lnd_open_channel_test.go index 52ec622cf7..a196d71c5f 100644 --- a/itest/lnd_open_channel_test.go +++ b/itest/lnd_open_channel_test.go @@ -60,7 +60,7 @@ func testOpenChannelAfterReorg(ht *lntest.HarnessTest) { // channel on the original miner's chain, which should be considered // open. block := ht.MineBlocksAndAssertNumTxes(10, 1)[0] - ht.AssertTxInBlock(block, fundingTxID) + ht.AssertTxInBlock(block, *fundingTxID) _, err = tempMiner.Client.Generate(15) require.NoError(ht, err, "unable to generate blocks") @@ -116,7 +116,7 @@ func testOpenChannelAfterReorg(ht *lntest.HarnessTest) { // Cleanup by mining the funding tx again, then closing the channel. block = ht.MineBlocksAndAssertNumTxes(1, 1)[0] - ht.AssertTxInBlock(block, fundingTxID) + ht.AssertTxInBlock(block, *fundingTxID) ht.CloseChannel(alice, chanPoint) } diff --git a/itest/lnd_psbt_test.go b/itest/lnd_psbt_test.go index c2dffcaa21..58f21508fe 100644 --- a/itest/lnd_psbt_test.go +++ b/itest/lnd_psbt_test.go @@ -306,7 +306,7 @@ func runPsbtChanFunding(ht *lntest.HarnessTest, carol, dave *node.HarnessNode, txHash := finalTx.TxHash() block := ht.MineBlocksAndAssertNumTxes(6, 1)[0] - ht.AssertTxInBlock(block, &txHash) + ht.AssertTxInBlock(block, txHash) ht.AssertTopologyChannelOpen(carol, chanPoint) ht.AssertTopologyChannelOpen(carol, chanPoint2) @@ -481,7 +481,7 @@ func runPsbtChanFundingExternal(ht *lntest.HarnessTest, carol, // Now we can mine a block to get the transaction confirmed, then wait // for the new channel to be propagated through the network. block := ht.MineBlocksAndAssertNumTxes(6, 1)[0] - ht.AssertTxInBlock(block, &txHash) + ht.AssertTxInBlock(block, txHash) ht.AssertTopologyChannelOpen(carol, chanPoint) ht.AssertTopologyChannelOpen(carol, chanPoint2) @@ -638,7 +638,7 @@ func runPsbtChanFundingSingleStep(ht *lntest.HarnessTest, carol, txHash := finalTx.TxHash() block := ht.MineBlocksAndAssertNumTxes(6, 1)[0] - ht.AssertTxInBlock(block, &txHash) + ht.AssertTxInBlock(block, txHash) ht.AssertTopologyChannelOpen(carol, chanPoint) // Next, to make sure the channel functions as normal, we'll make some @@ -1337,7 +1337,7 @@ func extractPublishAndMine(ht *lntest.HarnessTest, node *node.HarnessNode, // Mine one block which should contain two transactions. block := ht.MineBlocksAndAssertNumTxes(1, 1)[0] txHash := finalTx.TxHash() - ht.AssertTxInBlock(block, &txHash) + ht.AssertTxInBlock(block, txHash) return finalTx } @@ -1443,8 +1443,8 @@ func assertPsbtSpend(ht *lntest.HarnessTest, alice *node.HarnessNode, block := ht.MineBlocksAndAssertNumTxes(1, 2)[0] firstTxHash := prevTx.TxHash() secondTxHash := finalTx.TxHash() - ht.AssertTxInBlock(block, &firstTxHash) - ht.AssertTxInBlock(block, &secondTxHash) + ht.AssertTxInBlock(block, firstTxHash) + ht.AssertTxInBlock(block, secondTxHash) } // assertPsbtFundSignSpend funds a PSBT from the internal wallet and then @@ -1808,7 +1808,7 @@ func testPsbtChanFundingWithUnstableUtxos(ht *lntest.HarnessTest) { txHash := finalTx.TxHash() block := ht.MineBlocksAndAssertNumTxes(1, 1)[0] - ht.AssertTxInBlock(block, &txHash) + ht.AssertTxInBlock(block, txHash) // Now we do the same but instead use preselected utxos to verify that // these utxos respects the utxo restrictions on sweeper unconfirmed @@ -1952,5 +1952,5 @@ func testPsbtChanFundingWithUnstableUtxos(ht *lntest.HarnessTest) { txHash = finalTx.TxHash() block = ht.MineBlocksAndAssertNumTxes(1, 1)[0] - ht.AssertTxInBlock(block, &txHash) + ht.AssertTxInBlock(block, txHash) } diff --git a/itest/lnd_revocation_test.go b/itest/lnd_revocation_test.go index 82415e0396..a8cef50070 100644 --- a/itest/lnd_revocation_test.go +++ b/itest/lnd_revocation_test.go @@ -128,7 +128,7 @@ func breachRetributionTestCase(ht *lntest.HarnessTest, // ordering, the first output in this breach tx is the to_remote // output. toRemoteOp := wire.OutPoint{ - Hash: *breachTXID, + Hash: breachTXID, Index: 0, } @@ -151,7 +151,7 @@ func breachRetributionTestCase(ht *lntest.HarnessTest, // Assert that all the inputs of this transaction are spending outputs // generated by Bob's breach transaction above. for _, txIn := range justiceTx.TxIn { - require.Equal(ht, *breachTXID, txIn.PreviousOutPoint.Hash, + require.Equal(ht, breachTXID, txIn.PreviousOutPoint.Hash, "justice tx not spending commitment utxo") } @@ -174,7 +174,7 @@ func breachRetributionTestCase(ht *lntest.HarnessTest, // transaction which was just accepted into the mempool. block = ht.MineBlocksAndAssertNumTxes(1, 1)[0] justiceTxid := justiceTx.TxHash() - ht.AssertTxInBlock(block, &justiceTxid) + ht.AssertTxInBlock(block, justiceTxid) ht.AssertNodeNumChannels(carol, 0) @@ -318,7 +318,7 @@ func revokedCloseRetributionZeroValueRemoteOutputCase(ht *lntest.HarnessTest, // ordering, the first output in this breach tx is the to_local // output. toLocalOp := wire.OutPoint{ - Hash: *breachTXID, + Hash: breachTXID, Index: 0, } @@ -363,7 +363,7 @@ func revokedCloseRetributionZeroValueRemoteOutputCase(ht *lntest.HarnessTest, // transaction which was just accepted into the mempool. block := ht.MineBlocksAndAssertNumTxes(1, 1)[0] justiceTxid := justiceTx.TxHash() - ht.AssertTxInBlock(block, &justiceTxid) + ht.AssertTxInBlock(block, justiceTxid) // At this point, Dave should have no pending channels. ht.AssertNodeNumChannels(dave, 0) @@ -567,7 +567,7 @@ func revokedCloseRetributionRemoteHodlCase(ht *lntest.HarnessTest, // outputs to the second level before Dave broadcasts his justice tx, // we'll search through the mempool for a tx that matches the number of // expected inputs in the justice tx. - var justiceTxid *chainhash.Hash + var justiceTxid chainhash.Hash errNotFound := errors.New("justice tx not found") findJusticeTx := func() (*chainhash.Hash, error) { mempool := ht.GetRawMempool() @@ -579,14 +579,14 @@ func revokedCloseRetributionRemoteHodlCase(ht *lntest.HarnessTest, // NOTE: We don't use `ht.GetRawTransaction` // which asserts a txid must be found as the HTLC // spending txes might be aggregated. - tx, err := ht.Miner().Client.GetRawTransaction(txid) + tx, err := ht.Miner().Client.GetRawTransaction(&txid) if err != nil { return nil, err } exNumInputs := 2 + numInvoices if len(tx.MsgTx().TxIn) == exNumInputs { - return txid, nil + return &txid, nil } } @@ -598,7 +598,7 @@ func revokedCloseRetributionRemoteHodlCase(ht *lntest.HarnessTest, if err != nil { return err } - justiceTxid = txid + justiceTxid = *txid return nil }, defaultTimeout) @@ -619,7 +619,7 @@ func revokedCloseRetributionRemoteHodlCase(ht *lntest.HarnessTest, return err } - justiceTxid = txid + justiceTxid = *txid return nil }, defaultTimeout) @@ -631,7 +631,7 @@ func revokedCloseRetributionRemoteHodlCase(ht *lntest.HarnessTest, // isSecondLevelSpend checks that the passed secondLevelTxid is a // potentitial second level spend spending from the commit tx. isSecondLevelSpend := func(commitTxid, - secondLevelTxid *chainhash.Hash) bool { + secondLevelTxid chainhash.Hash) bool { secondLevel := ht.GetRawTransaction(secondLevelTxid) @@ -661,7 +661,7 @@ func revokedCloseRetributionRemoteHodlCase(ht *lntest.HarnessTest, // the breach tx, Carol might have had the time to take an // output to the second level. In that case, check that the // justice tx is spending this second level output. - if isSecondLevelSpend(breachTXID, &txIn.PreviousOutPoint.Hash) { + if isSecondLevelSpend(breachTXID, txIn.PreviousOutPoint.Hash) { continue } require.Fail(ht, "justice tx not spending commitment utxo "+ diff --git a/itest/lnd_signer_test.go b/itest/lnd_signer_test.go index bf9033cad8..9310699887 100644 --- a/itest/lnd_signer_test.go +++ b/itest/lnd_signer_test.go @@ -318,7 +318,7 @@ func assertSignOutputRaw(ht *lntest.HarnessTest, tx := wire.NewMsgTx(2) tx.TxIn = []*wire.TxIn{{ PreviousOutPoint: wire.OutPoint{ - Hash: *txid, + Hash: txid, Index: uint32(targetOutputIndex), }, }} diff --git a/itest/lnd_sweep_test.go b/itest/lnd_sweep_test.go index 5c4e63f3ea..3ccf6c8763 100644 --- a/itest/lnd_sweep_test.go +++ b/itest/lnd_sweep_test.go @@ -214,7 +214,7 @@ func testSweepCPFPAnchorOutgoingTimeout(ht *lntest.HarnessTest) { txns := ht.GetNumTxsFromMempool(2) // Find the sweeping tx. - sweepTx := ht.FindSweepingTxns(txns, 1, *closeTxid)[0] + sweepTx := ht.FindSweepingTxns(txns, 1, closeTxid)[0] // Get the weight for Bob's anchor sweeping tx. txWeight := ht.CalculateTxWeight(sweepTx) @@ -281,7 +281,7 @@ func testSweepCPFPAnchorOutgoingTimeout(ht *lntest.HarnessTest) { txns = ht.GetNumTxsFromMempool(2) // Find the sweeping tx. - sweepTx = ht.FindSweepingTxns(txns, 1, *closeTxid)[0] + sweepTx = ht.FindSweepingTxns(txns, 1, closeTxid)[0] // Calculate the fee rate of Bob's new sweeping tx. feeRate = uint64(ht.CalculateTxFeeRate(sweepTx)) @@ -320,7 +320,7 @@ func testSweepCPFPAnchorOutgoingTimeout(ht *lntest.HarnessTest) { txns = ht.GetNumTxsFromMempool(2) // Find the sweeping tx. - sweepTx = ht.FindSweepingTxns(txns, 1, *closeTxid)[0] + sweepTx = ht.FindSweepingTxns(txns, 1, closeTxid)[0] // Calculate the fee of Bob's new sweeping tx. fee = uint64(ht.CalculateTxFee(sweepTx)) @@ -341,7 +341,7 @@ func testSweepCPFPAnchorOutgoingTimeout(ht *lntest.HarnessTest) { txns = ht.GetNumTxsFromMempool(2) // Find the sweeping tx. - currentSweepTx := ht.FindSweepingTxns(txns, 1, *closeTxid)[0] + currentSweepTx := ht.FindSweepingTxns(txns, 1, closeTxid)[0] // Assert the anchor sweep tx stays unchanged. require.Equal(ht, sweepTx.TxHash(), currentSweepTx.TxHash()) @@ -553,7 +553,7 @@ func testSweepCPFPAnchorIncomingTimeout(ht *lntest.HarnessTest) { txns := ht.GetNumTxsFromMempool(2) // Find the sweeping tx. - sweepTx := ht.FindSweepingTxns(txns, 1, *closeTxid)[0] + sweepTx := ht.FindSweepingTxns(txns, 1, closeTxid)[0] // Get the weight for Bob's anchor sweeping tx. txWeight := ht.CalculateTxWeight(sweepTx) @@ -620,7 +620,7 @@ func testSweepCPFPAnchorIncomingTimeout(ht *lntest.HarnessTest) { txns = ht.GetNumTxsFromMempool(2) // Find the sweeping tx. - sweepTx = ht.FindSweepingTxns(txns, 1, *closeTxid)[0] + sweepTx = ht.FindSweepingTxns(txns, 1, closeTxid)[0] // Calculate the fee rate of Bob's new sweeping tx. feeRate = uint64(ht.CalculateTxFeeRate(sweepTx)) @@ -659,7 +659,7 @@ func testSweepCPFPAnchorIncomingTimeout(ht *lntest.HarnessTest) { txns = ht.GetNumTxsFromMempool(2) // Find the sweeping tx. - sweepTx = ht.FindSweepingTxns(txns, 1, *closeTxid)[0] + sweepTx = ht.FindSweepingTxns(txns, 1, closeTxid)[0] // Calculate the fee of Bob's new sweeping tx. fee = uint64(ht.CalculateTxFee(sweepTx)) @@ -680,7 +680,7 @@ func testSweepCPFPAnchorIncomingTimeout(ht *lntest.HarnessTest) { txns = ht.GetNumTxsFromMempool(2) // Find the sweeping tx. - currentSweepTx := ht.FindSweepingTxns(txns, 1, *closeTxid)[0] + currentSweepTx := ht.FindSweepingTxns(txns, 1, closeTxid)[0] // Assert the anchor sweep tx stays unchanged. require.Equal(ht, sweepTx.TxHash(), currentSweepTx.TxHash()) diff --git a/itest/lnd_taproot_test.go b/itest/lnd_taproot_test.go index 9434586a40..b30f8cdaab 100644 --- a/itest/lnd_taproot_test.go +++ b/itest/lnd_taproot_test.go @@ -171,7 +171,7 @@ func testTaprootComputeInputScriptKeySpendBip86(ht *lntest.HarnessTest, ht.AssertUTXOInWallet(alice, op, "") p2trOutpoint := wire.OutPoint{ - Hash: *txid, + Hash: txid, Index: uint32(p2trOutputIndex), } @@ -1413,7 +1413,7 @@ func clearWalletImportedTapscriptBalance(ht *lntest.HarnessTest, // Mine one block which should contain the sweep transaction. block := ht.MineBlocksAndAssertNumTxes(1, 1)[0] sweepTxHash := sweepTx.TxHash() - ht.AssertTxInBlock(block, &sweepTxHash) + ht.AssertTxInBlock(block, sweepTxHash) } // testScriptHashLock returns a simple bitcoin script that locks the funds to @@ -1484,7 +1484,7 @@ func sendToTaprootOutput(ht *lntest.HarnessTest, hn *node.HarnessNode, txid := ht.AssertNumTxsInMempool(1)[0] p2trOutputIndex := ht.GetOutputIndex(txid, tapScriptAddr.String()) p2trOutpoint := wire.OutPoint{ - Hash: *txid, + Hash: txid, Index: uint32(p2trOutputIndex), } @@ -1849,7 +1849,7 @@ func testTaprootCoopClose(ht *lntest.HarnessTest) { // assertTaprootDeliveryUsed returns true if a Taproot addr was used in // the co-op close transaction. - assertTaprootDeliveryUsed := func(closingTxid *chainhash.Hash) bool { + assertTaprootDeliveryUsed := func(closingTxid chainhash.Hash) bool { tx := ht.GetRawTransaction(closingTxid) for _, txOut := range tx.MsgTx().TxOut { if !txscript.IsPayToTaproot(txOut.PkScript) { diff --git a/itest/lnd_wallet_import_test.go b/itest/lnd_wallet_import_test.go index b9cce9f1e5..d8fa84dce4 100644 --- a/itest/lnd_wallet_import_test.go +++ b/itest/lnd_wallet_import_test.go @@ -372,7 +372,7 @@ func fundChanAndCloseFromImportedAccount(ht *lntest.HarnessTest, srcNode, ) block := ht.MineBlocksAndAssertNumTxes(6, 1)[0] - ht.AssertTxInBlock(block, txHash) + ht.AssertTxInBlock(block, *txHash) confBalanceAfterChan += chanChangeUtxoAmt ht.AssertWalletAccountBalance(srcNode, account, 0, 0) @@ -389,7 +389,7 @@ func fundChanAndCloseFromImportedAccount(ht *lntest.HarnessTest, srcNode, ) block := ht.MineBlocksAndAssertNumTxes(6, 1)[0] - ht.AssertTxInBlock(block, txHash) + ht.AssertTxInBlock(block, *txHash) confBalanceAfterChan += chanChangeUtxoAmt ht.AssertWalletAccountBalance( diff --git a/lntest/harness.go b/lntest/harness.go index 79c1f61ff7..09e2418351 100644 --- a/lntest/harness.go +++ b/lntest/harness.go @@ -342,6 +342,7 @@ func (h *HarnessTest) SetupStandbyNodes() { // above a good number of confirmations. const totalTxes = 200 h.MineBlocksAndAssertNumTxes(numBlocksSendOutput, totalTxes) + h.MineBlocks(numBlocksSendOutput) // Now we want to wait for the nodes to catch up. h.WaitForBlockchainSync(h.Alice) @@ -913,12 +914,12 @@ func (h *HarnessTest) validateNodeState(hn *node.HarnessNode) error { // GetChanPointFundingTxid takes a channel point and converts it into a chain // hash. func (h *HarnessTest) GetChanPointFundingTxid( - cp *lnrpc.ChannelPoint) *chainhash.Hash { + cp *lnrpc.ChannelPoint) chainhash.Hash { txid, err := lnrpc.GetChanPointFundingTxid(cp) require.NoError(h, err, "unable to get txid") - return txid + return *txid } // OutPointFromChannelPoint creates an outpoint from a given channel point. @@ -927,7 +928,7 @@ func (h *HarnessTest) OutPointFromChannelPoint( txid := h.GetChanPointFundingTxid(cp) return wire.OutPoint{ - Hash: *txid, + Hash: txid, Index: cp.OutputIndex, } } @@ -1263,7 +1264,7 @@ func (h *HarnessTest) OpenChannelAssertErr(srcNode, destNode *node.HarnessNode, // mempool. func (h *HarnessTest) CloseChannelAssertPending(hn *node.HarnessNode, cp *lnrpc.ChannelPoint, - force bool) (rpc.CloseChanClient, *chainhash.Hash) { + force bool) (rpc.CloseChanClient, chainhash.Hash) { // Calls the rpc to close the channel. closeReq := &lnrpc.CloseChannelRequest{ @@ -1306,9 +1307,9 @@ func (h *HarnessTest) CloseChannelAssertPending(hn *node.HarnessNode, pendingClose.ClosePending.Txid) // Assert the closing tx is in the mempool. - h.miner.AssertTxInMempool(closeTxid) + h.miner.AssertTxInMempool(*closeTxid) - return stream, closeTxid + return stream, *closeTxid } // CloseChannel attempts to coop close a non-anchored channel identified by the @@ -1321,7 +1322,7 @@ func (h *HarnessTest) CloseChannelAssertPending(hn *node.HarnessNode, // 5. the node reports zero waiting close channels. // 6. the node receives a topology update regarding the channel close. func (h *HarnessTest) CloseChannel(hn *node.HarnessNode, - cp *lnrpc.ChannelPoint) *chainhash.Hash { + cp *lnrpc.ChannelPoint) chainhash.Hash { stream, _ := h.CloseChannelAssertPending(hn, cp, false) @@ -1340,7 +1341,7 @@ func (h *HarnessTest) CloseChannel(hn *node.HarnessNode, // 7. mine DefaultCSV-1 blocks. // 8. the node reports zero pending force close channels. func (h *HarnessTest) ForceCloseChannel(hn *node.HarnessNode, - cp *lnrpc.ChannelPoint) *chainhash.Hash { + cp *lnrpc.ChannelPoint) chainhash.Hash { stream, _ := h.CloseChannelAssertPending(hn, cp, true) @@ -1902,7 +1903,7 @@ func (h *HarnessTest) CalculateTxFee(tx *wire.MsgTx) btcutil.Amount { var balance btcutil.Amount for _, in := range tx.TxIn { parentHash := in.PreviousOutPoint.Hash - rawTx := h.miner.GetRawTransaction(&parentHash) + rawTx := h.miner.GetRawTransaction(parentHash) parent := rawTx.MsgTx() value := parent.TxOut[in.PreviousOutPoint.Index].Value @@ -2151,7 +2152,7 @@ func (h *HarnessTest) ReceiveChannelEvent( // GetOutputIndex returns the output index of the given address in the given // transaction. -func (h *HarnessTest) GetOutputIndex(txid *chainhash.Hash, addr string) int { +func (h *HarnessTest) GetOutputIndex(txid chainhash.Hash, addr string) int { // We'll then extract the raw transaction from the mempool in order to // determine the index of the p2tr output. tx := h.miner.GetRawTransaction(txid) diff --git a/lntest/harness_assertion.go b/lntest/harness_assertion.go index add5b91f92..d56e70703f 100644 --- a/lntest/harness_assertion.go +++ b/lntest/harness_assertion.go @@ -534,7 +534,7 @@ func (h *HarnessTest) AssertTopologyChannelClosed(hn *node.HarnessNode, // by consuming a message from the passed close channel stream. Returns the // closing txid if found. func (h HarnessTest) WaitForChannelCloseEvent( - stream rpc.CloseChanClient) *chainhash.Hash { + stream rpc.CloseChanClient) chainhash.Hash { // Consume one event. event, err := h.ReceiveCloseChannelUpdate(stream) @@ -548,7 +548,7 @@ func (h HarnessTest) WaitForChannelCloseEvent( require.NoErrorf(h, err, "wrong format found in closing txid: %v", resp.ChanClose.ClosingTxid) - return txid + return *txid } // AssertNumWaitingClose checks that a PendingChannels response from the node @@ -634,7 +634,7 @@ func (h *HarnessTest) AssertNumPendingForceClose(hn *node.HarnessNode, // - assert the node has seen the channel close update. func (h *HarnessTest) AssertStreamChannelCoopClosed(hn *node.HarnessNode, cp *lnrpc.ChannelPoint, anchors bool, - stream rpc.CloseChanClient) *chainhash.Hash { + stream rpc.CloseChanClient) chainhash.Hash { // Assert the channel is waiting close. resp := h.AssertChannelWaitingClose(hn, cp) @@ -682,7 +682,7 @@ func (h *HarnessTest) AssertStreamChannelCoopClosed(hn *node.HarnessNode, // confirmed. func (h *HarnessTest) AssertStreamChannelForceClosed(hn *node.HarnessNode, cp *lnrpc.ChannelPoint, anchorSweep bool, - stream rpc.CloseChanClient) *chainhash.Hash { + stream rpc.CloseChanClient) chainhash.Hash { // Assert the channel is waiting close. resp := h.AssertChannelWaitingClose(hn, cp) diff --git a/lntest/harness_miner.go b/lntest/harness_miner.go index bc9aef1805..357adb4db1 100644 --- a/lntest/harness_miner.go +++ b/lntest/harness_miner.go @@ -113,7 +113,7 @@ func (h *HarnessTest) MineBlocksAndAssertNumTxes(num uint32, // Make sure the mempool has been updated. for _, txid := range txids { - h.miner.AssertTxNotInMempool(*txid) + h.miner.AssertTxNotInMempool(txid) } // Finally, make sure all the active nodes are synced. @@ -202,7 +202,7 @@ func (h *HarnessTest) mineTillForceCloseResolved(hn *node.HarnessNode) { } // AssertTxInMempool asserts a given transaction can be found in the mempool. -func (h *HarnessTest) AssertTxInMempool(txid *chainhash.Hash) *wire.MsgTx { +func (h *HarnessTest) AssertTxInMempool(txid chainhash.Hash) *wire.MsgTx { return h.miner.AssertTxInMempool(txid) } @@ -219,7 +219,7 @@ func (h *HarnessTest) AssertTxNotInMempool(txid chainhash.Hash) *wire.MsgTx { // AssertNumTxsInMempool polls until finding the desired number of transactions // in the provided miner's mempool. It will asserrt if this number is not met // after the given timeout. -func (h *HarnessTest) AssertNumTxsInMempool(n int) []*chainhash.Hash { +func (h *HarnessTest) AssertNumTxsInMempool(n int) []chainhash.Hash { return h.miner.AssertNumTxsInMempool(n) } @@ -230,7 +230,7 @@ func (h *HarnessTest) AssertOutpointInMempool(op wire.OutPoint) *wire.MsgTx { // AssertTxInBlock asserts that a given txid can be found in the passed block. func (h *HarnessTest) AssertTxInBlock(block *wire.MsgBlock, - txid *chainhash.Hash) { + txid chainhash.Hash) { h.miner.AssertTxInBlock(block, txid) } @@ -263,13 +263,13 @@ func (h *HarnessTest) DisconnectFromMiner(tempMiner *miner.HarnessMiner) { // GetRawMempool makes a RPC call to the miner's GetRawMempool and // asserts. -func (h *HarnessTest) GetRawMempool() []*chainhash.Hash { +func (h *HarnessTest) GetRawMempool() []chainhash.Hash { return h.miner.GetRawMempool() } // GetRawTransaction makes a RPC call to the miner's GetRawTransaction and // asserts. -func (h *HarnessTest) GetRawTransaction(txid *chainhash.Hash) *btcutil.Tx { +func (h *HarnessTest) GetRawTransaction(txid chainhash.Hash) *btcutil.Tx { return h.miner.GetRawTransaction(txid) } diff --git a/lntest/miner/miner.go b/lntest/miner/miner.go index bc05f542fd..204a229a44 100644 --- a/lntest/miner/miner.go +++ b/lntest/miner/miner.go @@ -17,6 +17,7 @@ import ( "github.com/btcsuite/btcd/integration/rpctest" "github.com/btcsuite/btcd/rpcclient" "github.com/btcsuite/btcd/wire" + "github.com/lightningnetwork/lnd/fn" "github.com/lightningnetwork/lnd/lntest/node" "github.com/lightningnetwork/lnd/lntest/wait" "github.com/stretchr/testify/require" @@ -155,11 +156,16 @@ func (h *HarnessMiner) GetBestBlock() (*chainhash.Hash, int32) { // GetRawMempool makes a RPC call to the miner's GetRawMempool and // asserts. -func (h *HarnessMiner) GetRawMempool() []*chainhash.Hash { +func (h *HarnessMiner) GetRawMempool() []chainhash.Hash { mempool, err := h.Client.GetRawMempool() require.NoError(h, err, "unable to get mempool") - return mempool + txns := make([]chainhash.Hash, 0, len(mempool)) + for _, txid := range mempool { + txns = append(txns, *txid) + } + + return txns } // GenerateBlocks mine 'num' of blocks and returns them. @@ -197,9 +203,9 @@ func (h *HarnessMiner) MineBlocks(num uint32) []*wire.MsgBlock { // AssertNumTxsInMempool polls until finding the desired number of transactions // in the provided miner's mempool. It will asserrt if this number is not met // after the given timeout. -func (h *HarnessMiner) AssertNumTxsInMempool(n int) []*chainhash.Hash { +func (h *HarnessMiner) AssertNumTxsInMempool(n int) []chainhash.Hash { var ( - mem []*chainhash.Hash + mem []chainhash.Hash err error ) @@ -221,7 +227,7 @@ func (h *HarnessMiner) AssertNumTxsInMempool(n int) []*chainhash.Hash { // AssertTxInBlock asserts that a given txid can be found in the passed block. func (h *HarnessMiner) AssertTxInBlock(block *wire.MsgBlock, - txid *chainhash.Hash) { + txid chainhash.Hash) { blockTxes := make([]chainhash.Hash, 0) @@ -263,8 +269,8 @@ func (h *HarnessMiner) MineBlocksAndAssertNumTxes(num uint32, // GetRawTransaction makes a RPC call to the miner's GetRawTransaction and // asserts. -func (h *HarnessMiner) GetRawTransaction(txid *chainhash.Hash) *btcutil.Tx { - tx, err := h.Client.GetRawTransaction(txid) +func (h *HarnessMiner) GetRawTransaction(txid chainhash.Hash) *btcutil.Tx { + tx, err := h.Client.GetRawTransaction(&txid) require.NoErrorf(h, err, "failed to get raw tx: %v", txid) return tx } @@ -272,15 +278,15 @@ func (h *HarnessMiner) GetRawTransaction(txid *chainhash.Hash) *btcutil.Tx { // GetRawTransactionVerbose makes a RPC call to the miner's // GetRawTransactionVerbose and asserts. func (h *HarnessMiner) GetRawTransactionVerbose( - txid *chainhash.Hash) *btcjson.TxRawResult { + txid chainhash.Hash) *btcjson.TxRawResult { - tx, err := h.Client.GetRawTransactionVerbose(txid) + tx, err := h.Client.GetRawTransactionVerbose(&txid) require.NoErrorf(h, err, "failed to get raw tx verbose: %v", txid) return tx } // AssertTxInMempool asserts a given transaction can be found in the mempool. -func (h *HarnessMiner) AssertTxInMempool(txid *chainhash.Hash) *wire.MsgTx { +func (h *HarnessMiner) AssertTxInMempool(txid chainhash.Hash) *wire.MsgTx { var msgTx *wire.MsgTx err := wait.NoError(func() error { @@ -294,7 +300,7 @@ func (h *HarnessMiner) AssertTxInMempool(txid *chainhash.Hash) *wire.MsgTx { for _, memTx := range mempool { // Check the values are equal. - if *memTx == *txid { + if memTx == txid { return nil } } @@ -324,7 +330,7 @@ func (h *HarnessMiner) AssertTxNotInMempool(txid chainhash.Hash) *wire.MsgTx { for _, memTx := range mempool { // Check the values are equal. - if txid.IsEqual(memTx) { + if txid == memTx { return fmt.Errorf("expect txid %v to be NOT "+ "found in mempool", txid) } @@ -417,7 +423,7 @@ func (h *HarnessMiner) AssertOutpointInMempool(op wire.OutPoint) *wire.MsgTx { // found. For instance, the aggregation logic used in // sweeping HTLC outputs will update the mempool by // replacing the HTLC spending txes with a single one. - tx, err := h.Client.GetRawTransaction(txid) + tx, err := h.Client.GetRawTransaction(&txid) if err != nil { return err } From 56e05a3e1ecc18fcd94d20eaa07790ce99107282 Mon Sep 17 00:00:00 2001 From: yyforyongyu Date: Thu, 17 Oct 2024 07:34:25 +0800 Subject: [PATCH 181/218] lntest: remove unused return value --- lntest/harness_miner.go | 4 ++-- lntest/miner/miner.go | 6 +----- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/lntest/harness_miner.go b/lntest/harness_miner.go index 357adb4db1..64abf87bae 100644 --- a/lntest/harness_miner.go +++ b/lntest/harness_miner.go @@ -212,8 +212,8 @@ func (h *HarnessTest) AssertTxInMempool(txid chainhash.Hash) *wire.MsgTx { // NOTE: this should be used after `AssertTxInMempool` to ensure the tx has // entered the mempool before. Otherwise it might give false positive and the // tx may enter the mempool after the check. -func (h *HarnessTest) AssertTxNotInMempool(txid chainhash.Hash) *wire.MsgTx { - return h.miner.AssertTxNotInMempool(txid) +func (h *HarnessTest) AssertTxNotInMempool(txid chainhash.Hash) { + h.miner.AssertTxNotInMempool(txid) } // AssertNumTxsInMempool polls until finding the desired number of transactions diff --git a/lntest/miner/miner.go b/lntest/miner/miner.go index 204a229a44..9fd235b083 100644 --- a/lntest/miner/miner.go +++ b/lntest/miner/miner.go @@ -320,9 +320,7 @@ func (h *HarnessMiner) AssertTxInMempool(txid chainhash.Hash) *wire.MsgTx { // NOTE: this should be used after `AssertTxInMempool` to ensure the tx has // entered the mempool before. Otherwise it might give false positive and the // tx may enter the mempool after the check. -func (h *HarnessMiner) AssertTxNotInMempool(txid chainhash.Hash) *wire.MsgTx { - var msgTx *wire.MsgTx - +func (h *HarnessMiner) AssertTxNotInMempool(txid chainhash.Hash) { err := wait.NoError(func() error { // We require the RPC call to be succeeded and won't wait for // it as it's an unexpected behavior. @@ -340,8 +338,6 @@ func (h *HarnessMiner) AssertTxNotInMempool(txid chainhash.Hash) *wire.MsgTx { }, wait.MinerMempoolTimeout) require.NoError(h, err, "timeout checking tx not in mempool") - - return msgTx } // SendOutputsWithoutChange uses the miner to send the given outputs using the From f09b6383437aeac55b69ec5368c386c481dad369 Mon Sep 17 00:00:00 2001 From: yyforyongyu Date: Thu, 17 Oct 2024 07:34:40 +0800 Subject: [PATCH 182/218] lntest: add method `AssertTxnsNotInMempool` So we only need to do one `GetRawMempool` lookup when checking the exclusion of multiple txns. --- lntest/harness_miner.go | 4 +--- lntest/miner/miner.go | 28 ++++++++++++++++++++++++++++ 2 files changed, 29 insertions(+), 3 deletions(-) diff --git a/lntest/harness_miner.go b/lntest/harness_miner.go index 64abf87bae..73619a9fbf 100644 --- a/lntest/harness_miner.go +++ b/lntest/harness_miner.go @@ -112,9 +112,7 @@ func (h *HarnessTest) MineBlocksAndAssertNumTxes(num uint32, } // Make sure the mempool has been updated. - for _, txid := range txids { - h.miner.AssertTxNotInMempool(txid) - } + h.miner.AssertTxnsNotInMempool(txids) // Finally, make sure all the active nodes are synced. bestBlock := blocks[len(blocks)-1] diff --git a/lntest/miner/miner.go b/lntest/miner/miner.go index 9fd235b083..3877e2f0dd 100644 --- a/lntest/miner/miner.go +++ b/lntest/miner/miner.go @@ -314,6 +314,34 @@ func (h *HarnessMiner) AssertTxInMempool(txid chainhash.Hash) *wire.MsgTx { return msgTx } +// AssertTxnsNotInMempool asserts the given txns are not found in the mempool. +// It assumes the mempool is not empty. +func (h *HarnessMiner) AssertTxnsNotInMempool(txids []chainhash.Hash) { + err := wait.NoError(func() error { + // We require the RPC call to be succeeded and won't wait for + // it as it's an unexpected behavior. + mempool := h.GetRawMempool() + + // Turn the mempool into a txn set for faster lookups. + mempoolTxns := fn.NewSet(mempool...) + + // Check if any of the txids are in the mempool. + for _, txid := range txids { + // Skip if the tx is not in the mempool. + if !mempoolTxns.Contains(txid) { + continue + } + + return fmt.Errorf("expect txid %v to be NOT found in "+ + "mempool", txid) + } + + return nil + }, wait.MinerMempoolTimeout) + + require.NoError(h, err, "timeout checking txns not in mempool") +} + // AssertTxNotInMempool asserts a given transaction cannot be found in the // mempool. It assumes the mempool is not empty. // From afdd53194b0875340679f5ca2cc242ef1eeda0cf Mon Sep 17 00:00:00 2001 From: Jonathan Harvey-Buschel Date: Thu, 17 Oct 2024 13:38:27 +0200 Subject: [PATCH 183/218] multi: allow mock aux signer to customize sig jobs --- funding/manager_test.go | 2 +- lnwallet/channel_test.go | 9 +++++---- lnwallet/mock.go | 23 +++++++++++++++++++---- lnwallet/test_utils.go | 2 +- peer/test_utils.go | 8 ++++++-- 5 files changed, 32 insertions(+), 12 deletions(-) diff --git a/funding/manager_test.go b/funding/manager_test.go index 633b3b89ae..a1b33d43ba 100644 --- a/funding/manager_test.go +++ b/funding/manager_test.go @@ -568,7 +568,7 @@ func createTestFundingManager(t *testing.T, privKey *btcec.PrivateKey, &lnwallet.MockAuxLeafStore{}, ), AuxSigner: fn.Some[lnwallet.AuxSigner]( - &lnwallet.MockAuxSigner{}, + lnwallet.NewAuxSignerMock(lnwallet.EmptyMockJobHandler), ), } diff --git a/lnwallet/channel_test.go b/lnwallet/channel_test.go index 5e90285786..ac18c8be6c 100644 --- a/lnwallet/channel_test.go +++ b/lnwallet/channel_test.go @@ -3469,8 +3469,9 @@ func TestChanSyncOweCommitmentAuxSigner(t *testing.T) { aliceChannel, bobChannel, err := CreateTestChannels(t, chanType) require.NoError(t, err, "unable to create test channels") - // We'll now manually attach an aux signer to Alice's channel. - auxSigner := &MockAuxSigner{} + // We'll now manually attach an aux signer to Alice's channel. We'll + // set each aux sig job to receive an instant response. + auxSigner := NewAuxSignerMock(EmptyMockJobHandler) aliceChannel.auxSigner = fn.Some[AuxSigner](auxSigner) var fakeOnionBlob [lnwire.OnionPacketSize]byte @@ -3494,8 +3495,8 @@ func TestChanSyncOweCommitmentAuxSigner(t *testing.T) { _, err = aliceChannel.AddHTLC(h, nil) require.NoError(t, err, "unable to recv bob's htlc: %v", err) - // We'll set up the mock to expect calls to PackSigs and also - // SubmitSubmitSecondLevelSigBatch. + // We'll set up the mock aux signer to expect calls to PackSigs and also + // SubmitSecondLevelSigBatch. var sigBlobBuf bytes.Buffer sigBlob := testSigBlob{ BlobInt: tlv.NewPrimitiveRecord[tlv.TlvType65634, uint16](5), diff --git a/lnwallet/mock.go b/lnwallet/mock.go index 68d5dcca78..e588c91c07 100644 --- a/lnwallet/mock.go +++ b/lnwallet/mock.go @@ -435,9 +435,26 @@ func (*MockAuxLeafStore) ApplyHtlcView( return fn.Ok(fn.None[tlv.Blob]()) } +// EmptyMockJobHandler is a mock job handler that just sends an empty response +// to all jobs. +func EmptyMockJobHandler(jobs []AuxSigJob) { + for _, sigJob := range jobs { + sigJob.Resp <- AuxSigJobResp{} + } +} + // MockAuxSigner is a mock implementation of the AuxSigner interface. type MockAuxSigner struct { mock.Mock + + jobHandlerFunc func([]AuxSigJob) +} + +// NewAuxSignerMock creates a new mock aux signer with the given job handler. +func NewAuxSignerMock(jobHandler func([]AuxSigJob)) *MockAuxSigner { + return &MockAuxSigner{ + jobHandlerFunc: jobHandler, + } } // SubmitSecondLevelSigBatch takes a batch of aux sign jobs and @@ -447,10 +464,8 @@ func (a *MockAuxSigner) SubmitSecondLevelSigBatch(chanState AuxChanState, args := a.Called(chanState, tx, jobs) - // While we return, we'll also send back an instant response for the - // set of jobs. - for _, sigJob := range jobs { - sigJob.Resp <- AuxSigJobResp{} + if a.jobHandlerFunc != nil { + a.jobHandlerFunc(jobs) } return args.Error(0) diff --git a/lnwallet/test_utils.go b/lnwallet/test_utils.go index d4f0d05aef..b450b3b303 100644 --- a/lnwallet/test_utils.go +++ b/lnwallet/test_utils.go @@ -597,7 +597,7 @@ func ForceStateTransition(chanA, chanB *LightningChannel) error { } func NewDefaultAuxSignerMock(t *testing.T) *MockAuxSigner { - auxSigner := &MockAuxSigner{} + auxSigner := NewAuxSignerMock(EmptyMockJobHandler) type testSigBlob struct { BlobInt tlv.RecordT[tlv.TlvType65634, uint16] diff --git a/peer/test_utils.go b/peer/test_utils.go index 948a22c4e3..cce8055bdc 100644 --- a/peer/test_utils.go +++ b/peer/test_utils.go @@ -305,7 +305,9 @@ func createTestPeerWithChannel(t *testing.T, updateChan func(a, channelAlice, err := lnwallet.NewLightningChannel( aliceSigner, aliceChannelState, alicePool, lnwallet.WithLeafStore(&lnwallet.MockAuxLeafStore{}), - lnwallet.WithAuxSigner(&lnwallet.MockAuxSigner{}), + lnwallet.WithAuxSigner(lnwallet.NewAuxSignerMock( + lnwallet.EmptyMockJobHandler, + )), ) if err != nil { return nil, err @@ -319,7 +321,9 @@ func createTestPeerWithChannel(t *testing.T, updateChan func(a, channelBob, err := lnwallet.NewLightningChannel( bobSigner, bobChannelState, bobPool, lnwallet.WithLeafStore(&lnwallet.MockAuxLeafStore{}), - lnwallet.WithAuxSigner(&lnwallet.MockAuxSigner{}), + lnwallet.WithAuxSigner(lnwallet.NewAuxSignerMock( + lnwallet.EmptyMockJobHandler, + )), ) if err != nil { return nil, err From b6363a8da47a43c89729968006dd0456fa2d8e7a Mon Sep 17 00:00:00 2001 From: Jonathan Harvey-Buschel Date: Thu, 17 Oct 2024 13:38:28 +0200 Subject: [PATCH 184/218] lnwallet: refactor test code for HTLC add and recv --- lnwallet/channel_test.go | 402 ++++++++------------------------------- 1 file changed, 75 insertions(+), 327 deletions(-) diff --git a/lnwallet/channel_test.go b/lnwallet/channel_test.go index ac18c8be6c..a8ac1f0b78 100644 --- a/lnwallet/channel_test.go +++ b/lnwallet/channel_test.go @@ -22,6 +22,7 @@ import ( "github.com/davecgh/go-spew/spew" "github.com/lightningnetwork/lnd/chainntnfs" "github.com/lightningnetwork/lnd/channeldb" + "github.com/lightningnetwork/lnd/channeldb/models" "github.com/lightningnetwork/lnd/fn" "github.com/lightningnetwork/lnd/input" "github.com/lightningnetwork/lnd/lntypes" @@ -49,6 +50,18 @@ func createHTLC(id int, amount lnwire.MilliSatoshi) (*lnwire.UpdateAddHTLC, [32] }, returnPreimage } +// addAndReceiveHTLC adds an HTLC as local to the first channel, and as remote +// to a second channel. The HTLC ID is not modified. +func addAndReceiveHTLC(t *testing.T, channel1, channel2 *LightningChannel, + htlc *lnwire.UpdateAddHTLC, openKey *models.CircuitKey) { + + _, err := channel1.AddHTLC(htlc, openKey) + require.NoErrorf(t, err, "channel 1 unable to add htlc: %v", err) + + _, err = channel2.ReceiveHTLC(htlc) + require.NoErrorf(t, err, "channel 2 unable to recv htlc: %v", err) +} + func assertOutputExistsByValue(t *testing.T, commitTx *wire.MsgTx, value btcutil.Amount, ) { @@ -437,10 +450,7 @@ func TestChannelZeroAddLocalHeight(t *testing.T) { htlc, _ := createHTLC(0, lnwire.MilliSatoshi(500000)) // -----add-----> - _, err = aliceChannel.AddHTLC(htlc, nil) - require.NoError(t, err) - _, err = bobChannel.ReceiveHTLC(htlc) - require.NoError(t, err) + addAndReceiveHTLC(t, aliceChannel, bobChannel, htlc, nil) // Force a state transition to lock in this add on both commitments. // -----sig-----> @@ -486,10 +496,7 @@ func TestChannelZeroAddLocalHeight(t *testing.T) { htlc2, _ := createHTLC(0, lnwire.MilliSatoshi(500000)) // <----add----- - _, err = bobChannel.AddHTLC(htlc2, nil) - require.NoError(t, err) - _, err = newAliceChannel.ReceiveHTLC(htlc2) - require.NoError(t, err) + addAndReceiveHTLC(t, bobChannel, newAliceChannel, htlc2, nil) // Bob should now send a commitment signature to Alice. // <----sig----- @@ -545,12 +552,7 @@ func TestCheckCommitTxSize(t *testing.T) { for i := 0; i <= 10; i++ { htlc, _ := createHTLC(i, lnwire.MilliSatoshi(1e7)) - if _, err := aliceChannel.AddHTLC(htlc, nil); err != nil { - t.Fatalf("alice unable to add htlc: %v", err) - } - if _, err := bobChannel.ReceiveHTLC(htlc); err != nil { - t.Fatalf("bob unable to receive htlc: %v", err) - } + addAndReceiveHTLC(t, aliceChannel, bobChannel, htlc, nil) if err := ForceStateTransition(aliceChannel, bobChannel); err != nil { t.Fatalf("unable to complete state update: %v", err) @@ -625,12 +627,7 @@ func testCommitHTLCSigTieBreak(t *testing.T, restart bool) { Expiry: uint32(numHtlcs - i), } - if _, err := aliceChannel.AddHTLC(htlc, nil); err != nil { - t.Fatalf("alice unable to add htlc: %v", err) - } - if _, err := bobChannel.ReceiveHTLC(htlc); err != nil { - t.Fatalf("bob unable to receive htlc: %v", err) - } + addAndReceiveHTLC(t, aliceChannel, bobChannel, htlc, nil) } // Have Alice initiate the first half of the commitment dance. The @@ -936,23 +933,13 @@ func testForceClose(t *testing.T, testCase *forceCloseTestCase) { // We'll ensure that the HTLC amount is above Alice's dust limit. htlcAmount := lnwire.NewMSatFromSatoshis(20000) htlcAlice, _ := createHTLC(0, htlcAmount) - if _, err := aliceChannel.AddHTLC(htlcAlice, nil); err != nil { - t.Fatalf("alice unable to add htlc: %v", err) - } - if _, err := bobChannel.ReceiveHTLC(htlcAlice); err != nil { - t.Fatalf("bob unable to recv add htlc: %v", err) - } + addAndReceiveHTLC(t, aliceChannel, bobChannel, htlcAlice, nil) // We'll also a distinct HTLC from Bob -> Alice. This way, Alice will // have both an incoming and outgoing HTLC on her commitment // transaction. htlcBob, preimageBob := createHTLC(0, htlcAmount) - if _, err := bobChannel.AddHTLC(htlcBob, nil); err != nil { - t.Fatalf("alice unable to add htlc: %v", err) - } - if _, err := aliceChannel.ReceiveHTLC(htlcBob); err != nil { - t.Fatalf("bob unable to recv add htlc: %v", err) - } + addAndReceiveHTLC(t, bobChannel, aliceChannel, htlcBob, nil) // Next, we'll perform two state transitions to ensure that both HTLC's // get fully locked-in. @@ -1406,12 +1393,7 @@ func TestDustHTLCFees(t *testing.T) { // This HTLC amount should be lower than the dust limits of both nodes. htlcAmount := lnwire.NewMSatFromSatoshis(100) htlc, _ := createHTLC(0, htlcAmount) - if _, err := aliceChannel.AddHTLC(htlc, nil); err != nil { - t.Fatalf("alice unable to add htlc: %v", err) - } - if _, err := bobChannel.ReceiveHTLC(htlc); err != nil { - t.Fatalf("bob unable to receive htlc: %v", err) - } + addAndReceiveHTLC(t, aliceChannel, bobChannel, htlc, nil) if err := ForceStateTransition(aliceChannel, bobChannel); err != nil { t.Fatalf("Can't update the channel state: %v", err) } @@ -1564,14 +1546,9 @@ func TestHTLCSigNumber(t *testing.T) { for i, htlcSat := range htlcValues { htlcMsat := lnwire.NewMSatFromSatoshis(htlcSat) htlc, _ := createHTLC(i, htlcMsat) - _, err := aliceChannel.AddHTLC(htlc, nil) - if err != nil { - t.Fatalf("alice unable to add htlc: %v", err) - } - _, err = bobChannel.ReceiveHTLC(htlc) - if err != nil { - t.Fatalf("bob unable to receive htlc: %v", err) - } + addAndReceiveHTLC( + t, aliceChannel, bobChannel, htlc, nil, + ) } return aliceChannel, bobChannel @@ -1805,12 +1782,7 @@ func TestStateUpdatePersistence(t *testing.T) { OnionBlob: fakeOnionBlob, } - if _, err := aliceChannel.AddHTLC(h, nil); err != nil { - t.Fatalf("unable to add alice's htlc: %v", err) - } - if _, err := bobChannel.ReceiveHTLC(h); err != nil { - t.Fatalf("unable to recv alice's htlc: %v", err) - } + addAndReceiveHTLC(t, aliceChannel, bobChannel, h, nil) } rHash := sha256.Sum256(bobPreimage[:]) bobh := &lnwire.UpdateAddHTLC{ @@ -1819,12 +1791,7 @@ func TestStateUpdatePersistence(t *testing.T) { Expiry: uint32(10), OnionBlob: fakeOnionBlob, } - if _, err := bobChannel.AddHTLC(bobh, nil); err != nil { - t.Fatalf("unable to add bob's htlc: %v", err) - } - if _, err := aliceChannel.ReceiveHTLC(bobh); err != nil { - t.Fatalf("unable to recv bob's htlc: %v", err) - } + addAndReceiveHTLC(t, bobChannel, aliceChannel, bobh, nil) // Also add a fee update to the update logs. fee := chainfee.SatPerKWeight(333) @@ -2492,12 +2459,7 @@ func TestUpdateFeeConcurrentSig(t *testing.T) { // First Alice adds the outgoing HTLC to her local channel's state // update log. Then Alice sends this wire message over to Bob who // adds this htlc to his remote state update log. - if _, err := aliceChannel.AddHTLC(htlc, nil); err != nil { - t.Fatalf("unable to add htlc: %v", err) - } - if _, err := bobChannel.ReceiveHTLC(htlc); err != nil { - t.Fatalf("unable to recv htlc: %v", err) - } + addAndReceiveHTLC(t, aliceChannel, bobChannel, htlc, nil) // Simulate Alice sending update fee message to bob. fee := chainfee.SatPerKWeight(333) @@ -2567,12 +2529,7 @@ func TestUpdateFeeSenderCommits(t *testing.T) { // First Alice adds the outgoing HTLC to her local channel's state // update log. Then Alice sends this wire message over to Bob who // adds this htlc to his remote state update log. - if _, err := aliceChannel.AddHTLC(htlc, nil); err != nil { - t.Fatalf("unable to add htlc: %v", err) - } - if _, err := bobChannel.ReceiveHTLC(htlc); err != nil { - t.Fatalf("unable to recv htlc: %v", err) - } + addAndReceiveHTLC(t, aliceChannel, bobChannel, htlc, nil) // Simulate Alice sending update fee message to bob. fee := chainfee.SatPerKWeight(333) @@ -2675,12 +2632,7 @@ func TestUpdateFeeReceiverCommits(t *testing.T) { // First Alice adds the outgoing HTLC to her local channel's state // update log. Then Alice sends this wire message over to Bob who // adds this htlc to his remote state update log. - if _, err := aliceChannel.AddHTLC(htlc, nil); err != nil { - t.Fatalf("unable to add htlc: %v", err) - } - if _, err := bobChannel.ReceiveHTLC(htlc); err != nil { - t.Fatalf("unable to recv htlc: %v", err) - } + addAndReceiveHTLC(t, aliceChannel, bobChannel, htlc, nil) // Simulate Alice sending update fee message to bob fee := chainfee.SatPerKWeight(333) @@ -3865,12 +3817,7 @@ func testChanSyncOweRevocation(t *testing.T, chanType channeldb.ChannelType) { Amount: htlcAmt, Expiry: uint32(10), } - if _, err := aliceChannel.AddHTLC(aliceHtlc, nil); err != nil { - t.Fatalf("unable to add alice's htlc: %v", err) - } - if _, err := bobChannel.ReceiveHTLC(aliceHtlc); err != nil { - t.Fatalf("unable to recv alice's htlc: %v", err) - } + addAndReceiveHTLC(t, aliceChannel, bobChannel, aliceHtlc, nil) if err := ForceStateTransition(aliceChannel, bobChannel); err != nil { t.Fatalf("unable to complete alice's state transition: %v", err) } @@ -4141,10 +4088,7 @@ func testChanSyncOweRevocationAndCommitForceTransition(t *testing.T, Expiry: uint32(10), ID: 1, } - _, err = bobChannel.AddHTLC(bobHtlc[1], nil) - require.NoError(t, err, "unable to add bob's htlc") - _, err = aliceChannel.ReceiveHTLC(bobHtlc[1]) - require.NoError(t, err, "unable to recv bob's htlc") + addAndReceiveHTLC(t, bobChannel, aliceChannel, bobHtlc[1], nil) // Bob signs the new state update, and sends the signature to Alice. bobNewCommit, err := bobChannel.SignNextCommitment() @@ -4366,14 +4310,7 @@ func TestChanSyncFailure(t *testing.T) { } index++ - _, err := bobChannel.AddHTLC(bobHtlc, nil) - if err != nil { - t.Fatalf("unable to add bob's htlc: %v", err) - } - _, err = aliceChannel.ReceiveHTLC(bobHtlc) - if err != nil { - t.Fatalf("unable to recv bob's htlc: %v", err) - } + addAndReceiveHTLC(t, bobChannel, aliceChannel, bobHtlc, nil) err = ForceStateTransition(bobChannel, aliceChannel) if err != nil { t.Fatalf("unable to complete bob's state "+ @@ -4399,14 +4336,7 @@ func TestChanSyncFailure(t *testing.T) { } index++ - _, err := bobChannel.AddHTLC(bobHtlc, nil) - if err != nil { - t.Fatalf("unable to add bob's htlc: %v", err) - } - _, err = aliceChannel.ReceiveHTLC(bobHtlc) - if err != nil { - t.Fatalf("unable to recv bob's htlc: %v", err) - } + addAndReceiveHTLC(t, bobChannel, aliceChannel, bobHtlc, nil) aliceNewCommit, err := aliceChannel.SignNextCommitment() if err != nil { @@ -4749,12 +4679,7 @@ func TestChannelRetransmissionFeeUpdate(t *testing.T) { Amount: lnwire.NewMSatFromSatoshis(20000), Expiry: uint32(10), } - if _, err := bobChannel.AddHTLC(bobHtlc, nil); err != nil { - t.Fatalf("unable to add bob's htlc: %v", err) - } - if _, err := aliceChannel.ReceiveHTLC(bobHtlc); err != nil { - t.Fatalf("unable to recv bob's htlc: %v", err) - } + addAndReceiveHTLC(t, bobChannel, aliceChannel, bobHtlc, nil) if err := ForceStateTransition(bobChannel, aliceChannel); err != nil { t.Fatalf("unable to complete bob's state transition: %v", err) } @@ -4826,12 +4751,7 @@ func TestFeeUpdateOldDiskFormat(t *testing.T) { var htlcs []*lnwire.UpdateAddHTLC for i := 0; i < numHTLCs; i++ { htlc, _ := createHTLC(i, htlcAmt) - if _, err := aliceChannel.AddHTLC(htlc, nil); err != nil { - t.Fatalf("unable to add htlc: %v", err) - } - if _, err := bobChannel.ReceiveHTLC(htlc); err != nil { - t.Fatalf("unable to recv htlc: %v", err) - } + addAndReceiveHTLC(t, aliceChannel, bobChannel, htlc, nil) htlcs = append(htlcs, htlc) if i%5 != 0 { @@ -4940,12 +4860,7 @@ func TestFeeUpdateOldDiskFormat(t *testing.T) { // Finally, to trigger a compactLogs execution, we'll add a new HTLC, // then force a state transition. htlc, _ := createHTLC(numHTLCs, htlcAmt) - if _, err := aliceChannel.AddHTLC(htlc, nil); err != nil { - t.Fatalf("unable to add htlc: %v", err) - } - if _, err := bobChannel.ReceiveHTLC(htlc); err != nil { - t.Fatalf("unable to recv htlc: %v", err) - } + addAndReceiveHTLC(t, aliceChannel, bobChannel, htlc, nil) if err := ForceStateTransition(aliceChannel, bobChannel); err != nil { t.Fatalf("unable to complete bob's state transition: %v", err) } @@ -5039,12 +4954,7 @@ func TestChanSyncInvalidLastSecret(t *testing.T) { Amount: htlcAmt, Expiry: uint32(5), } - if _, err := aliceChannel.AddHTLC(htlc, nil); err != nil { - t.Fatalf("unable to add htlc: %v", err) - } - if _, err := bobChannel.ReceiveHTLC(htlc); err != nil { - t.Fatalf("unable to recv htlc: %v", err) - } + addAndReceiveHTLC(t, aliceChannel, bobChannel, htlc, nil) // Then we'll initiate a state transition to lock in this new HTLC. if err := ForceStateTransition(aliceChannel, bobChannel); err != nil { @@ -5166,12 +5076,7 @@ func TestChanAvailableBandwidth(t *testing.T) { alicePreimages := make([][32]byte, numHtlcs) for i := 0; i < numHtlcs; i++ { htlc, preImage := createHTLC(i, dustAmt) - if _, err := aliceChannel.AddHTLC(htlc, nil); err != nil { - t.Fatalf("unable to add htlc: %v", err) - } - if _, err := bobChannel.ReceiveHTLC(htlc); err != nil { - t.Fatalf("unable to recv htlc: %v", err) - } + addAndReceiveHTLC(t, aliceChannel, bobChannel, htlc, nil) alicePreimages[i] = preImage } @@ -5184,12 +5089,7 @@ func TestChanAvailableBandwidth(t *testing.T) { htlcAmt := lnwire.NewMSatFromSatoshis(30000) for i := 0; i < numHtlcs; i++ { htlc, preImage := createHTLC(numHtlcs+i, htlcAmt) - if _, err := aliceChannel.AddHTLC(htlc, nil); err != nil { - t.Fatalf("unable to add htlc: %v", err) - } - if _, err := bobChannel.ReceiveHTLC(htlc); err != nil { - t.Fatalf("unable to recv htlc: %v", err) - } + addAndReceiveHTLC(t, aliceChannel, bobChannel, htlc, nil) alicePreimages = append(alicePreimages, preImage) } @@ -5458,13 +5358,7 @@ func TestChanCommitWeightDustHtlcs(t *testing.T) { t.Helper() htlc, preImage := createHTLC(int(htlcIndex), htlcAmt) - if _, err := aliceChannel.AddHTLC(htlc, nil); err != nil { - t.Fatalf("unable to add htlc: %v", err) - } - if _, err := bobChannel.ReceiveHTLC(htlc); err != nil { - t.Fatalf("unable to recv htlc: %v", err) - } - + addAndReceiveHTLC(t, aliceChannel, bobChannel, htlc, nil) if err := ForceStateTransition(aliceChannel, bobChannel); err != nil { t.Fatalf("unable to complete alice's state "+ "transition: %v", err) @@ -5599,19 +5493,10 @@ func TestLockedInHtlcForwardingSkipAfterRestart(t *testing.T) { // a state transition. var htlcAmt lnwire.MilliSatoshi = 100000 htlc, _ := createHTLC(0, htlcAmt) - if _, err := aliceChannel.AddHTLC(htlc, nil); err != nil { - t.Fatalf("unable to add htlc: %v", err) - } - if _, err := bobChannel.ReceiveHTLC(htlc); err != nil { - t.Fatalf("unable to recv htlc: %v", err) - } + addAndReceiveHTLC(t, aliceChannel, bobChannel, htlc, nil) + htlc2, _ := createHTLC(1, htlcAmt) - if _, err := aliceChannel.AddHTLC(htlc2, nil); err != nil { - t.Fatalf("unable to add htlc2: %v", err) - } - if _, err := bobChannel.ReceiveHTLC(htlc2); err != nil { - t.Fatalf("unable to recv htlc2: %v", err) - } + addAndReceiveHTLC(t, aliceChannel, bobChannel, htlc2, nil) // We'll now manually initiate a state transition between Alice and // bob. @@ -5917,12 +5802,7 @@ func TestInvalidCommitSigError(t *testing.T) { // Alice to Bob. var htlcAmt lnwire.MilliSatoshi = 100000 htlc, _ := createHTLC(0, htlcAmt) - if _, err := aliceChannel.AddHTLC(htlc, nil); err != nil { - t.Fatalf("unable to add htlc: %v", err) - } - if _, err := bobChannel.ReceiveHTLC(htlc); err != nil { - t.Fatalf("unable to recv htlc: %v", err) - } + addAndReceiveHTLC(t, aliceChannel, bobChannel, htlc, nil) // Alice will now attempt to initiate a state transition. aliceNewCommit, err := aliceChannel.SignNextCommitment() @@ -5967,19 +5847,9 @@ func TestChannelUnilateralCloseHtlcResolution(t *testing.T) { // initiating enough state transitions to lock both of them in. htlcAmount := lnwire.NewMSatFromSatoshis(20000) htlcAlice, _ := createHTLC(0, htlcAmount) - if _, err := aliceChannel.AddHTLC(htlcAlice, nil); err != nil { - t.Fatalf("alice unable to add htlc: %v", err) - } - if _, err := bobChannel.ReceiveHTLC(htlcAlice); err != nil { - t.Fatalf("bob unable to recv add htlc: %v", err) - } + addAndReceiveHTLC(t, aliceChannel, bobChannel, htlcAlice, nil) htlcBob, preimageBob := createHTLC(0, htlcAmount) - if _, err := bobChannel.AddHTLC(htlcBob, nil); err != nil { - t.Fatalf("bob unable to add htlc: %v", err) - } - if _, err := aliceChannel.ReceiveHTLC(htlcBob); err != nil { - t.Fatalf("alice unable to recv add htlc: %v", err) - } + addAndReceiveHTLC(t, bobChannel, aliceChannel, htlcBob, nil) if err := ForceStateTransition(aliceChannel, bobChannel); err != nil { t.Fatalf("Can't update the channel state: %v", err) } @@ -6124,12 +5994,7 @@ func TestChannelUnilateralClosePendingCommit(t *testing.T) { // create a new state transition. htlcAmount := lnwire.NewMSatFromSatoshis(20000) htlcAlice, _ := createHTLC(0, htlcAmount) - if _, err := aliceChannel.AddHTLC(htlcAlice, nil); err != nil { - t.Fatalf("alice unable to add htlc: %v", err) - } - if _, err := bobChannel.ReceiveHTLC(htlcAlice); err != nil { - t.Fatalf("bob unable to recv add htlc: %v", err) - } + addAndReceiveHTLC(t, aliceChannel, bobChannel, htlcAlice, nil) // With the HTLC added, we'll now manually initiate a state transition // from Alice to Bob. @@ -6325,12 +6190,7 @@ func TestMaxAcceptedHTLCs(t *testing.T) { // Send the maximum allowed number of HTLCs. for i := 0; i < numHTLCs; i++ { htlc, _ := createHTLC(i, htlcAmt) - if _, err := aliceChannel.AddHTLC(htlc, nil); err != nil { - t.Fatalf("unable to add htlc: %v", err) - } - if _, err := bobChannel.ReceiveHTLC(htlc); err != nil { - t.Fatalf("unable to recv htlc: %v", err) - } + addAndReceiveHTLC(t, aliceChannel, bobChannel, htlc, nil) // Just assign htlcID to the last received HTLC. htlcID = htlc.ID @@ -6369,12 +6229,7 @@ func TestMaxAcceptedHTLCs(t *testing.T) { // failed. We use numHTLCs here since the previous AddHTLC with this index // failed. htlc, _ = createHTLC(numHTLCs, htlcAmt) - if _, err := aliceChannel.AddHTLC(htlc, nil); err != nil { - t.Fatalf("unable to add htlc: %v", err) - } - if _, err := bobChannel.ReceiveHTLC(htlc); err != nil { - t.Fatalf("unable to recv htlc: %v", err) - } + addAndReceiveHTLC(t, aliceChannel, bobChannel, htlc, nil) // Add a commitment to Bob's commitment chain. aliceNewCommit, err := aliceChannel.SignNextCommitment() @@ -6450,12 +6305,7 @@ func TestMaxAsynchronousHtlcs(t *testing.T) { // Send the maximum allowed number of HTLCs minus one. for i := 0; i < numHTLCs-1; i++ { htlc, _ := createHTLC(i, htlcAmt) - if _, err := aliceChannel.AddHTLC(htlc, nil); err != nil { - t.Fatalf("unable to add htlc: %v", err) - } - if _, err := bobChannel.ReceiveHTLC(htlc); err != nil { - t.Fatalf("unable to recv htlc: %v", err) - } + addAndReceiveHTLC(t, aliceChannel, bobChannel, htlc, nil) // Just assign htlcID to the last received HTLC. htlcID = htlc.ID @@ -6467,12 +6317,7 @@ func TestMaxAsynchronousHtlcs(t *testing.T) { // Send an HTLC to Bob so that Bob's commitment transaction is full. htlc, _ := createHTLC(numHTLCs-1, htlcAmt) - if _, err := aliceChannel.AddHTLC(htlc, nil); err != nil { - t.Fatalf("unable to add htlc: %v", err) - } - if _, err := bobChannel.ReceiveHTLC(htlc); err != nil { - t.Fatalf("unable to recv htlc: %v", err) - } + addAndReceiveHTLC(t, aliceChannel, bobChannel, htlc, nil) // Fail back an HTLC and sign a commitment as in steps 1 & 2. err = bobChannel.FailHTLC(htlcID, []byte{}, nil, nil, nil) @@ -6511,12 +6356,7 @@ func TestMaxAsynchronousHtlcs(t *testing.T) { // Send the final Add which should succeed as in step 6. htlc, _ = createHTLC(numHTLCs, htlcAmt) - if _, err := aliceChannel.AddHTLC(htlc, nil); err != nil { - t.Fatalf("unable to add htlc: %v", err) - } - if _, err := bobChannel.ReceiveHTLC(htlc); err != nil { - t.Fatalf("unable to recv htlc: %v", err) - } + addAndReceiveHTLC(t, aliceChannel, bobChannel, htlc, nil) // Receiving the commitment should succeed as in step 7 since space was // made. @@ -6557,12 +6397,7 @@ func TestMaxPendingAmount(t *testing.T) { htlcAmt := lnwire.NewMSatFromSatoshis(1.5 * btcutil.SatoshiPerBitcoin) for i := 0; i < numHTLCs; i++ { htlc, _ := createHTLC(i, htlcAmt) - if _, err := aliceChannel.AddHTLC(htlc, nil); err != nil { - t.Fatalf("unable to add htlc: %v", err) - } - if _, err := bobChannel.ReceiveHTLC(htlc); err != nil { - t.Fatalf("unable to recv htlc: %v", err) - } + addAndReceiveHTLC(t, aliceChannel, bobChannel, htlc, nil) } // We finally add one more HTLC of 0.1 BTC to Alice's commitment. This @@ -6664,12 +6499,7 @@ func TestChanReserve(t *testing.T) { htlcAmt := lnwire.NewMSatFromSatoshis(0.5 * btcutil.SatoshiPerBitcoin) htlc, _ := createHTLC(aliceIndex, htlcAmt) aliceIndex++ - if _, err := aliceChannel.AddHTLC(htlc, nil); err != nil { - t.Fatalf("unable to add htlc: %v", err) - } - if _, err := bobChannel.ReceiveHTLC(htlc); err != nil { - t.Fatalf("unable to recv htlc: %v", err) - } + addAndReceiveHTLC(t, aliceChannel, bobChannel, htlc, nil) // Force a state transition, making sure this HTLC is considered valid // even though the channel reserves are not met. @@ -6716,12 +6546,7 @@ func TestChanReserve(t *testing.T) { // The first HTLC should successfully be sent. htlc, _ = createHTLC(aliceIndex, htlcAmt) aliceIndex++ - if _, err := aliceChannel.AddHTLC(htlc, nil); err != nil { - t.Fatalf("unable to add htlc: %v", err) - } - if _, err := bobChannel.ReceiveHTLC(htlc); err != nil { - t.Fatalf("unable to recv htlc: %v", err) - } + addAndReceiveHTLC(t, aliceChannel, bobChannel, htlc, nil) // Add a second HTLC of 1 BTC. This should fail because it will take // Alice's balance all the way down to her channel reserve, but since @@ -6787,12 +6612,7 @@ func TestChanReserve(t *testing.T) { htlcAmt = lnwire.NewMSatFromSatoshis(1 * btcutil.SatoshiPerBitcoin) htlc, _ = createHTLC(bobIndex, htlcAmt) bobIndex++ - if _, err := bobChannel.AddHTLC(htlc, nil); err != nil { - t.Fatalf("unable to add htlc: %v", err) - } - if _, err := aliceChannel.ReceiveHTLC(htlc); err != nil { - t.Fatalf("unable to recv htlc: %v", err) - } + addAndReceiveHTLC(t, bobChannel, aliceChannel, htlc, nil) // Do a last state transition, which should succeed. if err := ForceStateTransition(bobChannel, aliceChannel); err != nil { @@ -6920,12 +6740,7 @@ func TestMinHTLC(t *testing.T) { // ErrBelowMinHTLC. htlcAmt := lnwire.NewMSatFromSatoshis(0.5 * btcutil.SatoshiPerBitcoin) htlc, _ := createHTLC(0, htlcAmt) - if _, err := aliceChannel.AddHTLC(htlc, nil); err != nil { - t.Fatalf("unable to add htlc: %v", err) - } - if _, err := bobChannel.ReceiveHTLC(htlc); err != nil { - t.Fatalf("unable to recv htlc: %v", err) - } + addAndReceiveHTLC(t, aliceChannel, bobChannel, htlc, nil) // We add an HTLC below the min value, this should result in // an ErrBelowMinHTLC error. @@ -7167,12 +6982,7 @@ func TestChannelRestoreUpdateLogs(t *testing.T) { // Bob's commit, but not on Alice's. htlcAmount := lnwire.NewMSatFromSatoshis(20000) htlcAlice, _ := createHTLC(0, htlcAmount) - if _, err := aliceChannel.AddHTLC(htlcAlice, nil); err != nil { - t.Fatalf("alice unable to add htlc: %v", err) - } - if _, err := bobChannel.ReceiveHTLC(htlcAlice); err != nil { - t.Fatalf("bob unable to recv add htlc: %v", err) - } + addAndReceiveHTLC(t, aliceChannel, bobChannel, htlcAlice, nil) // Let Alice sign a new state, which will include the HTLC just sent. aliceNewCommit, err := aliceChannel.SignNextCommitment() @@ -7416,11 +7226,7 @@ func TestDuplicateFailRejection(t *testing.T) { // parties. htlcAmount := lnwire.NewMSatFromSatoshis(20000) htlcAlice, _ := createHTLC(0, htlcAmount) - if _, err := aliceChannel.AddHTLC(htlcAlice, nil); err != nil { - t.Fatalf("alice unable to add htlc: %v", err) - } - _, err = bobChannel.ReceiveHTLC(htlcAlice) - require.NoError(t, err, "unable to recv htlc") + addAndReceiveHTLC(t, aliceChannel, bobChannel, htlcAlice, nil) if err := ForceStateTransition(aliceChannel, bobChannel); err != nil { t.Fatalf("unable to complete state update: %v", err) @@ -7483,11 +7289,7 @@ func TestDuplicateSettleRejection(t *testing.T) { // parties. htlcAmount := lnwire.NewMSatFromSatoshis(20000) htlcAlice, alicePreimage := createHTLC(0, htlcAmount) - if _, err := aliceChannel.AddHTLC(htlcAlice, nil); err != nil { - t.Fatalf("alice unable to add htlc: %v", err) - } - _, err = bobChannel.ReceiveHTLC(htlcAlice) - require.NoError(t, err, "unable to recv htlc") + addAndReceiveHTLC(t, aliceChannel, bobChannel, htlcAlice, nil) if err := ForceStateTransition(aliceChannel, bobChannel); err != nil { t.Fatalf("unable to complete state update: %v", err) @@ -7591,12 +7393,7 @@ func TestChannelRestoreCommitHeight(t *testing.T) { // We'll send an HtLC from Alice to Bob. htlcAmount := lnwire.NewMSatFromSatoshis(100000000) htlcAlice, _ := createHTLC(0, htlcAmount) - if _, err := aliceChannel.AddHTLC(htlcAlice, nil); err != nil { - t.Fatalf("alice unable to add htlc: %v", err) - } - if _, err := bobChannel.ReceiveHTLC(htlcAlice); err != nil { - t.Fatalf("bob unable to recv add htlc: %v", err) - } + addAndReceiveHTLC(t, aliceChannel, bobChannel, htlcAlice, nil) // Let Alice sign a new state, which will include the HTLC just sent. aliceNewCommit, err := aliceChannel.SignNextCommitment() @@ -7660,12 +7457,7 @@ func TestChannelRestoreCommitHeight(t *testing.T) { // existing HTLCs (the HTLC with index 0) keep getting the add heights // restored properly. htlcAlice, _ = createHTLC(1, htlcAmount) - if _, err := aliceChannel.AddHTLC(htlcAlice, nil); err != nil { - t.Fatalf("alice unable to add htlc: %v", err) - } - if _, err := bobChannel.ReceiveHTLC(htlcAlice); err != nil { - t.Fatalf("bob unable to recv add htlc: %v", err) - } + addAndReceiveHTLC(t, aliceChannel, bobChannel, htlcAlice, nil) // Send a new signature from Alice to Bob, making Alice have a pending // remote commitment. @@ -9597,10 +9389,7 @@ func TestChannelUnsignedAckedFailure(t *testing.T) { htlc, _ := createHTLC(0, lnwire.MilliSatoshi(500000)) // -----add-----> - _, err = aliceChannel.AddHTLC(htlc, nil) - require.NoError(t, err) - _, err = bobChannel.ReceiveHTLC(htlc) - require.NoError(t, err) + addAndReceiveHTLC(t, aliceChannel, bobChannel, htlc, nil) // Force a state transition to lock in this add on both commitments. // -----sig-----> @@ -9660,10 +9449,7 @@ func TestChannelUnsignedAckedFailure(t *testing.T) { htlc2, _ := createHTLC(0, lnwire.MilliSatoshi(500000)) // <----add------ - _, err = bobChannel.AddHTLC(htlc2, nil) - require.NoError(t, err) - _, err = newAliceChannel.ReceiveHTLC(htlc2) - require.NoError(t, err) + addAndReceiveHTLC(t, bobChannel, newAliceChannel, htlc2, nil) // Bob sends the final signature to Alice and Alice should not // reject it, given that we properly restore the unsigned acked @@ -9707,10 +9493,7 @@ func TestChannelLocalUnsignedUpdatesFailure(t *testing.T) { htlc, _ := createHTLC(0, lnwire.MilliSatoshi(500000)) // <----add----- - _, err = bobChannel.AddHTLC(htlc, nil) - require.NoError(t, err) - _, err = aliceChannel.ReceiveHTLC(htlc) - require.NoError(t, err) + addAndReceiveHTLC(t, bobChannel, aliceChannel, htlc, nil) // Force a state transition to lock in this add on both commitments. // <----sig----- @@ -9794,10 +9577,7 @@ func TestChannelSignedAckRegression(t *testing.T) { htlc, preimage := createHTLC(0, lnwire.MilliSatoshi(5000000)) // <----add------ - _, err = bobChannel.AddHTLC(htlc, nil) - require.NoError(t, err) - _, err = aliceChannel.ReceiveHTLC(htlc) - require.NoError(t, err) + addAndReceiveHTLC(t, bobChannel, aliceChannel, htlc, nil) // Force a state transition to lock in the HTLC. // <----sig------ @@ -9836,10 +9616,7 @@ func TestChannelSignedAckRegression(t *testing.T) { htlc2, _ := createHTLC(0, lnwire.MilliSatoshi(5000000)) // -----add----> - _, err = aliceChannel.AddHTLC(htlc2, nil) - require.NoError(t, err) - _, err = bobChannel.ReceiveHTLC(htlc2) - require.NoError(t, err) + addAndReceiveHTLC(t, aliceChannel, bobChannel, htlc2, nil) // -----sig----> aliceNewCommit, err = aliceChannel.SignNextCommitment() @@ -9936,10 +9713,7 @@ func TestIsChannelClean(t *testing.T) { // sends an htlc. // ---add---> htlc, preimage := createHTLC(0, lnwire.MilliSatoshi(5000000)) - _, err = aliceChannel.AddHTLC(htlc, nil) - require.NoError(t, err) - _, err = bobChannel.ReceiveHTLC(htlc) - require.NoError(t, err) + addAndReceiveHTLC(t, aliceChannel, bobChannel, htlc, nil) assertCleanOrDirty(false, aliceChannel, bobChannel, t) // Assert that the channel remains dirty until the HTLC is completely @@ -10117,10 +9891,7 @@ func testGetDustSum(t *testing.T, chantype channeldb.ChannelType) { htlc1Amt := lnwire.MilliSatoshi(700_000) htlc1, preimage1 := createHTLC(0, htlc1Amt) - _, err = bobChannel.AddHTLC(htlc1, nil) - require.NoError(t, err) - _, err = aliceChannel.ReceiveHTLC(htlc1) - require.NoError(t, err) + addAndReceiveHTLC(t, bobChannel, aliceChannel, htlc1, nil) // Assert that GetDustSum from Alice's perspective does not consider // the HTLC dust on her commitment, but does on Bob's commitment. @@ -10160,10 +9931,7 @@ func testGetDustSum(t *testing.T, chantype channeldb.ChannelType) { htlc2Amt := lnwire.MilliSatoshi(100_000) htlc2, _ := createHTLC(0, htlc2Amt) - _, err = aliceChannel.AddHTLC(htlc2, nil) - require.NoError(t, err) - _, err = bobChannel.ReceiveHTLC(htlc2) - require.NoError(t, err) + addAndReceiveHTLC(t, aliceChannel, bobChannel, htlc2, nil) // Assert that GetDustSum from Alice's perspective includes the new // HTLC as dust on both commitments. @@ -10211,10 +9979,7 @@ func testGetDustSum(t *testing.T, chantype channeldb.ChannelType) { htlc3Amt := lnwire.MilliSatoshi(400_000) htlc3, _ := createHTLC(1, htlc3Amt) - _, err = aliceChannel.AddHTLC(htlc3, nil) - require.NoError(t, err) - _, err = bobChannel.ReceiveHTLC(htlc3) - require.NoError(t, err) + addAndReceiveHTLC(t, aliceChannel, bobChannel, htlc3, nil) // Assert that this new HTLC is not counted on Alice's local commitment // in the dust sum. Bob's commitment should count it. @@ -11216,11 +10981,7 @@ func TestAsynchronousSendingWithFeeBuffer(t *testing.T) { aliceChannel.channelState.LocalChanCfg.DustLimit + htlcFee, ) htlc3, _ := createHTLC(1, htlcAmt3) - _, err = bobChannel.AddHTLC(htlc3, nil) - require.NoError(t, err) - - _, err = aliceChannel.ReceiveHTLC(htlc3) - require.NoError(t, err) + addAndReceiveHTLC(t, bobChannel, aliceChannel, htlc3, nil) err = ForceStateTransition(bobChannel, aliceChannel) require.NoError(t, err) @@ -11326,10 +11087,7 @@ func TestEnforceFeeBuffer(t *testing.T) { // --------------- |-----rev------> htlc1, _ := createHTLC(0, lnwire.NewMSatFromSatoshis(htlcAmt1)) - _, err = aliceChannel.AddHTLC(htlc1, nil) - require.NoError(t, err) - _, err = bobChannel.ReceiveHTLC(htlc1) - require.NoError(t, err) + addAndReceiveHTLC(t, aliceChannel, bobChannel, htlc1, nil) err = ForceStateTransition(aliceChannel, bobChannel) require.NoError(t, err) @@ -11343,10 +11101,7 @@ func TestEnforceFeeBuffer(t *testing.T) { htlcAmt2 := lnwire.NewMSatFromSatoshis(btcutil.SatoshiPerBitcent) htlc2, _ := createHTLC(0, htlcAmt2) - _, err = bobChannel.AddHTLC(htlc2, nil) - require.NoError(t, err) - _, err = aliceChannel.ReceiveHTLC(htlc2) - require.NoError(t, err) + addAndReceiveHTLC(t, bobChannel, aliceChannel, htlc2, nil) err = ForceStateTransition(bobChannel, aliceChannel) require.NoError(t, err) @@ -11366,10 +11121,7 @@ func TestEnforceFeeBuffer(t *testing.T) { // <----rev------- |--------------- htlc4, _ := createHTLC(1, htlcAmt2) - _, err = bobChannel.AddHTLC(htlc4, nil) - require.NoError(t, err) - _, err = aliceChannel.ReceiveHTLC(htlc4) - require.NoError(t, err) + addAndReceiveHTLC(t, bobChannel, aliceChannel, htlc4, nil) err = ForceStateTransition(bobChannel, aliceChannel) require.NoError(t, err) @@ -11409,11 +11161,7 @@ func TestBlindingPointPersistence(t *testing.T) { tlv.NewPrimitiveRecord[lnwire.BlindingPointTlvType](blinding), ) - _, err = aliceChannel.AddHTLC(htlc, nil) - - require.NoError(t, err) - _, err = bobChannel.ReceiveHTLC(htlc) - require.NoError(t, err) + addAndReceiveHTLC(t, aliceChannel, bobChannel, htlc, nil) // Now, Alice will send a new commitment to Bob, which will persist our // pending HTLC to disk. From ed5d748a9f2e989fa5257d6af945f3d67c9c0090 Mon Sep 17 00:00:00 2001 From: Jonathan Harvey-Buschel Date: Thu, 17 Oct 2024 13:38:29 +0200 Subject: [PATCH 185/218] lnwallet: test aux signer shutdown handling --- lnwallet/channel_test.go | 80 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) diff --git a/lnwallet/channel_test.go b/lnwallet/channel_test.go index a8ac1f0b78..9ea4aa4586 100644 --- a/lnwallet/channel_test.go +++ b/lnwallet/channel_test.go @@ -4,6 +4,7 @@ import ( "bytes" crand "crypto/rand" "crypto/sha256" + "errors" "fmt" "math/rand" "reflect" @@ -3496,6 +3497,85 @@ func TestChanSyncOweCommitmentAuxSigner(t *testing.T) { require.NotEmpty(t, sigMsg.CustomRecords) } +// TestAuxSignerShutdown tests that the channel state machine gracefully handles +// a failure of the aux signer when signing a new commitment. +func TestAuxSignerShutdown(t *testing.T) { + t.Parallel() + + // We'll kick off the test by creating our channels which both are + // loaded with 5 BTC each. + aliceChannel, bobChannel, err := CreateTestChannels( + t, channeldb.SingleFunderTweaklessBit, + ) + require.NoError(t, err, "unable to create test channels") + + auxSignerShutdownErr := errors.New("aux signer shutdown") + + // We know that aux sig jobs will be checked in SignNextCommitment() in + // ascending output index order. So we'll fail on the first job that is + // out of order, i.e. with an output index greater than its position in + // the submitted jobs slice. If the jobs are ordered, we'll fail on the + // job that is at the middle of the submitted job slice. + failAuxSigJob := func(jobs []AuxSigJob) { + for idx, sigJob := range jobs { + // Simulate a clean shutdown of the aux signer and send + // an error. Skip all remaining jobs. + isMiddleJob := idx == len(jobs)/2 + if int(sigJob.OutputIndex) > idx || isMiddleJob { + sigJob.Resp <- AuxSigJobResp{ + Err: auxSignerShutdownErr, + } + + return + } + + // If the job is 'in order', send a response with no + // error. + sigJob.Resp <- AuxSigJobResp{} + } + } + + auxSigner := NewAuxSignerMock(failAuxSigJob) + aliceChannel.auxSigner = fn.Some[AuxSigner](auxSigner) + + // Each HTLC amount is 0.01 BTC. + htlcAmt := lnwire.NewMSatFromSatoshis(0.01 * btcutil.SatoshiPerBitcoin) + + // Create enough HTLCs to create multiple sig jobs (one job per HTLC). + const numHTLCs = 24 + + // Send the specified number of HTLCs. + for i := 0; i < numHTLCs; i++ { + htlc, _ := createHTLC(i, htlcAmt) + addAndReceiveHTLC(t, aliceChannel, bobChannel, htlc, nil) + } + + // We'll set up the mock aux signer to expect calls to PackSigs and also + // SubmitSecondLevelSigBatch. The direct return values for this mock aux + // signer are nil. The expected error comes from the sig jobs being + // passed to failAuxSigJob above, which mimics a faulty aux signer. + var sigBlobBuf bytes.Buffer + sigBlob := testSigBlob{ + BlobInt: tlv.NewPrimitiveRecord[tlv.TlvType65634, uint16](5), + } + tlvStream, err := tlv.NewStream(sigBlob.BlobInt.Record()) + require.NoError(t, err, "unable to create tlv stream") + require.NoError(t, tlvStream.Encode(&sigBlobBuf)) + + auxSigner.On( + "SubmitSecondLevelSigBatch", mock.Anything, mock.Anything, + mock.Anything, + ).Return(nil).Twice() + auxSigner.On( + "PackSigs", mock.Anything, + ).Return( + fn.Some(sigBlobBuf.Bytes()), nil, + ) + + _, err = aliceChannel.SignNextCommitment() + require.ErrorIs(t, err, auxSignerShutdownErr) +} + func testChanSyncOweCommitmentPendingRemote(t *testing.T, chanType channeldb.ChannelType, ) { From afb7532f1712926d6b22d63c266365c1ddfbc2e0 Mon Sep 17 00:00:00 2001 From: Jonathan Harvey-Buschel Date: Thu, 17 Oct 2024 13:38:30 +0200 Subject: [PATCH 186/218] lnwallet: sort sig jobs before submission --- lnwallet/channel.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lnwallet/channel.go b/lnwallet/channel.go index f3e0769506..20a8b8c945 100644 --- a/lnwallet/channel.go +++ b/lnwallet/channel.go @@ -2,6 +2,7 @@ package lnwallet import ( "bytes" + "cmp" "crypto/sha256" "errors" "fmt" @@ -4059,10 +4060,10 @@ func (lc *LightningChannel) SignNextCommitment() (*NewCommitState, error) { // order as they appear on the commitment transaction after BIP 69 // sorting. slices.SortFunc(sigBatch, func(i, j SignJob) int { - return int(i.OutputIndex - j.OutputIndex) + return cmp.Compare(i.OutputIndex, j.OutputIndex) }) slices.SortFunc(auxSigBatch, func(i, j AuxSigJob) int { - return int(i.OutputIndex - j.OutputIndex) + return cmp.Compare(i.OutputIndex, j.OutputIndex) }) lc.sigPool.SubmitSignBatch(sigBatch) From 753301cf38c54b30e8587211aa45e2fdf25f10db Mon Sep 17 00:00:00 2001 From: Jonathan Harvey-Buschel Date: Thu, 17 Oct 2024 13:38:31 +0200 Subject: [PATCH 187/218] htlcswitch: pass quit chans as unidirectional This is a requirement for replacing the quit channel with a Context. The Done() channel of a Context is always recv-only, so all users of that channel must not expect a bidirectional channel. --- htlcswitch/interceptable_switch.go | 6 ++--- htlcswitch/link.go | 2 +- htlcswitch/link_test.go | 40 ++++++++++++++++++------------ htlcswitch/mailbox.go | 4 +-- htlcswitch/mailbox_test.go | 4 +-- htlcswitch/switch.go | 4 +-- htlcswitch/test_utils.go | 18 ++++++++------ 7 files changed, 45 insertions(+), 33 deletions(-) diff --git a/htlcswitch/interceptable_switch.go b/htlcswitch/interceptable_switch.go index e06163d486..71302bf075 100644 --- a/htlcswitch/interceptable_switch.go +++ b/htlcswitch/interceptable_switch.go @@ -95,7 +95,7 @@ type InterceptableSwitch struct { type interceptedPackets struct { packets []*htlcPacket - linkQuit chan struct{} + linkQuit <-chan struct{} isReplay bool } @@ -465,8 +465,8 @@ func (s *InterceptableSwitch) Resolve(res *FwdResolution) error { // interceptor. If the interceptor signals the resume action, the htlcs are // forwarded to the switch. The link's quit signal should be provided to allow // cancellation of forwarding during link shutdown. -func (s *InterceptableSwitch) ForwardPackets(linkQuit chan struct{}, isReplay bool, - packets ...*htlcPacket) error { +func (s *InterceptableSwitch) ForwardPackets(linkQuit <-chan struct{}, + isReplay bool, packets ...*htlcPacket) error { // Synchronize with the main event loop. This should be light in the // case where there is no interceptor. diff --git a/htlcswitch/link.go b/htlcswitch/link.go index 0fb970d59b..87384a9da3 100644 --- a/htlcswitch/link.go +++ b/htlcswitch/link.go @@ -101,7 +101,7 @@ type ChannelLinkConfig struct { // switch. The function returns and error in case it fails to send one or // more packets. The link's quit signal should be provided to allow // cancellation of forwarding during link shutdown. - ForwardPackets func(chan struct{}, bool, ...*htlcPacket) error + ForwardPackets func(<-chan struct{}, bool, ...*htlcPacket) error // DecodeHopIterators facilitates batched decoding of HTLC Sphinx onion // blobs, which are then used to inform how to forward an HTLC. diff --git a/htlcswitch/link_test.go b/htlcswitch/link_test.go index 02fbd3ba0e..be20eeb220 100644 --- a/htlcswitch/link_test.go +++ b/htlcswitch/link_test.go @@ -2197,17 +2197,21 @@ func newSingleLinkTestHarness(t *testing.T, chanAmt, return nil } + forwardPackets := func(linkQuit <-chan struct{}, _ bool, + packets ...*htlcPacket) error { + + return aliceSwitch.ForwardPackets(linkQuit, packets...) + } + // Instantiate with a long interval, so that we can precisely control // the firing via force feeding. bticker := ticker.NewForce(time.Hour) aliceCfg := ChannelLinkConfig{ - FwrdingPolicy: globalPolicy, - Peer: alicePeer, - BestHeight: aliceSwitch.BestHeight, - Circuits: aliceSwitch.CircuitModifier(), - ForwardPackets: func(linkQuit chan struct{}, _ bool, packets ...*htlcPacket) error { - return aliceSwitch.ForwardPackets(linkQuit, packets...) - }, + FwrdingPolicy: globalPolicy, + Peer: alicePeer, + BestHeight: aliceSwitch.BestHeight, + Circuits: aliceSwitch.CircuitModifier(), + ForwardPackets: forwardPackets, DecodeHopIterators: decoder.DecodeHopIterators, ExtractErrorEncrypter: func(*btcec.PublicKey) ( hop.ErrorEncrypter, lnwire.FailCode) { @@ -4867,17 +4871,21 @@ func (h *persistentLinkHarness) restartLink( return nil } + forwardPackets := func(linkQuit <-chan struct{}, _ bool, + packets ...*htlcPacket) error { + + return h.hSwitch.ForwardPackets(linkQuit, packets...) + } + // Instantiate with a long interval, so that we can precisely control // the firing via force feeding. bticker := ticker.NewForce(time.Hour) aliceCfg := ChannelLinkConfig{ - FwrdingPolicy: globalPolicy, - Peer: alicePeer, - BestHeight: h.hSwitch.BestHeight, - Circuits: h.hSwitch.CircuitModifier(), - ForwardPackets: func(linkQuit chan struct{}, _ bool, packets ...*htlcPacket) error { - return h.hSwitch.ForwardPackets(linkQuit, packets...) - }, + FwrdingPolicy: globalPolicy, + Peer: alicePeer, + BestHeight: h.hSwitch.BestHeight, + Circuits: h.hSwitch.CircuitModifier(), + ForwardPackets: forwardPackets, DecodeHopIterators: decoder.DecodeHopIterators, ExtractErrorEncrypter: func(*btcec.PublicKey) ( hop.ErrorEncrypter, lnwire.FailCode) { @@ -7037,7 +7045,7 @@ func TestPipelineSettle(t *testing.T) { // erroneously forwarded. If the forwardChan is closed before the last // step, then the test will fail. forwardChan := make(chan struct{}) - fwdPkts := func(c chan struct{}, _ bool, hp ...*htlcPacket) error { + fwdPkts := func(c <-chan struct{}, _ bool, hp ...*htlcPacket) error { close(forwardChan) return nil } @@ -7223,7 +7231,7 @@ func TestChannelLinkShortFailureRelay(t *testing.T) { aliceMsgs := mockPeer.sentMsgs switchChan := make(chan *htlcPacket) - coreLink.cfg.ForwardPackets = func(linkQuit chan struct{}, _ bool, + coreLink.cfg.ForwardPackets = func(linkQuit <-chan struct{}, _ bool, packets ...*htlcPacket) error { for _, p := range packets { diff --git a/htlcswitch/mailbox.go b/htlcswitch/mailbox.go index 9b82f8912e..b283825dd9 100644 --- a/htlcswitch/mailbox.go +++ b/htlcswitch/mailbox.go @@ -95,7 +95,7 @@ type mailBoxConfig struct { // forwardPackets send a varidic number of htlcPackets to the switch to // be routed. A quit channel should be provided so that the call can // properly exit during shutdown. - forwardPackets func(chan struct{}, ...*htlcPacket) error + forwardPackets func(<-chan struct{}, ...*htlcPacket) error // clock is a time source for the mailbox. clock clock.Clock @@ -804,7 +804,7 @@ type mailOrchConfig struct { // forwardPackets send a varidic number of htlcPackets to the switch to // be routed. A quit channel should be provided so that the call can // properly exit during shutdown. - forwardPackets func(chan struct{}, ...*htlcPacket) error + forwardPackets func(<-chan struct{}, ...*htlcPacket) error // clock is a time source for the generated mailboxes. clock clock.Clock diff --git a/htlcswitch/mailbox_test.go b/htlcswitch/mailbox_test.go index e48aacfcd6..aa2f1b3ee6 100644 --- a/htlcswitch/mailbox_test.go +++ b/htlcswitch/mailbox_test.go @@ -250,7 +250,7 @@ func newMailboxContext(t *testing.T, startTime time.Time, return ctx } -func (c *mailboxContext) forward(_ chan struct{}, +func (c *mailboxContext) forward(_ <-chan struct{}, pkts ...*htlcPacket) error { for _, pkt := range pkts { @@ -706,7 +706,7 @@ func TestMailOrchestrator(t *testing.T) { // First, we'll create a new instance of our orchestrator. mo := newMailOrchestrator(&mailOrchConfig{ failMailboxUpdate: failMailboxUpdate, - forwardPackets: func(_ chan struct{}, + forwardPackets: func(_ <-chan struct{}, pkts ...*htlcPacket) error { return nil diff --git a/htlcswitch/switch.go b/htlcswitch/switch.go index 01596ade02..35eb4a6ef4 100644 --- a/htlcswitch/switch.go +++ b/htlcswitch/switch.go @@ -671,7 +671,7 @@ func (s *Switch) IsForwardedHTLC(chanID lnwire.ShortChannelID, // given to forward them through the router. The sending link's quit channel is // used to prevent deadlocks when the switch stops a link in the midst of // forwarding. -func (s *Switch) ForwardPackets(linkQuit chan struct{}, +func (s *Switch) ForwardPackets(linkQuit <-chan struct{}, packets ...*htlcPacket) error { var ( @@ -849,7 +849,7 @@ func (s *Switch) logFwdErrs(num *int, wg *sync.WaitGroup, fwdChan chan error) { // receive a shutdown requuest. This method does not wait for a response from // the htlcForwarder before returning. func (s *Switch) routeAsync(packet *htlcPacket, errChan chan error, - linkQuit chan struct{}) error { + linkQuit <-chan struct{}) error { command := &plexPacket{ pkt: packet, diff --git a/htlcswitch/test_utils.go b/htlcswitch/test_utils.go index 1786765fb5..f12dc847a2 100644 --- a/htlcswitch/test_utils.go +++ b/htlcswitch/test_utils.go @@ -1142,15 +1142,19 @@ func (h *hopNetwork) createChannelLink(server, peer *mockServer, return nil } + forwardPackets := func(linkQuit <-chan struct{}, _ bool, + packets ...*htlcPacket) error { + + return server.htlcSwitch.ForwardPackets(linkQuit, packets...) + } + link := NewChannelLink( ChannelLinkConfig{ - BestHeight: server.htlcSwitch.BestHeight, - FwrdingPolicy: h.globalPolicy, - Peer: peer, - Circuits: server.htlcSwitch.CircuitModifier(), - ForwardPackets: func(linkQuit chan struct{}, _ bool, packets ...*htlcPacket) error { - return server.htlcSwitch.ForwardPackets(linkQuit, packets...) - }, + BestHeight: server.htlcSwitch.BestHeight, + FwrdingPolicy: h.globalPolicy, + Peer: peer, + Circuits: server.htlcSwitch.CircuitModifier(), + ForwardPackets: forwardPackets, DecodeHopIterators: decoder.DecodeHopIterators, ExtractErrorEncrypter: func(*btcec.PublicKey) ( hop.ErrorEncrypter, lnwire.FailCode) { From 6feb74137d52b5d86b06d9339a473a83a666373c Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Thu, 17 Oct 2024 13:38:33 +0200 Subject: [PATCH 188/218] mod: bump fn to v1.2.3 --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 28259605c3..57c08f6996 100644 --- a/go.mod +++ b/go.mod @@ -35,7 +35,7 @@ require ( github.com/lightningnetwork/lightning-onion v1.2.1-0.20240712235311-98bd56499dfb github.com/lightningnetwork/lnd/cert v1.2.2 github.com/lightningnetwork/lnd/clock v1.1.1 - github.com/lightningnetwork/lnd/fn v1.2.1 + github.com/lightningnetwork/lnd/fn v1.2.3 github.com/lightningnetwork/lnd/healthcheck v1.2.5 github.com/lightningnetwork/lnd/kvdb v1.4.10 github.com/lightningnetwork/lnd/queue v1.1.1 diff --git a/go.sum b/go.sum index 1677c6a3d2..20739ffbd8 100644 --- a/go.sum +++ b/go.sum @@ -451,8 +451,8 @@ github.com/lightningnetwork/lnd/cert v1.2.2 h1:71YK6hogeJtxSxw2teq3eGeuy4rHGKcFf github.com/lightningnetwork/lnd/cert v1.2.2/go.mod h1:jQmFn/Ez4zhDgq2hnYSw8r35bqGVxViXhX6Cd7HXM6U= github.com/lightningnetwork/lnd/clock v1.1.1 h1:OfR3/zcJd2RhH0RU+zX/77c0ZiOnIMsDIBjgjWdZgA0= github.com/lightningnetwork/lnd/clock v1.1.1/go.mod h1:mGnAhPyjYZQJmebS7aevElXKTFDuO+uNFFfMXK1W8xQ= -github.com/lightningnetwork/lnd/fn v1.2.1 h1:pPsVGrwi9QBwdLJzaEGK33wmiVKOxs/zc8H7+MamFf0= -github.com/lightningnetwork/lnd/fn v1.2.1/go.mod h1:SyFohpVrARPKH3XVAJZlXdVe+IwMYc4OMAvrDY32kw0= +github.com/lightningnetwork/lnd/fn v1.2.3 h1:Q1OrgNSgQynVheBNa16CsKVov1JI5N2AR6G07x9Mles= +github.com/lightningnetwork/lnd/fn v1.2.3/go.mod h1:SyFohpVrARPKH3XVAJZlXdVe+IwMYc4OMAvrDY32kw0= github.com/lightningnetwork/lnd/healthcheck v1.2.5 h1:aTJy5xeBpcWgRtW/PGBDe+LMQEmNm/HQewlQx2jt7OA= github.com/lightningnetwork/lnd/healthcheck v1.2.5/go.mod h1:G7Tst2tVvWo7cx6mSBEToQC5L1XOGxzZTPB29g9Rv2I= github.com/lightningnetwork/lnd/kvdb v1.4.10 h1:vK89IVv1oVH9ubQWU+EmoCQFeVRaC8kfmOrqHbY5zoY= From 8703153c44d98785d02649180c7fe138f5a538da Mon Sep 17 00:00:00 2001 From: Jonathan Harvey-Buschel Date: Thu, 17 Oct 2024 13:38:34 +0200 Subject: [PATCH 189/218] multi: link quit can interrupt commitment signing In this commit, we make sig job handling when singing a next commitment non-blocking by allowing the shutdown of a channel link to prevent further waiting on sig jobs by the channel state machine. This addresses possible cases where the aux signer may be shut down via a separate quit signal, so the state machine could block indefinitely on receiving an update on a sig job. --- contractcourt/chain_watcher_test.go | 13 +- htlcswitch/link.go | 51 +++--- htlcswitch/link_isolated_test.go | 5 +- htlcswitch/link_test.go | 54 ++++-- htlcswitch/test_utils.go | 14 +- lnwallet/channel.go | 31 +++- lnwallet/channel_test.go | 252 +++++++++++++++------------- lnwallet/test_utils.go | 9 +- lnwallet/transactions_test.go | 4 +- 9 files changed, 256 insertions(+), 177 deletions(-) diff --git a/contractcourt/chain_watcher_test.go b/contractcourt/chain_watcher_test.go index 489a605185..2781170f0c 100644 --- a/contractcourt/chain_watcher_test.go +++ b/contractcourt/chain_watcher_test.go @@ -2,6 +2,7 @@ package contractcourt import ( "bytes" + "context" "crypto/sha256" "fmt" "testing" @@ -145,17 +146,15 @@ func TestChainWatcherRemoteUnilateralClosePendingCommit(t *testing.T) { // With the HTLC added, we'll now manually initiate a state transition // from Alice to Bob. - _, err = aliceChannel.SignNextCommitment() - if err != nil { - t.Fatal(err) - } + testQuit, testQuitFunc := context.WithCancel(context.Background()) + t.Cleanup(testQuitFunc) + _, err = aliceChannel.SignNextCommitment(testQuit) + require.NoError(t, err) // At this point, we'll now Bob broadcasting this new pending unrevoked // commitment. bobPendingCommit, err := aliceChannel.State().RemoteCommitChainTip() - if err != nil { - t.Fatal(err) - } + require.NoError(t, err) // We'll craft a fake spend notification with Bob's actual commitment. // The chain watcher should be able to detect that this is a pending diff --git a/htlcswitch/link.go b/htlcswitch/link.go index 87384a9da3..2e2f104af7 100644 --- a/htlcswitch/link.go +++ b/htlcswitch/link.go @@ -386,8 +386,10 @@ type channelLink struct { // our next CommitSig. incomingCommitHooks hookMap - wg sync.WaitGroup - quit chan struct{} + // ContextGuard is a helper that encapsulates a wait group and quit + // channel and allows contexts that either block or cancel on those + // depending on the use case. + *fn.ContextGuard } // hookMap is a data structure that is used to track the hooks that need to be @@ -468,7 +470,7 @@ func NewChannelLink(cfg ChannelLinkConfig, flushHooks: newHookMap(), outgoingCommitHooks: newHookMap(), incomingCommitHooks: newHookMap(), - quit: make(chan struct{}), + ContextGuard: fn.NewContextGuard(), } } @@ -547,7 +549,7 @@ func (l *channelLink) Start() error { l.updateFeeTimer = time.NewTimer(l.randomFeeUpdateTimeout()) - l.wg.Add(1) + l.Wg.Add(1) go l.htlcManager() return nil @@ -587,8 +589,8 @@ func (l *channelLink) Stop() { l.hodlQueue.Stop() } - close(l.quit) - l.wg.Wait() + close(l.Quit) + l.Wg.Wait() // Now that the htlcManager has completely exited, reset the packet // courier. This allows the mailbox to revaluate any lingering Adds that @@ -613,7 +615,7 @@ func (l *channelLink) Stop() { // WaitForShutdown blocks until the link finishes shutting down, which includes // termination of all dependent goroutines. func (l *channelLink) WaitForShutdown() { - l.wg.Wait() + l.Wg.Wait() } // EligibleToForward returns a bool indicating if the channel is able to @@ -674,7 +676,7 @@ func (l *channelLink) IsFlushing(linkDirection LinkDirection) bool { func (l *channelLink) OnFlushedOnce(hook func()) { select { case l.flushHooks.newTransients <- hook: - case <-l.quit: + case <-l.Quit: } } @@ -693,7 +695,7 @@ func (l *channelLink) OnCommitOnce(direction LinkDirection, hook func()) { select { case queue <- hook: - case <-l.quit: + case <-l.Quit: } } @@ -902,8 +904,10 @@ func (l *channelLink) syncChanStates() error { // We've just received a ChanSync message from the remote // party, so we'll process the message in order to determine // if we need to re-transmit any messages to the remote party. + ctx, cancel := l.WithCtxQuitNoTimeout() + defer cancel() msgsToReSend, openedCircuits, closedCircuits, err = - l.channel.ProcessChanSyncMsg(remoteChanSyncMsg) + l.channel.ProcessChanSyncMsg(ctx, remoteChanSyncMsg) if err != nil { return err } @@ -932,7 +936,7 @@ func (l *channelLink) syncChanStates() error { l.cfg.Peer.SendMessage(false, msg) } - case <-l.quit: + case <-l.Quit: return ErrLinkShuttingDown } @@ -1022,7 +1026,7 @@ func (l *channelLink) resolveFwdPkg(fwdPkg *channeldb.FwdPkg) error { // // NOTE: This MUST be run as a goroutine. func (l *channelLink) fwdPkgGarbager() { - defer l.wg.Done() + defer l.Wg.Done() l.cfg.FwdPkgGCTicker.Resume() defer l.cfg.FwdPkgGCTicker.Stop() @@ -1039,7 +1043,7 @@ func (l *channelLink) fwdPkgGarbager() { err) continue } - case <-l.quit: + case <-l.Quit: return } } @@ -1162,7 +1166,7 @@ func (l *channelLink) handleChanSyncErr(err error) { func (l *channelLink) htlcManager() { defer func() { l.cfg.BatchTicker.Stop() - l.wg.Done() + l.Wg.Done() l.log.Infof("exited") }() @@ -1256,7 +1260,7 @@ func (l *channelLink) htlcManager() { // With our link's in-memory state fully reconstructed, spawn a // goroutine to manage the reclamation of disk space occupied by // completed forwarding packages. - l.wg.Add(1) + l.Wg.Add(1) go l.fwdPkgGarbager() } @@ -1440,7 +1444,7 @@ func (l *channelLink) htlcManager() { ) } - case <-l.quit: + case <-l.Quit: return } } @@ -2298,7 +2302,7 @@ func (l *channelLink) handleUpstreamMsg(msg lnwire.Message) { } select { - case <-l.quit: + case <-l.Quit: return default: } @@ -2359,7 +2363,7 @@ func (l *channelLink) handleUpstreamMsg(msg lnwire.Message) { } select { - case <-l.quit: + case <-l.Quit: return default: } @@ -2589,7 +2593,10 @@ func (l *channelLink) updateCommitTx() error { return nil } - newCommit, err := l.channel.SignNextCommitment() + ctx, done := l.WithCtxQuitNoTimeout() + defer done() + + newCommit, err := l.channel.SignNextCommitment(ctx) if err == lnwallet.ErrNoWindow { l.cfg.PendingCommitTicker.Resume() l.log.Trace("PendingCommitTicker resumed") @@ -2626,7 +2633,7 @@ func (l *channelLink) updateCommitTx() error { } select { - case <-l.quit: + case <-l.Quit: return ErrLinkShuttingDown default: } @@ -3232,7 +3239,7 @@ func (l *channelLink) handleSwitchPacket(pkt *htlcPacket) error { // NOTE: Part of the ChannelLink interface. func (l *channelLink) HandleChannelUpdate(message lnwire.Message) { select { - case <-l.quit: + case <-l.Quit: // Return early if the link is already in the process of // quitting. It doesn't make sense to hand the message to the // mailbox here. @@ -3931,7 +3938,7 @@ func (l *channelLink) forwardBatch(replay bool, packets ...*htlcPacket) { filteredPkts = append(filteredPkts, pkt) } - err := l.cfg.ForwardPackets(l.quit, replay, filteredPkts...) + err := l.cfg.ForwardPackets(l.Quit, replay, filteredPkts...) if err != nil { log.Errorf("Unhandled error while reforwarding htlc "+ "settle/fail over htlcswitch: %v", err) diff --git a/htlcswitch/link_isolated_test.go b/htlcswitch/link_isolated_test.go index 5d769d9201..5281cbed7f 100644 --- a/htlcswitch/link_isolated_test.go +++ b/htlcswitch/link_isolated_test.go @@ -1,6 +1,7 @@ package htlcswitch import ( + "context" "crypto/sha256" "testing" "time" @@ -94,7 +95,9 @@ func (l *linkTestContext) receiveHtlcAliceToBob() { func (l *linkTestContext) sendCommitSigBobToAlice(expHtlcs int) { l.t.Helper() - sigs, err := l.bobChannel.SignNextCommitment() + testQuit, testQuitFunc := context.WithCancel(context.Background()) + defer testQuitFunc() + sigs, err := l.bobChannel.SignNextCommitment(testQuit) if err != nil { l.t.Fatalf("error signing commitment: %v", err) } diff --git a/htlcswitch/link_test.go b/htlcswitch/link_test.go index be20eeb220..574f3a6778 100644 --- a/htlcswitch/link_test.go +++ b/htlcswitch/link_test.go @@ -2252,12 +2252,14 @@ func newSingleLinkTestHarness(t *testing.T, chanAmt, return aliceSwitch.AddLink(aliceLink) } go func() { - for { - select { - case <-notifyUpdateChan: - case <-aliceLink.(*channelLink).quit: - close(doneChan) - return + if chanLink, ok := aliceLink.(*channelLink); ok { + for { + select { + case <-notifyUpdateChan: + case <-chanLink.Quit: + close(doneChan) + return + } } } }() @@ -2324,7 +2326,10 @@ func handleStateUpdate(link *channelLink, } link.HandleChannelUpdate(remoteRev) - remoteSigs, err := remoteChannel.SignNextCommitment() + ctx, done := link.WithCtxQuitNoTimeout() + defer done() + + remoteSigs, err := remoteChannel.SignNextCommitment(ctx) if err != nil { return err } @@ -2367,7 +2372,7 @@ func updateState(batchTick chan time.Time, link *channelLink, // Trigger update by ticking the batchTicker. select { case batchTick <- time.Now(): - case <-link.quit: + case <-link.Quit: return fmt.Errorf("link shutting down") } return handleStateUpdate(link, remoteChannel) @@ -2375,7 +2380,10 @@ func updateState(batchTick chan time.Time, link *channelLink, // The remote is triggering the state update, emulate this by // signing and sending CommitSig to the link. - remoteSigs, err := remoteChannel.SignNextCommitment() + ctx, done := link.WithCtxQuitNoTimeout() + defer done() + + remoteSigs, err := remoteChannel.SignNextCommitment(ctx) if err != nil { return err } @@ -4931,12 +4939,14 @@ func (h *persistentLinkHarness) restartLink( return nil, nil, err } go func() { - for { - select { - case <-notifyUpdateChan: - case <-aliceLink.(*channelLink).quit: - close(doneChan) - return + if chanLink, ok := aliceLink.(*channelLink); ok { + for { + select { + case <-notifyUpdateChan: + case <-chanLink.Quit: + close(doneChan) + return + } } } }() @@ -5919,7 +5929,12 @@ func TestChannelLinkFail(t *testing.T) { // Sign a commitment that will include // signature for the HTLC just sent. - sigs, err := remoteChannel.SignNextCommitment() + quitCtx, done := c.WithCtxQuitNoTimeout() + defer done() + + sigs, err := remoteChannel.SignNextCommitment( + quitCtx, + ) if err != nil { t.Fatalf("error signing commitment: %v", err) @@ -5961,7 +5976,12 @@ func TestChannelLinkFail(t *testing.T) { // Sign a commitment that will include // signature for the HTLC just sent. - sigs, err := remoteChannel.SignNextCommitment() + quitCtx, done := c.WithCtxQuitNoTimeout() + defer done() + + sigs, err := remoteChannel.SignNextCommitment( + quitCtx, + ) if err != nil { t.Fatalf("error signing commitment: %v", err) diff --git a/htlcswitch/test_utils.go b/htlcswitch/test_utils.go index f12dc847a2..67aaa4a957 100644 --- a/htlcswitch/test_utils.go +++ b/htlcswitch/test_utils.go @@ -1195,12 +1195,14 @@ func (h *hopNetwork) createChannelLink(server, peer *mockServer, } go func() { - for { - select { - case <-notifyUpdateChan: - case <-link.(*channelLink).quit: - close(doneChan) - return + if chanLink, ok := link.(*channelLink); ok { + for { + select { + case <-notifyUpdateChan: + case <-chanLink.Quit: + close(doneChan) + return + } } } }() diff --git a/lnwallet/channel.go b/lnwallet/channel.go index 20a8b8c945..cefee3ef12 100644 --- a/lnwallet/channel.go +++ b/lnwallet/channel.go @@ -3,6 +3,7 @@ package lnwallet import ( "bytes" "cmp" + "context" "crypto/sha256" "errors" "fmt" @@ -138,6 +139,10 @@ var ( // errNoPartialSig is returned when a partial signature is required, // but none is found. errNoPartialSig = errors.New("no partial signature found") + + // errQuit is returned when a quit signal was received, interrupting the + // current operation. + errQuit = errors.New("received quit signal") ) // ErrCommitSyncLocalDataLoss is returned in the case that we receive a valid @@ -3956,7 +3961,9 @@ type NewCommitState struct { // for the remote party's commitment are also returned. // //nolint:funlen -func (lc *LightningChannel) SignNextCommitment() (*NewCommitState, error) { +func (lc *LightningChannel) SignNextCommitment( + ctx context.Context) (*NewCommitState, error) { + lc.Lock() defer lc.Unlock() @@ -4122,7 +4129,13 @@ func (lc *LightningChannel) SignNextCommitment() (*NewCommitState, error) { auxSigs := make([]fn.Option[tlv.Blob], 0, len(auxSigBatch)) for i := range sigBatch { htlcSigJob := sigBatch[i] - jobResp := <-htlcSigJob.Resp + var jobResp SignJobResp + + select { + case jobResp = <-htlcSigJob.Resp: + case <-ctx.Done(): + return nil, errQuit + } // If an error occurred, then we'll cancel any other active // jobs. @@ -4138,7 +4151,13 @@ func (lc *LightningChannel) SignNextCommitment() (*NewCommitState, error) { } auxHtlcSigJob := auxSigBatch[i] - auxJobResp := <-auxHtlcSigJob.Resp + var auxJobResp AuxSigJobResp + + select { + case auxJobResp = <-auxHtlcSigJob.Resp: + case <-ctx.Done(): + return nil, errQuit + } // If an error occurred, then we'll cancel any other active // jobs. @@ -4232,7 +4251,9 @@ func (lc *LightningChannel) resignMusigCommit( // previous commitment txn. This allows the link to clear its mailbox of those // circuits in case they are still in memory, and ensure the switch's circuit // map has been updated by deleting the closed circuits. -func (lc *LightningChannel) ProcessChanSyncMsg( +// +//nolint:funlen +func (lc *LightningChannel) ProcessChanSyncMsg(ctx context.Context, msg *lnwire.ChannelReestablish) ([]lnwire.Message, []models.CircuitKey, []models.CircuitKey, error) { @@ -4396,7 +4417,7 @@ func (lc *LightningChannel) ProcessChanSyncMsg( // revocation, but also initiate a state transition to re-sync // them. if lc.OweCommitment() { - newCommit, err := lc.SignNextCommitment() + newCommit, err := lc.SignNextCommitment(ctx) switch { // If we signed this state, then we'll accumulate diff --git a/lnwallet/channel_test.go b/lnwallet/channel_test.go index 9ea4aa4586..553c3b2f97 100644 --- a/lnwallet/channel_test.go +++ b/lnwallet/channel_test.go @@ -124,7 +124,7 @@ func testAddSettleWorkflow(t *testing.T, tweakless bool, // we expect the messages to be ordered, Bob will receive the HTLC we // just sent before he receives this signature, so the signature will // cover the HTLC. - aliceNewCommit, err := aliceChannel.SignNextCommitment() + aliceNewCommit, err := aliceChannel.SignNextCommitment(ctxb) require.NoError(t, err, "alice unable to sign commitment") // Bob receives this signature message, and checks that this covers the @@ -142,7 +142,7 @@ func testAddSettleWorkflow(t *testing.T, tweakless bool, // This signature will cover the HTLC, since Bob will first send the // revocation just created. The revocation also acks every received // HTLC up to the point where Alice sent here signature. - bobNewCommit, err := bobChannel.SignNextCommitment() + bobNewCommit, err := bobChannel.SignNextCommitment(ctxb) require.NoError(t, err, "bob unable to sign alice's commitment") // Alice then processes this revocation, sending her own revocation for @@ -255,14 +255,14 @@ func testAddSettleWorkflow(t *testing.T, tweakless bool, t.Fatalf("alice unable to accept settle of outbound htlc: %v", err) } - bobNewCommit, err = bobChannel.SignNextCommitment() + bobNewCommit, err = bobChannel.SignNextCommitment(ctxb) require.NoError(t, err, "bob unable to sign settle commitment") err = aliceChannel.ReceiveNewCommitment(bobNewCommit.CommitSigs) require.NoError(t, err, "alice unable to process bob's new commitment") aliceRevocation2, _, _, err := aliceChannel.RevokeCurrentCommitment() require.NoError(t, err, "alice unable to generate revocation") - aliceNewCommit, err = aliceChannel.SignNextCommitment() + aliceNewCommit, err = aliceChannel.SignNextCommitment(ctxb) require.NoError(t, err, "alice unable to sign new commitment") fwdPkg, _, err = bobChannel.ReceiveRevocation(aliceRevocation2) @@ -470,7 +470,7 @@ func TestChannelZeroAddLocalHeight(t *testing.T) { // Bob should send a commitment signature to Alice. // <----sig------ - bobNewCommit, err := bobChannel.SignNextCommitment() + bobNewCommit, err := bobChannel.SignNextCommitment(ctxb) require.NoError(t, err) err = aliceChannel.ReceiveNewCommitment(bobNewCommit.CommitSigs) @@ -501,7 +501,7 @@ func TestChannelZeroAddLocalHeight(t *testing.T) { // Bob should now send a commitment signature to Alice. // <----sig----- - bobNewCommit, err = bobChannel.SignNextCommitment() + bobNewCommit, err = bobChannel.SignNextCommitment(ctxb) require.NoError(t, err) // Alice should accept the commitment. Previously she would @@ -635,7 +635,7 @@ func testCommitHTLCSigTieBreak(t *testing.T, restart bool) { // tie-breaking for commitment sorting won't affect the commitment // signed by Alice because received HTLC scripts commit to the CLTV // directly, so the outputs will have different scriptPubkeys. - aliceNewCommit, err := aliceChannel.SignNextCommitment() + aliceNewCommit, err := aliceChannel.SignNextCommitment(ctxb) require.NoError(t, err, "unable to sign alice's commitment") err = bobChannel.ReceiveNewCommitment(aliceNewCommit.CommitSigs) @@ -650,7 +650,7 @@ func testCommitHTLCSigTieBreak(t *testing.T, restart bool) { // the offered HTLC scripts he adds for Alice will need to have the // tie-breaking applied because the CLTV is not committed, but instead // implicit via the construction of the second-level transactions. - bobNewCommit, err := bobChannel.SignNextCommitment() + bobNewCommit, err := bobChannel.SignNextCommitment(ctxb) require.NoError(t, err, "unable to sign bob's commitment") if len(bobNewCommit.PendingHTLCs) != numHtlcs { @@ -759,7 +759,7 @@ func TestCommitHTLCSigCustomRecordSize(t *testing.T) { } // We expect an error because of the large custom records blob. - _, err = aliceChannel.SignNextCommitment() + _, err = aliceChannel.SignNextCommitment(ctxb) require.ErrorContains(t, err, "exceeds max allowed size") } @@ -1573,7 +1573,7 @@ func TestHTLCSigNumber(t *testing.T) { // =================================================================== aliceChannel, bobChannel := createChanWithHTLC(aboveDust, aboveDust) - aliceNewCommit, err := aliceChannel.SignNextCommitment() + aliceNewCommit, err := aliceChannel.SignNextCommitment(ctxb) require.NoError(t, err, "Error signing next commitment") if len(aliceNewCommit.HtlcSigs) != 2 { @@ -1596,7 +1596,7 @@ func TestHTLCSigNumber(t *testing.T) { // =================================================================== aliceChannel, bobChannel = createChanWithHTLC(aboveDust) - aliceNewCommit, err = aliceChannel.SignNextCommitment() + aliceNewCommit, err = aliceChannel.SignNextCommitment(ctxb) require.NoError(t, err, "Error signing next commitment") if len(aliceNewCommit.HtlcSigs) != 1 { @@ -1618,7 +1618,7 @@ func TestHTLCSigNumber(t *testing.T) { // ============================================================== aliceChannel, bobChannel = createChanWithHTLC(belowDust) - aliceNewCommit, err = aliceChannel.SignNextCommitment() + aliceNewCommit, err = aliceChannel.SignNextCommitment(ctxb) require.NoError(t, err, "Error signing next commitment") // Since the HTLC is below Bob's dust limit, Alice won't need to send @@ -1636,7 +1636,7 @@ func TestHTLCSigNumber(t *testing.T) { // ================================================================ aliceChannel, bobChannel = createChanWithHTLC(aboveDust) - aliceNewCommit, err = aliceChannel.SignNextCommitment() + aliceNewCommit, err = aliceChannel.SignNextCommitment(ctxb) require.NoError(t, err, "Error signing next commitment") // Since the HTLC is above Bob's dust limit, Alice should send a @@ -1657,7 +1657,7 @@ func TestHTLCSigNumber(t *testing.T) { // Alice should produce only one signature, since one HTLC is below // dust. - aliceNewCommit, err = aliceChannel.SignNextCommitment() + aliceNewCommit, err = aliceChannel.SignNextCommitment(ctxb) require.NoError(t, err, "Error signing next commitment") if len(aliceNewCommit.HtlcSigs) != 1 { @@ -2427,7 +2427,7 @@ func TestUpdateFeeFail(t *testing.T) { // Alice sends signature for commitment that does not cover any fee // update. - aliceNewCommit, err := aliceChannel.SignNextCommitment() + aliceNewCommit, err := aliceChannel.SignNextCommitment(ctxb) require.NoError(t, err, "alice unable to sign commitment") // Bob verifies this commit, meaning that he checks that it is @@ -2469,11 +2469,11 @@ func TestUpdateFeeConcurrentSig(t *testing.T) { } // Alice signs a commitment, and sends this to bob. - aliceNewCommits, err := aliceChannel.SignNextCommitment() + aliceNewCommits, err := aliceChannel.SignNextCommitment(ctxb) require.NoError(t, err, "alice unable to sign commitment") // At the same time, Bob signs a commitment. - bobNewCommits, err := bobChannel.SignNextCommitment() + bobNewCommits, err := bobChannel.SignNextCommitment(ctxb) require.NoError(t, err, "bob unable to sign alice's commitment") // ...that Alice receives. @@ -2540,7 +2540,7 @@ func TestUpdateFeeSenderCommits(t *testing.T) { // Alice signs a commitment, which will cover everything sent to Bob // (the HTLC and the fee update), and everything acked by Bob (nothing // so far). - aliceNewCommits, err := aliceChannel.SignNextCommitment() + aliceNewCommits, err := aliceChannel.SignNextCommitment(ctxb) require.NoError(t, err, "alice unable to sign commitment") // Bob receives this signature message, and verifies that it is @@ -2570,7 +2570,7 @@ func TestUpdateFeeSenderCommits(t *testing.T) { // Bob commits to all updates he has received from Alice. This includes // the HTLC he received, and the fee update. - bobNewCommit, err := bobChannel.SignNextCommitment() + bobNewCommit, err := bobChannel.SignNextCommitment(ctxb) require.NoError(t, err, "bob unable to sign alice's commitment") // Alice receives the revocation of the old one, and can now assume @@ -2643,7 +2643,7 @@ func TestUpdateFeeReceiverCommits(t *testing.T) { // Bob commits to every change he has sent since last time (none). He // does not commit to the received HTLC and fee update, since Alice // cannot know if he has received them. - bobNewCommit, err := bobChannel.SignNextCommitment() + bobNewCommit, err := bobChannel.SignNextCommitment(ctxb) require.NoError(t, err, "alice unable to sign commitment") // Alice receives this signature message, and verifies that it is @@ -2664,7 +2664,7 @@ func TestUpdateFeeReceiverCommits(t *testing.T) { // Alice will sign next commitment. Since she sent the revocation, she // also ack'ed everything received, but in this case this is nothing. // Since she sent the two updates, this signature will cover those two. - aliceNewCommit, err := aliceChannel.SignNextCommitment() + aliceNewCommit, err := aliceChannel.SignNextCommitment(ctxb) require.NoError(t, err, "bob unable to sign alice's commitment") // Bob gets the signature for the new commitment from Alice. He assumes @@ -2694,7 +2694,7 @@ func TestUpdateFeeReceiverCommits(t *testing.T) { // Bob will send a new signature, which will cover what he just acked: // the HTLC and fee update. - bobNewCommit, err = bobChannel.SignNextCommitment() + bobNewCommit, err = bobChannel.SignNextCommitment(ctxb) require.NoError(t, err, "alice unable to sign commitment") // Alice receives revocation from Bob, and can now be sure that Bob @@ -2784,7 +2784,7 @@ func TestUpdateFeeMultipleUpdates(t *testing.T) { // Alice signs a commitment, which will cover everything sent to Bob // (the HTLC and the fee update), and everything acked by Bob (nothing // so far). - aliceNewCommit, err := aliceChannel.SignNextCommitment() + aliceNewCommit, err := aliceChannel.SignNextCommitment(ctxb) require.NoError(t, err, "alice unable to sign commitment") bobChannel.ReceiveUpdateFee(fee1) @@ -2830,7 +2830,7 @@ func TestUpdateFeeMultipleUpdates(t *testing.T) { // Bob commits to all updates he has received from Alice. This includes // the HTLC he received, and the fee update. - bobNewCommit, err := bobChannel.SignNextCommitment() + bobNewCommit, err := bobChannel.SignNextCommitment(ctxb) require.NoError(t, err, "bob unable to sign alice's commitment") // Alice receives the revocation of the old one, and can now assume that @@ -2936,7 +2936,9 @@ func assertNoChanSyncNeeded(t *testing.T, aliceChannel *LightningChannel, } } - bobMsgsToSend, _, _, err := bobChannel.ProcessChanSyncMsg(aliceChanSyncMsg) + bobMsgsToSend, _, _, err := bobChannel.ProcessChanSyncMsg( + ctxb, aliceChanSyncMsg, + ) if err != nil { t.Fatalf("line #%v: unable to process ChannelReestablish "+ "msg: %v", line, err) @@ -2946,7 +2948,9 @@ func assertNoChanSyncNeeded(t *testing.T, aliceChannel *LightningChannel, "instead wants to send: %v", line, spew.Sdump(bobMsgsToSend)) } - aliceMsgsToSend, _, _, err := aliceChannel.ProcessChanSyncMsg(bobChanSyncMsg) + aliceMsgsToSend, _, _, err := aliceChannel.ProcessChanSyncMsg( + ctxb, bobChanSyncMsg, + ) if err != nil { t.Fatalf("line #%v: unable to process ChannelReestablish "+ "msg: %v", line, err) @@ -3140,7 +3144,7 @@ func testChanSyncOweCommitment(t *testing.T, chanType channeldb.ChannelType) { // Now we'll begin the core of the test itself. Alice will extend a new // commitment to Bob, but the connection drops before Bob can process // it. - aliceNewCommit, err := aliceChannel.SignNextCommitment() + aliceNewCommit, err := aliceChannel.SignNextCommitment(ctxb) require.NoError(t, err, "unable to sign commitment") // If this is a taproot channel, then we'll generate fresh verification @@ -3165,7 +3169,7 @@ func testChanSyncOweCommitment(t *testing.T, chanType channeldb.ChannelType) { // above. assertAliceCommitRetransmit := func() *lnwire.CommitSig { aliceMsgsToSend, _, _, err := aliceChannel.ProcessChanSyncMsg( - bobSyncMsg, + ctxb, bobSyncMsg, ) if err != nil { t.Fatalf("unable to process chan sync msg: %v", err) @@ -3257,7 +3261,9 @@ func testChanSyncOweCommitment(t *testing.T, chanType channeldb.ChannelType) { // From Bob's Pov he has nothing else to send, so he should conclude he // has no further action remaining. - bobMsgsToSend, _, _, err := bobChannel.ProcessChanSyncMsg(aliceSyncMsg) + bobMsgsToSend, _, _, err := bobChannel.ProcessChanSyncMsg( + ctxb, aliceSyncMsg, + ) require.NoError(t, err, "unable to process chan sync msg") if len(bobMsgsToSend) != 0 { t.Fatalf("expected bob to send %v messages instead will "+ @@ -3285,7 +3291,7 @@ func testChanSyncOweCommitment(t *testing.T, chanType channeldb.ChannelType) { require.NoError(t, err, "bob unable to process alice's commitment") bobRevocation, _, _, err := bobChannel.RevokeCurrentCommitment() require.NoError(t, err, "unable to revoke bob commitment") - bobNewCommit, err := bobChannel.SignNextCommitment() + bobNewCommit, err := bobChannel.SignNextCommitment(ctxb) require.NoError(t, err, "bob unable to sign commitment") _, _, err = aliceChannel.ReceiveRevocation(bobRevocation) require.NoError(t, err, "alice unable to recv revocation") @@ -3468,7 +3474,7 @@ func TestChanSyncOweCommitmentAuxSigner(t *testing.T) { fn.Ok(fn.Some(sigBlobBuf.Bytes())), nil, ) - _, err = aliceChannel.SignNextCommitment() + _, err = aliceChannel.SignNextCommitment(ctxb) require.NoError(t, err, "unable to sign commitment") _, err = aliceChannel.GenMusigNonces() @@ -3480,7 +3486,7 @@ func TestChanSyncOweCommitmentAuxSigner(t *testing.T) { require.NoError(t, err, "unable to produce chan sync msg") aliceMsgsToSend, _, _, err := aliceChannel.ProcessChanSyncMsg( - bobSyncMsg, + ctxb, bobSyncMsg, ) require.NoError(t, err) require.Len(t, aliceMsgsToSend, 2) @@ -3572,7 +3578,7 @@ func TestAuxSignerShutdown(t *testing.T) { fn.Some(sigBlobBuf.Bytes()), nil, ) - _, err = aliceChannel.SignNextCommitment() + _, err = aliceChannel.SignNextCommitment(ctxb) require.ErrorIs(t, err, auxSignerShutdownErr) } @@ -3638,7 +3644,9 @@ func testChanSyncOweCommitmentPendingRemote(t *testing.T, t.Fatalf("unable to settle htlc: %v", err) } - aliceNewCommit, err := aliceChannel.SignNextCommitment() + aliceNewCommit, err := aliceChannel.SignNextCommitment( + ctxb, + ) if err != nil { t.Fatalf("unable to sign commitment: %v", err) } @@ -3676,7 +3684,7 @@ func testChanSyncOweCommitmentPendingRemote(t *testing.T, } // Bob signs the commitment he owes. - bobNewCommit, err := bobChannel.SignNextCommitment() + bobNewCommit, err := bobChannel.SignNextCommitment(ctxb) require.NoError(t, err, "unable to sign commitment") // This commitment is expected to contain no htlcs anymore. @@ -3778,14 +3786,14 @@ func testChanSyncOweRevocation(t *testing.T, chanType channeldb.ChannelType) { // // Alice signs the next state, then Bob receives and sends his // revocation message. - aliceNewCommit, err := aliceChannel.SignNextCommitment() + aliceNewCommit, err := aliceChannel.SignNextCommitment(ctxb) require.NoError(t, err, "unable to sign commitment") err = bobChannel.ReceiveNewCommitment(aliceNewCommit.CommitSigs) require.NoError(t, err, "bob unable to process alice's commitment") bobRevocation, _, _, err := bobChannel.RevokeCurrentCommitment() require.NoError(t, err, "unable to revoke bob commitment") - bobNewCommit, err := bobChannel.SignNextCommitment() + bobNewCommit, err := bobChannel.SignNextCommitment(ctxb) require.NoError(t, err, "bob unable to sign commitment") _, _, err = aliceChannel.ReceiveRevocation(bobRevocation) @@ -3821,7 +3829,7 @@ func testChanSyncOweRevocation(t *testing.T, chanType channeldb.ChannelType) { t.Helper() aliceMsgsToSend, _, _, err := aliceChannel.ProcessChanSyncMsg( - bobSyncMsg, + ctxb, bobSyncMsg, ) if err != nil { t.Fatalf("unable to process chan sync msg: %v", err) @@ -3852,7 +3860,9 @@ func testChanSyncOweRevocation(t *testing.T, chanType channeldb.ChannelType) { } // From Bob's PoV he shouldn't think that he owes Alice any messages. - bobMsgsToSend, _, _, err := bobChannel.ProcessChanSyncMsg(aliceSyncMsg) + bobMsgsToSend, _, _, err := bobChannel.ProcessChanSyncMsg( + ctxb, aliceSyncMsg, + ) require.NoError(t, err, "unable to process chan sync msg") if len(bobMsgsToSend) != 0 { t.Fatalf("expected bob to not retransmit, instead has: %v", @@ -3972,7 +3982,7 @@ func testChanSyncOweRevocationAndCommit(t *testing.T, // Progressing the exchange: Alice will send her signature, Bob will // receive, send a revocation and also a signature for Alice's state. - aliceNewCommits, err := aliceChannel.SignNextCommitment() + aliceNewCommits, err := aliceChannel.SignNextCommitment(ctxb) require.NoError(t, err, "unable to sign commitment") err = bobChannel.ReceiveNewCommitment(aliceNewCommits.CommitSigs) require.NoError(t, err, "bob unable to process alice's commitment") @@ -3981,7 +3991,7 @@ func testChanSyncOweRevocationAndCommit(t *testing.T, // reach Alice before the connection dies. bobRevocation, _, _, err := bobChannel.RevokeCurrentCommitment() require.NoError(t, err, "unable to revoke bob commitment") - bobNewCommit, err := bobChannel.SignNextCommitment() + bobNewCommit, err := bobChannel.SignNextCommitment(ctxb) require.NoError(t, err, "bob unable to sign commitment") // If we now attempt to resync, then Alice should conclude that she @@ -4003,7 +4013,9 @@ func testChanSyncOweRevocationAndCommit(t *testing.T, } } - aliceMsgsToSend, _, _, err := aliceChannel.ProcessChanSyncMsg(bobSyncMsg) + aliceMsgsToSend, _, _, err := aliceChannel.ProcessChanSyncMsg( + ctxb, bobSyncMsg, + ) require.NoError(t, err, "unable to process chan sync msg") if len(aliceMsgsToSend) != 0 { t.Fatalf("expected alice to not retransmit, instead she's "+ @@ -4014,7 +4026,7 @@ func testChanSyncOweRevocationAndCommit(t *testing.T, t.Helper() bobMsgsToSend, _, _, err := bobChannel.ProcessChanSyncMsg( - aliceSyncMsg, + ctxb, aliceSyncMsg, ) if err != nil { t.Fatalf("unable to process chan sync msg: %v", err) @@ -4171,7 +4183,7 @@ func testChanSyncOweRevocationAndCommitForceTransition(t *testing.T, addAndReceiveHTLC(t, bobChannel, aliceChannel, bobHtlc[1], nil) // Bob signs the new state update, and sends the signature to Alice. - bobNewCommit, err := bobChannel.SignNextCommitment() + bobNewCommit, err := bobChannel.SignNextCommitment(ctxb) require.NoError(t, err, "bob unable to sign commitment") err = aliceChannel.ReceiveNewCommitment(bobNewCommit.CommitSigs) @@ -4195,7 +4207,7 @@ func testChanSyncOweRevocationAndCommitForceTransition(t *testing.T, // Progressing the exchange: Alice will send her signature, with Bob // processing the new state locally. - aliceNewCommits, err := aliceChannel.SignNextCommitment() + aliceNewCommits, err := aliceChannel.SignNextCommitment(ctxb) require.NoError(t, err, "unable to sign commitment") err = bobChannel.ReceiveNewCommitment(aliceNewCommits.CommitSigs) require.NoError(t, err, "bob unable to process alice's commitment") @@ -4225,7 +4237,9 @@ func testChanSyncOweRevocationAndCommitForceTransition(t *testing.T, } } - aliceMsgsToSend, _, _, err := aliceChannel.ProcessChanSyncMsg(bobSyncMsg) + aliceMsgsToSend, _, _, err := aliceChannel.ProcessChanSyncMsg( + ctxb, bobSyncMsg, + ) require.NoError(t, err, "unable to process chan sync msg") if len(aliceMsgsToSend) != 0 { t.Fatalf("expected alice to not retransmit, instead she's "+ @@ -4236,7 +4250,9 @@ func testChanSyncOweRevocationAndCommitForceTransition(t *testing.T, // send his RevokeAndAck message again. Additionally, the CommitSig // message that he sends should be sufficient to finalize the state // transition. - bobMsgsToSend, _, _, err := bobChannel.ProcessChanSyncMsg(aliceSyncMsg) + bobMsgsToSend, _, _, err := bobChannel.ProcessChanSyncMsg( + ctxb, aliceSyncMsg, + ) require.NoError(t, err, "unable to process chan sync msg") if len(bobMsgsToSend) != 2 { t.Fatalf("expected bob to send %v messages, instead "+ @@ -4418,7 +4434,9 @@ func TestChanSyncFailure(t *testing.T) { addAndReceiveHTLC(t, bobChannel, aliceChannel, bobHtlc, nil) - aliceNewCommit, err := aliceChannel.SignNextCommitment() + aliceNewCommit, err := aliceChannel.SignNextCommitment( + ctxb, + ) if err != nil { t.Fatalf("unable to sign next commit: %v", err) } @@ -4443,7 +4461,7 @@ func TestChanSyncFailure(t *testing.T) { } // Alice should detect from Bob's message that she lost state. - _, _, _, err = aliceOld.ProcessChanSyncMsg(bobSyncMsg) + _, _, _, err = aliceOld.ProcessChanSyncMsg(ctxb, bobSyncMsg) if _, ok := err.(*ErrCommitSyncLocalDataLoss); !ok { t.Fatalf("wrong error, expected "+ "ErrCommitSyncLocalDataLoss instead got: %v", @@ -4451,7 +4469,9 @@ func TestChanSyncFailure(t *testing.T) { } // Bob should detect that Alice probably lost state. - _, _, _, err = bobChannel.ProcessChanSyncMsg(aliceSyncMsg) + _, _, _, err = bobChannel.ProcessChanSyncMsg( + ctxb, aliceSyncMsg, + ) if err != ErrCommitSyncRemoteDataLoss { t.Fatalf("wrong error, expected "+ "ErrCommitSyncRemoteDataLoss instead got: %v", @@ -4512,7 +4532,7 @@ func TestChanSyncFailure(t *testing.T) { bobSyncMsg, err := bobChannel.channelState.ChanSyncMsg() require.NoError(t, err, "unable to produce chan sync msg") bobSyncMsg.LocalUnrevokedCommitPoint = nil - _, _, _, err = aliceOld.ProcessChanSyncMsg(bobSyncMsg) + _, _, _, err = aliceOld.ProcessChanSyncMsg(ctxb, bobSyncMsg) if err != ErrCannotSyncCommitChains { t.Fatalf("wrong error, expected ErrCannotSyncCommitChains "+ "instead got: %v", err) @@ -4524,7 +4544,7 @@ func TestChanSyncFailure(t *testing.T) { bobSyncMsg, err = bobChannel.channelState.ChanSyncMsg() require.NoError(t, err, "unable to produce chan sync msg") bobSyncMsg.NextLocalCommitHeight++ - _, _, _, err = aliceChannel.ProcessChanSyncMsg(bobSyncMsg) + _, _, _, err = aliceChannel.ProcessChanSyncMsg(ctxb, bobSyncMsg) if err != ErrCannotSyncCommitChains { t.Fatalf("wrong error, expected ErrCannotSyncCommitChains "+ "instead got: %v", err) @@ -4535,7 +4555,7 @@ func TestChanSyncFailure(t *testing.T) { bobSyncMsg, err = bobChannel.channelState.ChanSyncMsg() require.NoError(t, err, "unable to produce chan sync msg") bobSyncMsg.NextLocalCommitHeight-- - _, _, _, err = aliceChannel.ProcessChanSyncMsg(bobSyncMsg) + _, _, _, err = aliceChannel.ProcessChanSyncMsg(ctxb, bobSyncMsg) if err != ErrCommitSyncRemoteDataLoss { t.Fatalf("wrong error, expected ErrCommitSyncRemoteDataLoss "+ "instead got: %v", err) @@ -4551,7 +4571,7 @@ func TestChanSyncFailure(t *testing.T) { require.NoError(t, err, "unable to parse pubkey") bobSyncMsg.LocalUnrevokedCommitPoint = modCommitPoint - _, _, _, err = aliceChannel.ProcessChanSyncMsg(bobSyncMsg) + _, _, _, err = aliceChannel.ProcessChanSyncMsg(ctxb, bobSyncMsg) if err != ErrInvalidLocalUnrevokedCommitPoint { t.Fatalf("wrong error, expected "+ "ErrInvalidLocalUnrevokedCommitPoint instead got: %v", @@ -4571,7 +4591,7 @@ func TestChanSyncFailure(t *testing.T) { bobSyncMsg, err = bobChannel.channelState.ChanSyncMsg() require.NoError(t, err, "unable to produce chan sync msg") bobSyncMsg.LocalUnrevokedCommitPoint = modCommitPoint - _, _, _, err = aliceChannel.ProcessChanSyncMsg(bobSyncMsg) + _, _, _, err = aliceChannel.ProcessChanSyncMsg(ctxb, bobSyncMsg) if err != ErrInvalidLocalUnrevokedCommitPoint { t.Fatalf("wrong error, expected "+ "ErrInvalidLocalUnrevokedCommitPoint instead got: %v", @@ -4638,7 +4658,7 @@ func TestChannelRetransmissionFeeUpdate(t *testing.T) { // Now, Alice will send a new commitment to Bob, but we'll simulate a // connection failure, so Bob doesn't get her signature. - aliceNewCommit, err := aliceChannel.SignNextCommitment() + aliceNewCommit, err := aliceChannel.SignNextCommitment(ctxb) require.NoError(t, err, "unable to sign commitment") // Restart both channels to simulate a connection restart. @@ -4656,7 +4676,9 @@ func TestChannelRetransmissionFeeUpdate(t *testing.T) { require.NoError(t, err, "unable to produce chan sync msg") // Bob should detect that he doesn't need to send anything to Alice. - bobMsgsToSend, _, _, err := bobChannel.ProcessChanSyncMsg(aliceSyncMsg) + bobMsgsToSend, _, _, err := bobChannel.ProcessChanSyncMsg( + ctxb, aliceSyncMsg, + ) require.NoError(t, err, "unable to process chan sync msg") if len(bobMsgsToSend) != 0 { t.Fatalf("expected bob to send %v messages instead "+ @@ -4668,7 +4690,7 @@ func TestChannelRetransmissionFeeUpdate(t *testing.T) { // that she needs to first send a new UpdateFee message, and also a // CommitSig. aliceMsgsToSend, _, _, err := aliceChannel.ProcessChanSyncMsg( - bobSyncMsg, + ctxb, bobSyncMsg, ) require.NoError(t, err, "unable to process chan sync msg") if len(aliceMsgsToSend) != 2 { @@ -4724,7 +4746,7 @@ func TestChannelRetransmissionFeeUpdate(t *testing.T) { require.NoError(t, err, "bob unable to process alice's commitment") bobRevocation, _, _, err := bobChannel.RevokeCurrentCommitment() require.NoError(t, err, "unable to revoke bob commitment") - bobNewCommit, err := bobChannel.SignNextCommitment() + bobNewCommit, err := bobChannel.SignNextCommitment(ctxb) require.NoError(t, err, "bob unable to sign commitment") _, _, err = aliceChannel.ReceiveRevocation(bobRevocation) require.NoError(t, err, "alice unable to recv revocation") @@ -4855,7 +4877,7 @@ func TestFeeUpdateOldDiskFormat(t *testing.T) { // Now, Alice will send a new commitment to Bob, but we'll simulate a // connection failure, so Bob doesn't get the signature. - aliceNewCommitSig, err := aliceChannel.SignNextCommitment() + aliceNewCommitSig, err := aliceChannel.SignNextCommitment(ctxb) require.NoError(t, err, "unable to sign commitment") // Before restarting Alice, to mimic the old format, we fetch the @@ -4912,7 +4934,7 @@ func TestFeeUpdateOldDiskFormat(t *testing.T) { require.NoError(t, err, "bob unable to process alice's commitment") bobRevocation, _, _, err := bobChannel.RevokeCurrentCommitment() require.NoError(t, err, "unable to revoke bob commitment") - bobNewCommitSigs, err := bobChannel.SignNextCommitment() + bobNewCommitSigs, err := bobChannel.SignNextCommitment(ctxb) require.NoError(t, err, "bob unable to sign commitment") _, _, err = aliceChannel.ReceiveRevocation(bobRevocation) require.NoError(t, err, "alice unable to recv revocation") @@ -4988,11 +5010,11 @@ func TestChanSyncUnableToSync(t *testing.T) { NextLocalCommitHeight: 1000, RemoteCommitTailHeight: 9000, } - _, _, _, err = bobChannel.ProcessChanSyncMsg(badChanSync) + _, _, _, err = bobChannel.ProcessChanSyncMsg(ctxb, badChanSync) if err != ErrCannotSyncCommitChains { t.Fatalf("expected error instead have: %v", err) } - _, _, _, err = aliceChannel.ProcessChanSyncMsg(badChanSync) + _, _, _, err = aliceChannel.ProcessChanSyncMsg(ctxb, badChanSync) if err != ErrCannotSyncCommitChains { t.Fatalf("expected error instead have: %v", err) } @@ -5060,7 +5082,7 @@ func TestChanSyncInvalidLastSecret(t *testing.T) { // Alice's former self should conclude that she possibly lost data as // Bob is sending a valid commit secret for the latest state. - _, _, _, err = aliceOld.ProcessChanSyncMsg(bobChanSync) + _, _, _, err = aliceOld.ProcessChanSyncMsg(ctxb, bobChanSync) if _, ok := err.(*ErrCommitSyncLocalDataLoss); !ok { t.Fatalf("wrong error, expected ErrCommitSyncLocalDataLoss "+ "instead got: %v", err) @@ -5068,7 +5090,7 @@ func TestChanSyncInvalidLastSecret(t *testing.T) { // Bob should conclude that he should force close the channel, as Alice // cannot continue operation. - _, _, _, err = bobChannel.ProcessChanSyncMsg(aliceChanSync) + _, _, _, err = bobChannel.ProcessChanSyncMsg(ctxb, aliceChanSync) if err != ErrInvalidLastCommitSecret { t.Fatalf("wrong error, expected ErrInvalidLastCommitSecret, "+ "instead got: %v", err) @@ -5551,7 +5573,7 @@ func TestSignCommitmentFailNotLockedIn(t *testing.T) { // If we now try to initiate a state update, then it should fail as // Alice is unable to actually create a new state. - _, err = aliceChannel.SignNextCommitment() + _, err = aliceChannel.SignNextCommitment(ctxb) if err != ErrNoWindow { t.Fatalf("expected ErrNoWindow, instead have: %v", err) } @@ -5580,7 +5602,7 @@ func TestLockedInHtlcForwardingSkipAfterRestart(t *testing.T) { // We'll now manually initiate a state transition between Alice and // bob. - aliceNewCommit, err := aliceChannel.SignNextCommitment() + aliceNewCommit, err := aliceChannel.SignNextCommitment(ctxb) if err != nil { t.Fatal(err) } @@ -5609,7 +5631,7 @@ func TestLockedInHtlcForwardingSkipAfterRestart(t *testing.T) { // Now, have Bob initiate a transition to lock in the Adds sent by // Alice. - bobNewCommit, err := bobChannel.SignNextCommitment() + bobNewCommit, err := bobChannel.SignNextCommitment(ctxb) if err != nil { t.Fatal(err) } @@ -5654,7 +5676,7 @@ func TestLockedInHtlcForwardingSkipAfterRestart(t *testing.T) { // We'll now initiate another state transition, but this time Bob will // lead. - bobNewCommit, err = bobChannel.SignNextCommitment() + bobNewCommit, err = bobChannel.SignNextCommitment(ctxb) if err != nil { t.Fatal(err) } @@ -5689,7 +5711,7 @@ func TestLockedInHtlcForwardingSkipAfterRestart(t *testing.T) { // Now, begin another state transition led by Alice, and fail the second // HTLC part-way through the dance. - aliceNewCommit, err = aliceChannel.SignNextCommitment() + aliceNewCommit, err = aliceChannel.SignNextCommitment(ctxb) if err != nil { t.Fatal(err) } @@ -5762,7 +5784,7 @@ func TestLockedInHtlcForwardingSkipAfterRestart(t *testing.T) { // Have Alice initiate a state transition, which does not include the // HTLCs just re-added to the channel state. - aliceNewCommit, err = aliceChannel.SignNextCommitment() + aliceNewCommit, err = aliceChannel.SignNextCommitment(ctxb) if err != nil { t.Fatal(err) } @@ -5791,7 +5813,7 @@ func TestLockedInHtlcForwardingSkipAfterRestart(t *testing.T) { } // Now initiate a final update from Bob to lock in the final Fail. - bobNewCommit, err = bobChannel.SignNextCommitment() + bobNewCommit, err = bobChannel.SignNextCommitment(ctxb) if err != nil { t.Fatal(err) } @@ -5823,7 +5845,7 @@ func TestLockedInHtlcForwardingSkipAfterRestart(t *testing.T) { // Finally, have Bob initiate a state transition that locks in the Fail // added after the restart. - aliceNewCommit, err = aliceChannel.SignNextCommitment() + aliceNewCommit, err = aliceChannel.SignNextCommitment(ctxb) if err != nil { t.Fatal(err) } @@ -5885,7 +5907,7 @@ func TestInvalidCommitSigError(t *testing.T) { addAndReceiveHTLC(t, aliceChannel, bobChannel, htlc, nil) // Alice will now attempt to initiate a state transition. - aliceNewCommit, err := aliceChannel.SignNextCommitment() + aliceNewCommit, err := aliceChannel.SignNextCommitment(ctxb) require.NoError(t, err, "unable to sign new commit") // Before the signature gets to Bob, we'll mutate it, such that the @@ -6078,7 +6100,7 @@ func TestChannelUnilateralClosePendingCommit(t *testing.T) { // With the HTLC added, we'll now manually initiate a state transition // from Alice to Bob. - _, err = aliceChannel.SignNextCommitment() + _, err = aliceChannel.SignNextCommitment(ctxb) if err != nil { t.Fatal(err) } @@ -6312,7 +6334,7 @@ func TestMaxAcceptedHTLCs(t *testing.T) { addAndReceiveHTLC(t, aliceChannel, bobChannel, htlc, nil) // Add a commitment to Bob's commitment chain. - aliceNewCommit, err := aliceChannel.SignNextCommitment() + aliceNewCommit, err := aliceChannel.SignNextCommitment(ctxb) require.NoError(t, err, "unable to sign next commitment") err = bobChannel.ReceiveNewCommitment(aliceNewCommit.CommitSigs) require.NoError(t, err, "unable to recv new commitment") @@ -6407,7 +6429,7 @@ func TestMaxAsynchronousHtlcs(t *testing.T) { t.Fatalf("unable to receive fail htlc: %v", err) } - bobNewCommit, err := bobChannel.SignNextCommitment() + bobNewCommit, err := bobChannel.SignNextCommitment(ctxb) require.NoError(t, err, "unable to sign next commitment") err = aliceChannel.ReceiveNewCommitment(bobNewCommit.CommitSigs) @@ -6415,7 +6437,7 @@ func TestMaxAsynchronousHtlcs(t *testing.T) { // Cover the HTLC referenced with id equal to numHTLCs-1 with a new // signature (step 3). - aliceNewCommit, err := aliceChannel.SignNextCommitment() + aliceNewCommit, err := aliceChannel.SignNextCommitment(ctxb) require.NoError(t, err, "unable to sign next commitment") err = bobChannel.ReceiveNewCommitment(aliceNewCommit.CommitSigs) @@ -6440,7 +6462,7 @@ func TestMaxAsynchronousHtlcs(t *testing.T) { // Receiving the commitment should succeed as in step 7 since space was // made. - aliceNewCommit, err = aliceChannel.SignNextCommitment() + aliceNewCommit, err = aliceChannel.SignNextCommitment(ctxb) require.NoError(t, err, "unable to sign next commitment") err = bobChannel.ReceiveNewCommitment(aliceNewCommit.CommitSigs) @@ -7065,7 +7087,7 @@ func TestChannelRestoreUpdateLogs(t *testing.T) { addAndReceiveHTLC(t, aliceChannel, bobChannel, htlcAlice, nil) // Let Alice sign a new state, which will include the HTLC just sent. - aliceNewCommit, err := aliceChannel.SignNextCommitment() + aliceNewCommit, err := aliceChannel.SignNextCommitment(ctxb) require.NoError(t, err, "unable to sign commitment") // Bob receives this commitment signature, and revokes his old state. @@ -7095,7 +7117,7 @@ func TestChannelRestoreUpdateLogs(t *testing.T) { // and remote commit chains are updated in an async fashion. Since the // remote chain was updated with the latest state (since Bob sent the // revocation earlier) we can keep advancing the remote commit chain. - aliceNewCommit, err = aliceChannel.SignNextCommitment() + aliceNewCommit, err = aliceChannel.SignNextCommitment(ctxb) require.NoError(t, err, "unable to sign commitment") // After Alice has signed this commitment, her local commitment will @@ -7242,7 +7264,7 @@ func TestChannelRestoreUpdateLogsFailedHTLC(t *testing.T) { restoreAndAssert(t, aliceChannel, 1, 0, 0, 0) // Bob sends a signature. - bobNewCommit, err := bobChannel.SignNextCommitment() + bobNewCommit, err := bobChannel.SignNextCommitment(ctxb) require.NoError(t, err, "unable to sign commitment") err = aliceChannel.ReceiveNewCommitment(bobNewCommit.CommitSigs) require.NoError(t, err, "unable to receive commitment") @@ -7269,7 +7291,7 @@ func TestChannelRestoreUpdateLogsFailedHTLC(t *testing.T) { // Now send a signature from Alice. This will give Bob a new commitment // where the HTLC is removed. - aliceNewCommit, err := aliceChannel.SignNextCommitment() + aliceNewCommit, err := aliceChannel.SignNextCommitment(ctxb) require.NoError(t, err, "unable to sign commitment") err = bobChannel.ReceiveNewCommitment(aliceNewCommit.CommitSigs) require.NoError(t, err, "unable to receive commitment") @@ -7332,7 +7354,7 @@ func TestDuplicateFailRejection(t *testing.T) { // We'll now have Bob sign a new commitment to lock in the HTLC fail // for Alice. - _, err = bobChannel.SignNextCommitment() + _, err = bobChannel.SignNextCommitment(ctxb) require.NoError(t, err, "unable to sign commit") // We'll now force a restart for Bob and Alice, so we can test the @@ -7395,7 +7417,7 @@ func TestDuplicateSettleRejection(t *testing.T) { // We'll now have Bob sign a new commitment to lock in the HTLC fail // for Alice. - _, err = bobChannel.SignNextCommitment() + _, err = bobChannel.SignNextCommitment(ctxb) require.NoError(t, err, "unable to sign commit") // We'll now force a restart for Bob and Alice, so we can test the @@ -7476,7 +7498,7 @@ func TestChannelRestoreCommitHeight(t *testing.T) { addAndReceiveHTLC(t, aliceChannel, bobChannel, htlcAlice, nil) // Let Alice sign a new state, which will include the HTLC just sent. - aliceNewCommit, err := aliceChannel.SignNextCommitment() + aliceNewCommit, err := aliceChannel.SignNextCommitment(ctxb) require.NoError(t, err, "unable to sign commitment") // The HTLC should only be on the pending remote commitment, so the @@ -7508,7 +7530,7 @@ func TestChannelRestoreCommitHeight(t *testing.T) { // Now let Bob send the commitment signature making the HTLC lock in on // Alice's commitment. - bobNewCommit, err := bobChannel.SignNextCommitment() + bobNewCommit, err := bobChannel.SignNextCommitment(ctxb) require.NoError(t, err, "unable to sign commitment") // At this stage Bob has a pending remote commitment. Make sure @@ -7541,7 +7563,7 @@ func TestChannelRestoreCommitHeight(t *testing.T) { // Send a new signature from Alice to Bob, making Alice have a pending // remote commitment. - aliceNewCommit, err = aliceChannel.SignNextCommitment() + aliceNewCommit, err = aliceChannel.SignNextCommitment(ctxb) require.NoError(t, err, "unable to sign commitment") // A restoration should keep the add heights iof the first HTLC, and @@ -7580,7 +7602,7 @@ func TestChannelRestoreCommitHeight(t *testing.T) { // Sign a new state for Alice, making Bob have a pending remote // commitment. - bobNewCommit, err = bobChannel.SignNextCommitment() + bobNewCommit, err = bobChannel.SignNextCommitment(ctxb) require.NoError(t, err, "unable to sign commitment") // The signing of a new commitment for Alice should have given the new @@ -7617,7 +7639,7 @@ func TestChannelRestoreCommitHeight(t *testing.T) { require.NoError(t, err, "unable to recv htlc cancel") // Now Bob signs for the fail update. - bobNewCommit, err = bobChannel.SignNextCommitment() + bobNewCommit, err = bobChannel.SignNextCommitment(ctxb) require.NoError(t, err, "unable to sign commitment") // Bob has a pending commitment for Alice, it shouldn't affect the add @@ -7675,13 +7697,13 @@ func TestForceCloseBorkedState(t *testing.T) { // Do the commitment dance until Bob sends a revocation so Alice is // able to receive the revocation, and then also make a new state // herself. - aliceNewCommit, err := aliceChannel.SignNextCommitment() + aliceNewCommit, err := aliceChannel.SignNextCommitment(ctxb) require.NoError(t, err, "unable to sign commit") err = bobChannel.ReceiveNewCommitment(aliceNewCommit.CommitSigs) require.NoError(t, err, "unable to receive commitment") revokeMsg, _, _, err := bobChannel.RevokeCurrentCommitment() require.NoError(t, err, "unable to revoke bob commitment") - bobNewCommit, err := bobChannel.SignNextCommitment() + bobNewCommit, err := bobChannel.SignNextCommitment(ctxb) require.NoError(t, err, "unable to sign commit") err = aliceChannel.ReceiveNewCommitment(bobNewCommit.CommitSigs) require.NoError(t, err, "unable to receive commitment") @@ -7715,7 +7737,7 @@ func TestForceCloseBorkedState(t *testing.T) { // We manually advance the commitment tail here since the above // ReceiveRevocation call will fail before it's actually advanced. aliceChannel.commitChains.Remote.advanceTail() - _, err = aliceChannel.SignNextCommitment() + _, err = aliceChannel.SignNextCommitment(ctxb) if err != channeldb.ErrChanBorked { t.Fatalf("sign commitment should have failed: %v", err) } @@ -8181,7 +8203,7 @@ func TestChannelFeeRateFloor(t *testing.T) { } // Check that alice can still sign commitments. - aliceNewCommit, err := alice.SignNextCommitment() + aliceNewCommit, err := alice.SignNextCommitment(ctxb) require.NoError(t, err, "alice unable to sign commitment") // Check that bob can still receive commitments. @@ -9488,7 +9510,7 @@ func TestChannelUnsignedAckedFailure(t *testing.T) { // Bob should send a commitment signature to Alice. // <----sig------ - bobNewCommit, err := bobChannel.SignNextCommitment() + bobNewCommit, err := bobChannel.SignNextCommitment(ctxb) require.NoError(t, err) err = aliceChannel.ReceiveNewCommitment(bobNewCommit.CommitSigs) require.NoError(t, err) @@ -9503,7 +9525,7 @@ func TestChannelUnsignedAckedFailure(t *testing.T) { // Alice should sign the next commitment and go down before // sending it. // -----sig-----X - aliceNewCommit, err := aliceChannel.SignNextCommitment() + aliceNewCommit, err := aliceChannel.SignNextCommitment(ctxb) require.NoError(t, err) newAliceChannel, err := NewLightningChannel( @@ -9534,7 +9556,7 @@ func TestChannelUnsignedAckedFailure(t *testing.T) { // Bob sends the final signature to Alice and Alice should not // reject it, given that we properly restore the unsigned acked // updates and therefore our update log is structured correctly. - bobNewCommit, err = bobChannel.SignNextCommitment() + bobNewCommit, err = bobChannel.SignNextCommitment(ctxb) require.NoError(t, err) err = newAliceChannel.ReceiveNewCommitment(bobNewCommit.CommitSigs) require.NoError(t, err) @@ -9592,7 +9614,7 @@ func TestChannelLocalUnsignedUpdatesFailure(t *testing.T) { // Alice should send a commitment signature to Bob. // -----sig----> - aliceNewCommit, err := aliceChannel.SignNextCommitment() + aliceNewCommit, err := aliceChannel.SignNextCommitment(ctxb) require.NoError(t, err) err = bobChannel.ReceiveNewCommitment(aliceNewCommit.CommitSigs) require.NoError(t, err) @@ -9616,7 +9638,7 @@ func TestChannelLocalUnsignedUpdatesFailure(t *testing.T) { // Bob sends the final signature and Alice should not reject it. // <----sig----- - bobNewCommit, err := bobChannel.SignNextCommitment() + bobNewCommit, err := bobChannel.SignNextCommitment(ctxb) require.NoError(t, err) err = newAliceChannel.ReceiveNewCommitment(bobNewCommit.CommitSigs) require.NoError(t, err) @@ -9675,7 +9697,7 @@ func TestChannelSignedAckRegression(t *testing.T) { require.NoError(t, err) // -----sig----> - aliceNewCommit, err := aliceChannel.SignNextCommitment() + aliceNewCommit, err := aliceChannel.SignNextCommitment(ctxb) require.NoError(t, err) err = bobChannel.ReceiveNewCommitment(aliceNewCommit.CommitSigs) require.NoError(t, err) @@ -9687,7 +9709,7 @@ func TestChannelSignedAckRegression(t *testing.T) { require.NoError(t, err) // <----sig----- - bobNewCommit, err := bobChannel.SignNextCommitment() + bobNewCommit, err := bobChannel.SignNextCommitment(ctxb) require.NoError(t, err) err = aliceChannel.ReceiveNewCommitment(bobNewCommit.CommitSigs) require.NoError(t, err) @@ -9699,7 +9721,7 @@ func TestChannelSignedAckRegression(t *testing.T) { addAndReceiveHTLC(t, aliceChannel, bobChannel, htlc2, nil) // -----sig----> - aliceNewCommit, err = aliceChannel.SignNextCommitment() + aliceNewCommit, err = aliceChannel.SignNextCommitment(ctxb) require.NoError(t, err) err = bobChannel.ReceiveNewCommitment(aliceNewCommit.CommitSigs) require.NoError(t, err) @@ -9729,7 +9751,7 @@ func TestChannelSignedAckRegression(t *testing.T) { // Bob should no longer fail to sign this commitment due to faulty // update logs. // <----sig----- - bobNewCommit, err = newBobChannel.SignNextCommitment() + bobNewCommit, err = newBobChannel.SignNextCommitment(ctxb) require.NoError(t, err) // Alice should receive the new commitment without hiccups. @@ -9800,7 +9822,7 @@ func TestIsChannelClean(t *testing.T) { // removed from both commitments. // ---sig---> - aliceNewCommit, err := aliceChannel.SignNextCommitment() + aliceNewCommit, err := aliceChannel.SignNextCommitment(ctxb) require.NoError(t, err) err = bobChannel.ReceiveNewCommitment(aliceNewCommit.CommitSigs) require.NoError(t, err) @@ -9814,7 +9836,7 @@ func TestIsChannelClean(t *testing.T) { assertCleanOrDirty(false, aliceChannel, bobChannel, t) // <---sig--- - bobNewCommit, err := bobChannel.SignNextCommitment() + bobNewCommit, err := bobChannel.SignNextCommitment(ctxb) require.NoError(t, err) err = aliceChannel.ReceiveNewCommitment(bobNewCommit.CommitSigs) require.NoError(t, err) @@ -9835,7 +9857,7 @@ func TestIsChannelClean(t *testing.T) { assertCleanOrDirty(false, aliceChannel, bobChannel, t) // <---sig--- - bobNewCommit, err = bobChannel.SignNextCommitment() + bobNewCommit, err = bobChannel.SignNextCommitment(ctxb) require.NoError(t, err) err = aliceChannel.ReceiveNewCommitment(bobNewCommit.CommitSigs) require.NoError(t, err) @@ -9849,7 +9871,7 @@ func TestIsChannelClean(t *testing.T) { assertCleanOrDirty(false, aliceChannel, bobChannel, t) // ---sig---> - aliceNewCommit, err = aliceChannel.SignNextCommitment() + aliceNewCommit, err = aliceChannel.SignNextCommitment(ctxb) require.NoError(t, err) err = bobChannel.ReceiveNewCommitment(aliceNewCommit.CommitSigs) require.NoError(t, err) @@ -9873,7 +9895,7 @@ func TestIsChannelClean(t *testing.T) { assertCleanOrDirty(false, aliceChannel, bobChannel, t) // ---sig---> - aliceNewCommit, err = aliceChannel.SignNextCommitment() + aliceNewCommit, err = aliceChannel.SignNextCommitment(ctxb) require.NoError(t, err) err = bobChannel.ReceiveNewCommitment(aliceNewCommit.CommitSigs) require.NoError(t, err) @@ -9887,7 +9909,7 @@ func TestIsChannelClean(t *testing.T) { assertCleanOrDirty(false, aliceChannel, bobChannel, t) // <---sig--- - bobNewCommit, err = bobChannel.SignNextCommitment() + bobNewCommit, err = bobChannel.SignNextCommitment(ctxb) require.NoError(t, err) err = aliceChannel.ReceiveNewCommitment(bobNewCommit.CommitSigs) require.NoError(t, err) @@ -10022,7 +10044,7 @@ func testGetDustSum(t *testing.T, chantype channeldb.ChannelType) { checkDust(bobChannel, htlc2Amt, htlc2Amt) // Alice signs for this HTLC and neither perspective should change. - aliceNewCommit, err := aliceChannel.SignNextCommitment() + aliceNewCommit, err := aliceChannel.SignNextCommitment(ctxb) require.NoError(t, err) err = bobChannel.ReceiveNewCommitment(aliceNewCommit.CommitSigs) require.NoError(t, err) @@ -10041,7 +10063,7 @@ func testGetDustSum(t *testing.T, chantype channeldb.ChannelType) { // The rest of the dance is completed and neither perspective should // change. - bobNewCommit, err := bobChannel.SignNextCommitment() + bobNewCommit, err := bobChannel.SignNextCommitment(ctxb) require.NoError(t, err) err = aliceChannel.ReceiveNewCommitment(bobNewCommit.CommitSigs) require.NoError(t, err) @@ -10862,7 +10884,7 @@ func TestAsynchronousSendingContraint(t *testing.T) { // Bob signs the new state for alice, which ONLY has his htlc on it // because he only includes acked updates of alice. // <----sig-------|--------------- - bobNewCommit, err := bobChannel.SignNextCommitment() + bobNewCommit, err := bobChannel.SignNextCommitment(ctxb) require.NoError(t, err) err = aliceChannel.ReceiveNewCommitment(bobNewCommit.CommitSigs) @@ -10878,7 +10900,7 @@ func TestAsynchronousSendingContraint(t *testing.T) { // incoming htlc in her commitment sig to bob, but this will dip her // local balance below her reserve because she already used everything // up when adding her htlc. - _, err = aliceChannel.SignNextCommitment() + _, err = aliceChannel.SignNextCommitment(ctxb) require.ErrorIs(t, err, ErrBelowChanReserve) } @@ -11003,7 +11025,7 @@ func TestAsynchronousSendingWithFeeBuffer(t *testing.T) { // Force a state transition, this will lock-in the htlc of bob. // ------sig-----> (includes bob's htlc) // <----rev------ (locks in bob's htlc for alice) - aliceNewCommit, err := aliceChannel.SignNextCommitment() + aliceNewCommit, err := aliceChannel.SignNextCommitment(ctxb) require.NoError(t, err) err = bobChannel.ReceiveNewCommitment(aliceNewCommit.CommitSigs) require.NoError(t, err) @@ -11245,7 +11267,7 @@ func TestBlindingPointPersistence(t *testing.T) { // Now, Alice will send a new commitment to Bob, which will persist our // pending HTLC to disk. - aliceCommit, err := aliceChannel.SignNextCommitment() + aliceCommit, err := aliceChannel.SignNextCommitment(ctxb) require.NoError(t, err, "unable to sign commitment") // Restart alice to force fetching state from disk. diff --git a/lnwallet/test_utils.go b/lnwallet/test_utils.go index b450b3b303..2253232962 100644 --- a/lnwallet/test_utils.go +++ b/lnwallet/test_utils.go @@ -2,6 +2,7 @@ package lnwallet import ( "bytes" + "context" "crypto/rand" "encoding/binary" "encoding/hex" @@ -103,6 +104,10 @@ var ( bobDustLimit = btcutil.Amount(1300) testChannelCapacity float64 = 10 + + // ctxb is a context that will never be cancelled, that is used in + // place of a real quit context. + ctxb = context.Background() ) // CreateTestChannels creates to fully populated channels to be used within @@ -557,7 +562,7 @@ func calcStaticFee(chanType channeldb.ChannelType, numHTLCs int) btcutil.Amount // pending updates. This method is useful when testing interactions between two // live state machines. func ForceStateTransition(chanA, chanB *LightningChannel) error { - aliceNewCommit, err := chanA.SignNextCommitment() + aliceNewCommit, err := chanA.SignNextCommitment(ctxb) if err != nil { return err } @@ -570,7 +575,7 @@ func ForceStateTransition(chanA, chanB *LightningChannel) error { if err != nil { return err } - bobNewCommit, err := chanB.SignNextCommitment() + bobNewCommit, err := chanB.SignNextCommitment(ctxb) if err != nil { return err } diff --git a/lnwallet/transactions_test.go b/lnwallet/transactions_test.go index 8786c2d5dc..0f7988a32c 100644 --- a/lnwallet/transactions_test.go +++ b/lnwallet/transactions_test.go @@ -357,7 +357,7 @@ func testVectors(t *testing.T, chanType channeldb.ChannelType, test testCase) { // Execute commit dance to arrive at the point where the local node has // received the test commitment and the remote signature. - localNewCommit, err := localChannel.SignNextCommitment() + localNewCommit, err := localChannel.SignNextCommitment(ctxb) require.NoError(t, err, "local unable to sign commitment") err = remoteChannel.ReceiveNewCommitment(localNewCommit.CommitSigs) @@ -369,7 +369,7 @@ func testVectors(t *testing.T, chanType channeldb.ChannelType, test testCase) { _, _, err = localChannel.ReceiveRevocation(revMsg) require.NoError(t, err) - remoteNewCommit, err := remoteChannel.SignNextCommitment() + remoteNewCommit, err := remoteChannel.SignNextCommitment(ctxb) require.NoError(t, err) require.Equal( From 455cdffede6bb79e474e8b8bdd8fa04946da2a26 Mon Sep 17 00:00:00 2001 From: Jonathan Harvey-Buschel Date: Thu, 17 Oct 2024 13:38:35 +0200 Subject: [PATCH 190/218] lnwallet: test link quit signal handling --- lnwallet/channel_test.go | 71 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) diff --git a/lnwallet/channel_test.go b/lnwallet/channel_test.go index 553c3b2f97..f6dd434f67 100644 --- a/lnwallet/channel_test.go +++ b/lnwallet/channel_test.go @@ -2,6 +2,7 @@ package lnwallet import ( "bytes" + "context" crand "crypto/rand" "crypto/sha256" "errors" @@ -11,6 +12,7 @@ import ( "runtime" "testing" "testing/quick" + "time" "github.com/btcsuite/btcd/blockchain" "github.com/btcsuite/btcd/btcec/v2" @@ -3582,6 +3584,75 @@ func TestAuxSignerShutdown(t *testing.T) { require.ErrorIs(t, err, auxSignerShutdownErr) } +// TestQuitDuringSignNextCommitment tests that the channel state machine can +// successfully exit on receiving a quit signal when signing a new commitment. +func TestQuitDuringSignNextCommitment(t *testing.T) { + t.Parallel() + + // We'll kick off the test by creating our channels which both are + // loaded with 5 BTC each. + aliceChannel, bobChannel, err := CreateTestChannels( + t, channeldb.SingleFunderTweaklessBit, + ) + require.NoError(t, err, "unable to create test channels") + + // We'll simulate an aux signer that was started successfully, but is + // now frozen / inactive. This could happen if the aux signer shut down + // without sending an error on any aux sig job error channel. + noopAuxSigJob := func(jobs []AuxSigJob) {} + + auxSigner := NewAuxSignerMock(noopAuxSigJob) + aliceChannel.auxSigner = fn.Some[AuxSigner](auxSigner) + + // Each HTLC amount is 0.01 BTC. + htlcAmt := lnwire.NewMSatFromSatoshis(0.01 * btcutil.SatoshiPerBitcoin) + + // Create enough HTLCs to create multiple sig jobs (one job per HTLC). + const numHTLCs = 24 + + // Send the specified number of HTLCs. + for i := 0; i < numHTLCs; i++ { + htlc, _ := createHTLC(i, htlcAmt) + addAndReceiveHTLC(t, aliceChannel, bobChannel, htlc, nil) + } + + // We'll set up the mock aux signer to expect calls to PackSigs and also + // SubmitSecondLevelSigBatch. The direct return values for this mock aux + // signer are nil. The expected error comes from the behavior of + // noopAuxSigJob above, which mimics a faulty aux signer. + var sigBlobBuf bytes.Buffer + sigBlob := testSigBlob{ + BlobInt: tlv.NewPrimitiveRecord[tlv.TlvType65634, uint16](5), + } + tlvStream, err := tlv.NewStream(sigBlob.BlobInt.Record()) + require.NoError(t, err, "unable to create tlv stream") + require.NoError(t, tlvStream.Encode(&sigBlobBuf)) + + auxSigner.On( + "SubmitSecondLevelSigBatch", mock.Anything, mock.Anything, + mock.Anything, + ).Return(nil).Twice() + auxSigner.On( + "PackSigs", mock.Anything, + ).Return( + fn.Some(sigBlobBuf.Bytes()), nil, + ) + + quitDelay := time.Millisecond * 20 + quit, quitFunc := context.WithCancel(context.Background()) + + // Alice's channel will be stuck waiting for aux sig job responses until + // we send the quit signal. We add an explicit sleep here so that we can + // cause a failure if we run the test with a very short timeout. + go func() { + time.Sleep(quitDelay) + quitFunc() + }() + + _, err = aliceChannel.SignNextCommitment(quit) + require.ErrorIs(t, err, errQuit) +} + func testChanSyncOweCommitmentPendingRemote(t *testing.T, chanType channeldb.ChannelType, ) { From d650829f9b83491869f3ea47808063b46e71c5f5 Mon Sep 17 00:00:00 2001 From: Jonathan Harvey-Buschel Date: Thu, 17 Oct 2024 13:38:36 +0200 Subject: [PATCH 191/218] gitignore: ignore vscode workspace files --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index a44a7b8f50..6be439ed7e 100644 --- a/.gitignore +++ b/.gitignore @@ -66,6 +66,7 @@ profile.tmp .DS_Store .vscode +*.code-workspace # Coverage test coverage.txt From 2999b37607f86475b0be766115088c512594117f Mon Sep 17 00:00:00 2001 From: George Tsagkarelis Date: Fri, 18 Oct 2024 12:40:50 +0200 Subject: [PATCH 192/218] lnrpc+invoices: add cancelSet to HtlcModifier interface --- invoices/interface.go | 6 +++++ lnrpc/invoicesrpc/htlc_modifier.go | 1 + lnrpc/invoicesrpc/invoices.pb.go | 30 ++++++++++++++++++------- lnrpc/invoicesrpc/invoices.proto | 6 +++++ lnrpc/invoicesrpc/invoices.swagger.json | 4 ++++ 5 files changed, 39 insertions(+), 8 deletions(-) diff --git a/invoices/interface.go b/invoices/interface.go index 4f5e08e32e..c906da1c3f 100644 --- a/invoices/interface.go +++ b/invoices/interface.go @@ -247,6 +247,12 @@ type HtlcModifyResponse struct { // HTLC was originally sent with, in case additional value is carried // along with it (which might be the case in custom channels). AmountPaid lnwire.MilliSatoshi + + // CancelSet is a flag the interceptor client can set to force a + // cancellation of all HTLCs associated with the invoice that are + // currently accepted. Setting this field will ignore the AmountPaid + // field. + CancelSet bool } // HtlcModifyCallback is a function that is called when an invoice is diff --git a/lnrpc/invoicesrpc/htlc_modifier.go b/lnrpc/invoicesrpc/htlc_modifier.go index ce61af4680..00259962cf 100644 --- a/lnrpc/invoicesrpc/htlc_modifier.go +++ b/lnrpc/invoicesrpc/htlc_modifier.go @@ -82,5 +82,6 @@ func (r *htlcModifier) onIntercept( return &invoices.HtlcModifyResponse{ AmountPaid: amtPaid, + CancelSet: resp.CancelSet, }, nil } diff --git a/lnrpc/invoicesrpc/invoices.pb.go b/lnrpc/invoicesrpc/invoices.pb.go index c23d3a6694..ca2b54e7d1 100644 --- a/lnrpc/invoicesrpc/invoices.pb.go +++ b/lnrpc/invoicesrpc/invoices.pb.go @@ -782,6 +782,11 @@ type HtlcModifyResponse struct { // HTLC carries other valuable items, as can be the case with custom channel // types. AmtPaid *uint64 `protobuf:"varint,2,opt,name=amt_paid,json=amtPaid,proto3,oneof" json:"amt_paid,omitempty"` + // This flag indicates whether the HTLCs associated with the invoices should + // be cancelled. The interceptor client may set this field if some + // unexpected behavior is encountered. Setting this will ignore the amt_paid + // field. + CancelSet bool `protobuf:"varint,3,opt,name=cancel_set,json=cancelSet,proto3" json:"cancel_set,omitempty"` } func (x *HtlcModifyResponse) Reset() { @@ -830,6 +835,13 @@ func (x *HtlcModifyResponse) GetAmtPaid() uint64 { return 0 } +func (x *HtlcModifyResponse) GetCancelSet() bool { + if x != nil { + return x.CancelSet + } + return false +} + var File_invoicesrpc_invoices_proto protoreflect.FileDescriptor var file_invoicesrpc_invoices_proto_rawDesc = []byte{ @@ -924,14 +936,16 @@ var file_invoicesrpc_invoices_proto_rawDesc = []byte{ 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, - 0x38, 0x01, 0x22, 0x7b, 0x0a, 0x12, 0x48, 0x74, 0x6c, 0x63, 0x4d, 0x6f, 0x64, 0x69, 0x66, 0x79, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x38, 0x0a, 0x0b, 0x63, 0x69, 0x72, 0x63, - 0x75, 0x69, 0x74, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, - 0x69, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x73, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x69, 0x72, 0x63, - 0x75, 0x69, 0x74, 0x4b, 0x65, 0x79, 0x52, 0x0a, 0x63, 0x69, 0x72, 0x63, 0x75, 0x69, 0x74, 0x4b, - 0x65, 0x79, 0x12, 0x1e, 0x0a, 0x08, 0x61, 0x6d, 0x74, 0x5f, 0x70, 0x61, 0x69, 0x64, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x04, 0x48, 0x00, 0x52, 0x07, 0x61, 0x6d, 0x74, 0x50, 0x61, 0x69, 0x64, 0x88, - 0x01, 0x01, 0x42, 0x0b, 0x0a, 0x09, 0x5f, 0x61, 0x6d, 0x74, 0x5f, 0x70, 0x61, 0x69, 0x64, 0x2a, + 0x38, 0x01, 0x22, 0x9a, 0x01, 0x0a, 0x12, 0x48, 0x74, 0x6c, 0x63, 0x4d, 0x6f, 0x64, 0x69, 0x66, + 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x38, 0x0a, 0x0b, 0x63, 0x69, 0x72, + 0x63, 0x75, 0x69, 0x74, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, + 0x2e, 0x69, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x73, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x69, 0x72, + 0x63, 0x75, 0x69, 0x74, 0x4b, 0x65, 0x79, 0x52, 0x0a, 0x63, 0x69, 0x72, 0x63, 0x75, 0x69, 0x74, + 0x4b, 0x65, 0x79, 0x12, 0x1e, 0x0a, 0x08, 0x61, 0x6d, 0x74, 0x5f, 0x70, 0x61, 0x69, 0x64, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x04, 0x48, 0x00, 0x52, 0x07, 0x61, 0x6d, 0x74, 0x50, 0x61, 0x69, 0x64, + 0x88, 0x01, 0x01, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x5f, 0x73, 0x65, + 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x63, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x53, + 0x65, 0x74, 0x42, 0x0b, 0x0a, 0x09, 0x5f, 0x61, 0x6d, 0x74, 0x5f, 0x70, 0x61, 0x69, 0x64, 0x2a, 0x44, 0x0a, 0x0e, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x4d, 0x6f, 0x64, 0x69, 0x66, 0x69, 0x65, 0x72, 0x12, 0x0b, 0x0a, 0x07, 0x44, 0x45, 0x46, 0x41, 0x55, 0x4c, 0x54, 0x10, 0x00, 0x12, 0x11, 0x0a, 0x0d, 0x48, 0x54, 0x4c, 0x43, 0x5f, 0x53, 0x45, 0x54, 0x5f, 0x4f, 0x4e, 0x4c, 0x59, 0x10, diff --git a/lnrpc/invoicesrpc/invoices.proto b/lnrpc/invoicesrpc/invoices.proto index 539645f4af..d9d3bc1236 100644 --- a/lnrpc/invoicesrpc/invoices.proto +++ b/lnrpc/invoicesrpc/invoices.proto @@ -242,4 +242,10 @@ message HtlcModifyResponse { // HTLC carries other valuable items, as can be the case with custom channel // types. optional uint64 amt_paid = 2; + + // This flag indicates whether the HTLCs associated with the invoices should + // be cancelled. The interceptor client may set this field if some + // unexpected behavior is encountered. Setting this will ignore the amt_paid + // field. + bool cancel_set = 3; } diff --git a/lnrpc/invoicesrpc/invoices.swagger.json b/lnrpc/invoicesrpc/invoices.swagger.json index 4e8cb6ac5f..fba415fb36 100644 --- a/lnrpc/invoicesrpc/invoices.swagger.json +++ b/lnrpc/invoicesrpc/invoices.swagger.json @@ -423,6 +423,10 @@ "type": "string", "format": "uint64", "description": "The modified amount in milli-satoshi that the exit HTLC is paying. This\nvalue can be different from the actual on-chain HTLC amount, in case the\nHTLC carries other valuable items, as can be the case with custom channel\ntypes." + }, + "cancel_set": { + "type": "boolean", + "description": "This flag indicates whether the HTLCs associated with the invoices should\nbe cancelled. The interceptor client may set this field if some\nunexpected behavior is encountered. Setting this will ignore the amt_paid\nfield." } } }, From 6f401b4265a63b386f43c625273f9c8f7e9235f0 Mon Sep 17 00:00:00 2001 From: George Tsagkarelis Date: Fri, 18 Oct 2024 12:44:58 +0200 Subject: [PATCH 193/218] invoices: cancel htlc on HtlcModify signal --- invoices/invoiceregistry.go | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/invoices/invoiceregistry.go b/invoices/invoiceregistry.go index 517e237ca6..9d54b6ad8d 100644 --- a/invoices/invoiceregistry.go +++ b/invoices/invoiceregistry.go @@ -1049,6 +1049,8 @@ func (i *InvoiceRegistry) notifyExitHopHtlcLocked( return nil, nil, err } + var cancelSet bool + // Provide the invoice to the settlement interceptor to allow // the interceptor's client an opportunity to manipulate the // settlement process. @@ -1066,6 +1068,8 @@ func (i *InvoiceRegistry) notifyExitHopHtlcLocked( if resp.AmountPaid != 0 { ctx.amtPaid = resp.AmountPaid } + + cancelSet = resp.CancelSet }) if err != nil { err := fmt.Errorf("error during invoice HTLC interception: %w", @@ -1092,7 +1096,18 @@ func (i *InvoiceRegistry) notifyExitHopHtlcLocked( updateDesc.State != nil // Assign resolution to outer scope variable. - resolution = res + if cancelSet { + // If a cancel signal was set for the htlc set, we set + // the resolution as a failure with an underpayment + // indication. Something was wrong with this htlc, so + // we probably can't settle the invoice at all. + resolution = NewFailResolution( + ctx.circuitKey, ctx.currentHeight, + ResultAmountTooLow, + ) + } else { + resolution = res + } return updateDesc, nil } From 552d1825945386265569bb73afa0a4518c919f43 Mon Sep 17 00:00:00 2001 From: Boris Nagaev Date: Fri, 25 Oct 2024 20:28:17 -0300 Subject: [PATCH 194/218] lnwallet: fix closechannel for P2TR external addr If the delivery address is P2TR, function InternalKeyForAddr checks its existance in the wallet to return internal key for it in case it is a custom taproot channel. It used to return the error returned by wallet.AddressInfo. The error is now ignored if it is ErrAddressNotFound error. This fixes "lncli closechannel --delivery_addr Date: Fri, 25 Oct 2024 20:12:15 -0300 Subject: [PATCH 195/218] itest: coop_close_with_external_delivery with p2tr Customize the itest with the type of external delivery address. Test with P2TR address type in addition to P2WKPH. --- .../lnd_coop_close_external_delivery_test.go | 43 ++++++++++++++++--- 1 file changed, 37 insertions(+), 6 deletions(-) diff --git a/itest/lnd_coop_close_external_delivery_test.go b/itest/lnd_coop_close_external_delivery_test.go index 7557923034..57c2da2f4f 100644 --- a/itest/lnd_coop_close_external_delivery_test.go +++ b/itest/lnd_coop_close_external_delivery_test.go @@ -10,13 +10,44 @@ import ( ) func testCoopCloseWithExternalDelivery(ht *lntest.HarnessTest) { - ht.Run("set delivery address at open", func(t *testing.T) { + ok := ht.Run("set P2WPKH delivery address at open", func(t *testing.T) { tt := ht.Subtest(t) - testCoopCloseWithExternalDeliveryImpl(tt, true) + testCoopCloseWithExternalDeliveryImpl( + tt, true, lnrpc.AddressType_UNUSED_WITNESS_PUBKEY_HASH, + ) }) - ht.Run("set delivery address at close", func(t *testing.T) { + // Abort the test if failed. + if !ok { + return + } + + ok = ht.Run("set P2WPKH delivery address at close", func(t *testing.T) { + tt := ht.Subtest(t) + testCoopCloseWithExternalDeliveryImpl( + tt, false, lnrpc.AddressType_UNUSED_WITNESS_PUBKEY_HASH, + ) + }) + // Abort the test if failed. + if !ok { + return + } + + ok = ht.Run("set P2TR delivery address at open", func(t *testing.T) { + tt := ht.Subtest(t) + testCoopCloseWithExternalDeliveryImpl( + tt, true, lnrpc.AddressType_UNUSED_TAPROOT_PUBKEY, + ) + }) + // Abort the test if failed. + if !ok { + return + } + + ht.Run("set P2TR delivery address at close", func(t *testing.T) { tt := ht.Subtest(t) - testCoopCloseWithExternalDeliveryImpl(tt, false) + testCoopCloseWithExternalDeliveryImpl( + tt, false, lnrpc.AddressType_UNUSED_TAPROOT_PUBKEY, + ) }) } @@ -25,7 +56,7 @@ func testCoopCloseWithExternalDelivery(ht *lntest.HarnessTest) { // not. Some users set this value to be an address in a different wallet and // this should not affect our ability to accurately report the settled balance. func testCoopCloseWithExternalDeliveryImpl(ht *lntest.HarnessTest, - upfrontShutdown bool) { + upfrontShutdown bool, deliveryAddressType lnrpc.AddressType) { alice, bob := ht.Alice, ht.Bob ht.ConnectNodes(alice, bob) @@ -35,7 +66,7 @@ func testCoopCloseWithExternalDeliveryImpl(ht *lntest.HarnessTest, // wallet. We already correctly track settled balances when the address // is in the LND wallet. addr := bob.RPC.NewAddress(&lnrpc.NewAddressRequest{ - Type: lnrpc.AddressType_UNUSED_WITNESS_PUBKEY_HASH, + Type: deliveryAddressType, }) // Prepare for channel open. From 941590d384ff896ad5074001b04832ae5ef9d84d Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Fri, 6 Sep 2024 18:19:49 -0700 Subject: [PATCH 196/218] contractcourt: use t.Run in TestHtlcTimeoutResolver Along the way we refactor the test to eliminate some unnecessary line length. --- contractcourt/htlc_timeout_resolver_test.go | 479 +++++++++++--------- 1 file changed, 253 insertions(+), 226 deletions(-) diff --git a/contractcourt/htlc_timeout_resolver_test.go b/contractcourt/htlc_timeout_resolver_test.go index c551a6f1ce..8ccd4aaaca 100644 --- a/contractcourt/htlc_timeout_resolver_test.go +++ b/contractcourt/htlc_timeout_resolver_test.go @@ -69,11 +69,31 @@ func (m *mockWitnessBeacon) AddPreimages(preimages ...lntypes.Preimage) error { return nil } -// TestHtlcTimeoutResolver tests that the timeout resolver properly handles all -// variations of possible local+remote spends. -func TestHtlcTimeoutResolver(t *testing.T) { - t.Parallel() +type htlcTimeoutTestCase struct { + // name is a human readable description of the test case. + name string + + // remoteCommit denotes if the commitment broadcast was the remote + // commitment or not. + remoteCommit bool + + // timeout denotes if the HTLC should be let timeout, or if the "remote" + // party should sweep it on-chain. This also affects what type of + // resolution message we expect. + timeout bool + + // txToBroadcast is a function closure that should generate the + // transaction that should spend the HTLC output. Test authors can use + // this to customize the witness used when spending to trigger various + // redemption cases. + txToBroadcast func() (*wire.MsgTx, error) + + // outcome is the resolver outcome that we expect to be reported once + // the contract is fully resolved. + outcome channeldb.ResolverOutcome +} +func genHtlcTimeoutTestCases() []htlcTimeoutTestCase { fakePreimageBytes := bytes.Repeat([]byte{1}, lntypes.HashSize) var ( @@ -105,29 +125,7 @@ func TestHtlcTimeoutResolver(t *testing.T) { }, } - testCases := []struct { - // name is a human readable description of the test case. - name string - - // remoteCommit denotes if the commitment broadcast was the - // remote commitment or not. - remoteCommit bool - - // timeout denotes if the HTLC should be let timeout, or if the - // "remote" party should sweep it on-chain. This also affects - // what type of resolution message we expect. - timeout bool - - // txToBroadcast is a function closure that should generate the - // transaction that should spend the HTLC output. Test authors - // can use this to customize the witness used when spending to - // trigger various redemption cases. - txToBroadcast func() (*wire.MsgTx, error) - - // outcome is the resolver outcome that we expect to be reported - // once the contract is fully resolved. - outcome channeldb.ResolverOutcome - }{ + return []htlcTimeoutTestCase{ // Remote commitment is broadcast, we time out the HTLC on // chain, and should expect a fail HTLC resolution. { @@ -149,7 +147,8 @@ func TestHtlcTimeoutResolver(t *testing.T) { // immediately if the witness is already set // correctly. if reflect.DeepEqual( - templateTx.TxIn[0].Witness, witness, + templateTx.TxIn[0].Witness, + witness, ) { return templateTx, nil @@ -219,7 +218,8 @@ func TestHtlcTimeoutResolver(t *testing.T) { // immediately if the witness is already set // correctly. if reflect.DeepEqual( - templateTx.TxIn[0].Witness, witness, + templateTx.TxIn[0].Witness, + witness, ) { return templateTx, nil @@ -253,7 +253,8 @@ func TestHtlcTimeoutResolver(t *testing.T) { // immediately if the witness is already set // correctly. if reflect.DeepEqual( - templateTx.TxIn[0].Witness, witness, + templateTx.TxIn[0].Witness, + witness, ) { return templateTx, nil @@ -265,243 +266,265 @@ func TestHtlcTimeoutResolver(t *testing.T) { outcome: channeldb.ResolverOutcomeClaimed, }, } +} + +func testHtlcTimeoutResolver(t *testing.T, testCase htlcTimeoutTestCase) { + fakePreimageBytes := bytes.Repeat([]byte{1}, lntypes.HashSize) + var fakePreimage lntypes.Preimage + + fakeSignDesc := &input.SignDescriptor{ + Output: &wire.TxOut{}, + } + + copy(fakePreimage[:], fakePreimageBytes) notifier := &mock.ChainNotifier{ EpochChan: make(chan *chainntnfs.BlockEpoch), SpendChan: make(chan *chainntnfs.SpendDetail), ConfChan: make(chan *chainntnfs.TxConfirmation), } - witnessBeacon := newMockWitnessBeacon() - - for _, testCase := range testCases { - t.Logf("Running test case: %v", testCase.name) - - checkPointChan := make(chan struct{}, 1) - incubateChan := make(chan struct{}, 1) - resolutionChan := make(chan ResolutionMsg, 1) - reportChan := make(chan *channeldb.ResolverReport) - - //nolint:lll - chainCfg := ChannelArbitratorConfig{ - ChainArbitratorConfig: ChainArbitratorConfig{ - Notifier: notifier, - PreimageDB: witnessBeacon, - IncubateOutputs: func(wire.OutPoint, - fn.Option[lnwallet.OutgoingHtlcResolution], - fn.Option[lnwallet.IncomingHtlcResolution], - uint32, fn.Option[int32]) error { - - incubateChan <- struct{}{} - return nil - }, - DeliverResolutionMsg: func(msgs ...ResolutionMsg) error { - if len(msgs) != 1 { - return fmt.Errorf("expected 1 "+ - "resolution msg, instead got %v", - len(msgs)) - } - resolutionChan <- msgs[0] - return nil - }, - Budget: *DefaultBudgetConfig(), - QueryIncomingCircuit: func(circuit models.CircuitKey) *models.CircuitKey { - return nil - }, + witnessBeacon := newMockWitnessBeacon() + checkPointChan := make(chan struct{}, 1) + incubateChan := make(chan struct{}, 1) + resolutionChan := make(chan ResolutionMsg, 1) + reportChan := make(chan *channeldb.ResolverReport) + + //nolint:lll + chainCfg := ChannelArbitratorConfig{ + ChainArbitratorConfig: ChainArbitratorConfig{ + Notifier: notifier, + PreimageDB: witnessBeacon, + IncubateOutputs: func(wire.OutPoint, + fn.Option[lnwallet.OutgoingHtlcResolution], + fn.Option[lnwallet.IncomingHtlcResolution], + uint32, fn.Option[int32], + ) error { + incubateChan <- struct{}{} + return nil }, - PutResolverReport: func(_ kvdb.RwTx, - _ *channeldb.ResolverReport) error { + DeliverResolutionMsg: func(msgs ...ResolutionMsg) error { + if len(msgs) != 1 { + return fmt.Errorf("expected 1 "+ + "resolution msg, instead got %v", + len(msgs)) + } + resolutionChan <- msgs[0] return nil }, - } + Budget: *DefaultBudgetConfig(), + QueryIncomingCircuit: func(circuit models.CircuitKey, + ) *models.CircuitKey { + return nil + }, + }, + PutResolverReport: func(_ kvdb.RwTx, + _ *channeldb.ResolverReport, + ) error { + return nil + }, + } - cfg := ResolverConfig{ - ChannelArbitratorConfig: chainCfg, - Checkpoint: func(_ ContractResolver, - reports ...*channeldb.ResolverReport) error { + cfg := ResolverConfig{ + ChannelArbitratorConfig: chainCfg, + Checkpoint: func(_ ContractResolver, + reports ...*channeldb.ResolverReport, + ) error { + checkPointChan <- struct{}{} - checkPointChan <- struct{}{} + // Send all of our reports into the channel. + for _, report := range reports { + reportChan <- report + } - // Send all of our reports into the channel. - for _, report := range reports { - reportChan <- report - } + return nil + }, + } + resolver := &htlcTimeoutResolver{ + htlcResolution: lnwallet.OutgoingHtlcResolution{ + ClaimOutpoint: testChanPoint2, + SweepSignDesc: *fakeSignDesc, + }, + contractResolverKit: *newContractResolverKit( + cfg, + ), + htlc: channeldb.HTLC{ + Amt: testHtlcAmt, + }, + } - return nil - }, - } - resolver := &htlcTimeoutResolver{ - htlcResolution: lnwallet.OutgoingHtlcResolution{ - ClaimOutpoint: testChanPoint2, - SweepSignDesc: *fakeSignDesc, - }, - contractResolverKit: *newContractResolverKit( - cfg, - ), - htlc: channeldb.HTLC{ - Amt: testHtlcAmt, - }, + var reports []*channeldb.ResolverReport + + // If the test case needs the remote commitment to be + // broadcast, then we'll set the timeout commit to a fake + // transaction to force the code path. + if !testCase.remoteCommit { + timeoutTx, err := testCase.txToBroadcast() + require.NoError(t, err) + + resolver.htlcResolution.SignedTimeoutTx = timeoutTx + + if testCase.timeout { + timeoutTxID := timeoutTx.TxHash() + reports = append(reports, &channeldb.ResolverReport{ + OutPoint: timeoutTx.TxIn[0].PreviousOutPoint, + Amount: testHtlcAmt.ToSatoshis(), + ResolverType: channeldb.ResolverTypeOutgoingHtlc, + ResolverOutcome: channeldb.ResolverOutcomeFirstStage, + SpendTxID: &timeoutTxID, + }) } + } - var reports []*channeldb.ResolverReport + // With all the setup above complete, we can initiate the + // resolution process, and the bulk of our test. + var wg sync.WaitGroup + resolveErr := make(chan error, 1) + wg.Add(1) + go func() { + defer wg.Done() - // If the test case needs the remote commitment to be - // broadcast, then we'll set the timeout commit to a fake - // transaction to force the code path. - if !testCase.remoteCommit { - timeoutTx, err := testCase.txToBroadcast() - require.NoError(t, err) - - resolver.htlcResolution.SignedTimeoutTx = timeoutTx - - if testCase.timeout { - timeoutTxID := timeoutTx.TxHash() - reports = append(reports, &channeldb.ResolverReport{ - OutPoint: timeoutTx.TxIn[0].PreviousOutPoint, - Amount: testHtlcAmt.ToSatoshis(), - ResolverType: channeldb.ResolverTypeOutgoingHtlc, - ResolverOutcome: channeldb.ResolverOutcomeFirstStage, - SpendTxID: &timeoutTxID, - }) - } + _, err := resolver.Resolve(false) + if err != nil { + resolveErr <- err } + }() + + // As the output isn't yet in the nursery, we expect that we + // should receive an incubation request. + select { + case <-incubateChan: + case err := <-resolveErr: + t.Fatalf("unable to resolve HTLC: %v", err) + case <-time.After(time.Second * 5): + t.Fatalf("failed to receive incubation request") + } - // With all the setup above complete, we can initiate the - // resolution process, and the bulk of our test. - var wg sync.WaitGroup - resolveErr := make(chan error, 1) - wg.Add(1) - go func() { - defer wg.Done() - - _, err := resolver.Resolve(false) - if err != nil { - resolveErr <- err - } - }() + // Next, the resolver should request a spend notification for + // the direct HTLC output. We'll use the txToBroadcast closure + // for the test case to generate the transaction that we'll + // send to the resolver. + spendingTx, err := testCase.txToBroadcast() + if err != nil { + t.Fatalf("unable to generate tx: %v", err) + } + spendTxHash := spendingTx.TxHash() + + select { + case notifier.SpendChan <- &chainntnfs.SpendDetail{ + SpendingTx: spendingTx, + SpenderTxHash: &spendTxHash, + }: + case <-time.After(time.Second * 5): + t.Fatalf("failed to request spend ntfn") + } - // At the output isn't yet in the nursery, we expect that we - // should receive an incubation request. + if !testCase.timeout { + // If the resolver should settle now, then we'll + // extract the pre-image to be extracted and the + // resolution message sent. select { - case <-incubateChan: - case err := <-resolveErr: - t.Fatalf("unable to resolve HTLC: %v", err) + case newPreimage := <-witnessBeacon.newPreimages: + if newPreimage[0] != fakePreimage { + t.Fatalf("wrong pre-image: "+ + "expected %v, got %v", + fakePreimage, newPreimage) + } + case <-time.After(time.Second * 5): - t.Fatalf("failed to receive incubation request") + t.Fatalf("pre-image not added") } - // Next, the resolver should request a spend notification for - // the direct HTLC output. We'll use the txToBroadcast closure - // for the test case to generate the transaction that we'll - // send to the resolver. - spendingTx, err := testCase.txToBroadcast() - if err != nil { - t.Fatalf("unable to generate tx: %v", err) + // Finally, we should get a resolution message with the + // pre-image set within the message. + select { + case resolutionMsg := <-resolutionChan: + // Once again, the pre-images should match up. + if *resolutionMsg.PreImage != fakePreimage { + t.Fatalf("wrong pre-image: "+ + "expected %v, got %v", + fakePreimage, resolutionMsg.PreImage) + } + case <-time.After(time.Second * 5): + t.Fatalf("resolution not sent") } - spendTxHash := spendingTx.TxHash() + } else { + // Otherwise, the HTLC should now timeout. First, we + // should get a resolution message with a populated + // failure message. select { - case notifier.SpendChan <- &chainntnfs.SpendDetail{ - SpendingTx: spendingTx, - SpenderTxHash: &spendTxHash, - }: + case resolutionMsg := <-resolutionChan: + if resolutionMsg.Failure == nil { + t.Fatalf("expected failure resolution msg") + } case <-time.After(time.Second * 5): - t.Fatalf("failed to request spend ntfn") + t.Fatalf("resolution not sent") } - if !testCase.timeout { - // If the resolver should settle now, then we'll - // extract the pre-image to be extracted and the - // resolution message sent. + // We should also get another request for the spend + // notification of the second-level transaction to + // indicate that it's been swept by the nursery, but + // only if this is a local commitment transaction. + if !testCase.remoteCommit { select { - case newPreimage := <-witnessBeacon.newPreimages: - if newPreimage[0] != fakePreimage { - t.Fatalf("wrong pre-image: "+ - "expected %v, got %v", - fakePreimage, newPreimage) - } - + case notifier.SpendChan <- &chainntnfs.SpendDetail{ + SpendingTx: spendingTx, + SpenderTxHash: &spendTxHash, + }: case <-time.After(time.Second * 5): - t.Fatalf("pre-image not added") + t.Fatalf("failed to request spend ntfn") } + } + } - // Finally, we should get a resolution message with the - // pre-image set within the message. - select { - case resolutionMsg := <-resolutionChan: - // Once again, the pre-images should match up. - if *resolutionMsg.PreImage != fakePreimage { - t.Fatalf("wrong pre-image: "+ - "expected %v, got %v", - fakePreimage, resolutionMsg.PreImage) - } - case <-time.After(time.Second * 5): - t.Fatalf("resolution not sent") - } - } else { + // In any case, before the resolver exits, it should checkpoint + // its final state. + select { + case <-checkPointChan: + case err := <-resolveErr: + t.Fatalf("unable to resolve HTLC: %v", err) + case <-time.After(time.Second * 5): + t.Fatalf("check point not received") + } - // Otherwise, the HTLC should now timeout. First, we - // should get a resolution message with a populated - // failure message. - select { - case resolutionMsg := <-resolutionChan: - if resolutionMsg.Failure == nil { - t.Fatalf("expected failure resolution msg") - } - case <-time.After(time.Second * 5): - t.Fatalf("resolution not sent") - } + // Add a report to our set of expected reports with the outcome + // that the test specifies (either success or timeout). + spendTxID := spendingTx.TxHash() + amt := btcutil.Amount(fakeSignDesc.Output.Value) - // We should also get another request for the spend - // notification of the second-level transaction to - // indicate that it's been swept by the nursery, but - // only if this is a local commitment transaction. - if !testCase.remoteCommit { - select { - case notifier.SpendChan <- &chainntnfs.SpendDetail{ - SpendingTx: spendingTx, - SpenderTxHash: &spendTxHash, - }: - case <-time.After(time.Second * 5): - t.Fatalf("failed to request spend ntfn") - } - } - } + reports = append(reports, &channeldb.ResolverReport{ + OutPoint: testChanPoint2, + Amount: amt, + ResolverType: channeldb.ResolverTypeOutgoingHtlc, + ResolverOutcome: testCase.outcome, + SpendTxID: &spendTxID, + }) - // In any case, before the resolver exits, it should checkpoint - // its final state. - select { - case <-checkPointChan: - case err := <-resolveErr: - t.Fatalf("unable to resolve HTLC: %v", err) - case <-time.After(time.Second * 5): - t.Fatalf("check point not received") - } + for _, report := range reports { + assertResolverReport(t, reportChan, report) + } - // Add a report to our set of expected reports with the outcome - // that the test specifies (either success or timeout). - spendTxID := spendingTx.TxHash() - amt := btcutil.Amount(fakeSignDesc.Output.Value) - - reports = append(reports, &channeldb.ResolverReport{ - OutPoint: testChanPoint2, - Amount: amt, - ResolverType: channeldb.ResolverTypeOutgoingHtlc, - ResolverOutcome: testCase.outcome, - SpendTxID: &spendTxID, - }) + wg.Wait() - for _, report := range reports { - assertResolverReport(t, reportChan, report) - } + // Finally, the resolver should be marked as resolved. + if !resolver.resolved { + t.Fatalf("resolver should be marked as resolved") + } +} + +// TestHtlcTimeoutResolver tests that the timeout resolver properly handles all +// variations of possible local+remote spends. +func TestHtlcTimeoutResolver(t *testing.T) { + t.Parallel() - wg.Wait() + testCases := genHtlcTimeoutTestCases() - // Finally, the resolver should be marked as resolved. - if !resolver.resolved { - t.Fatalf("resolver should be marked as resolved") - } + for _, testCase := range testCases { + t.Run(testCase.name, func(t *testing.T) { + testHtlcTimeoutResolver(t, testCase) + }) } } @@ -545,6 +568,7 @@ func TestHtlcTimeoutSingleStage(t *testing.T) { // by the nursery. preCheckpoint: func(ctx *htlcResolverTestContext, _ bool) error { + // The nursery will create and publish a sweep // tx. ctx.notifier.SpendChan <- &chainntnfs.SpendDetail{ @@ -653,6 +677,7 @@ func TestHtlcTimeoutSecondStage(t *testing.T) { // that our sweep succeeded. preCheckpoint: func(ctx *htlcResolverTestContext, _ bool) error { + // The nursery will publish the timeout tx. ctx.notifier.SpendChan <- &chainntnfs.SpendDetail{ SpendingTx: timeoutTx, @@ -1296,7 +1321,9 @@ func TestHtlcTimeoutSecondStageSweeperRemoteSpend(t *testing.T) { } func testHtlcTimeout(t *testing.T, resolution lnwallet.OutgoingHtlcResolution, - checkpoints []checkpoint) { + checkpoints []checkpoint, +) { + t.Helper() defer timeout()() From b2b5ec052bf0b5159729451b0f2c111f8f3b5471 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Tue, 3 Sep 2024 18:58:12 -0700 Subject: [PATCH 197/218] contractcourt: use the sweeper for HTLC offered remote timeout resolution In this commit, we bring the timeout resolver more in line with the success resolver by using the sweeper to handle the HTLC offered remote timeout outputs. These are outputs that we can sweep directly from the remote party's commitment transaction when they broadcast their version of the commitment transaction. With this change, we slim down the scope slightly by only doing this for anchor channels. Non-anchor channels will continue to use the utxonursery for this output type for now. --- contractcourt/htlc_timeout_resolver.go | 94 +++++++++++++++++++-- contractcourt/htlc_timeout_resolver_test.go | 65 +++++++------- 2 files changed, 123 insertions(+), 36 deletions(-) diff --git a/contractcourt/htlc_timeout_resolver.go b/contractcourt/htlc_timeout_resolver.go index 87cbfce0b9..670da607d4 100644 --- a/contractcourt/htlc_timeout_resolver.go +++ b/contractcourt/htlc_timeout_resolver.go @@ -549,7 +549,6 @@ func (h *htlcTimeoutResolver) sweepSecondLevelTx(immediate bool) error { return err } - // TODO(yy): checkpoint here? return err } @@ -573,6 +572,59 @@ func (h *htlcTimeoutResolver) sendSecondLevelTxLegacy() error { return h.Checkpoint(h) } +// sweepDirectHtlcOutput sends the direct spend of the HTLC output to the +// sweeper. This is used when the remote party goes on chain, and we're able to +// sweep an HTLC we offered after a timeout. Only the CLTV encumbered outputs +// are resolved via this path. +func (h *htlcTimeoutResolver) sweepDirectHtlcOutput(immediate bool) error { + var htlcWitnessType input.StandardWitnessType + if h.isTaproot() { + htlcWitnessType = input.TaprootHtlcOfferedRemoteTimeout + } else { + htlcWitnessType = input.HtlcOfferedRemoteTimeout + } + + sweepInput := input.NewCsvInputWithCltv( + &h.htlcResolution.ClaimOutpoint, htlcWitnessType, + &h.htlcResolution.SweepSignDesc, h.broadcastHeight, + h.htlcResolution.CsvDelay, h.htlcResolution.Expiry, + ) + + // Calculate the budget. + // + // TODO(yy): the budget is twice the output's value, which is needed as + // we don't force sweep the output now. To prevent cascading force + // closes, we use all its output value plus a wallet input as the + // budget. This is a temporary solution until we can optionally cancel + // the incoming HTLC, more details in, + // - https://github.com/lightningnetwork/lnd/issues/7969 + budget := calculateBudget( + btcutil.Amount(sweepInput.SignDesc().Output.Value), 2, 0, + ) + + log.Infof("%T(%x): offering offered remote timeout HTLC output to "+ + "sweeper with deadline %v and budget=%v at height=%v", + h, h.htlc.RHash[:], h.incomingHTLCExpiryHeight, budget, + h.broadcastHeight) + + _, err := h.Sweeper.SweepInput( + sweepInput, + sweep.Params{ + Budget: budget, + + // This is an outgoing HTLC, so we want to make sure + // that we sweep it before the incoming HTLC expires. + DeadlineHeight: h.incomingHTLCExpiryHeight, + Immediate: immediate, + }, + ) + if err != nil { + return err + } + + return nil +} + // spendHtlcOutput handles the initial spend of an HTLC output via the timeout // clause. If this is our local commitment, the second-level timeout TX will be // used to spend the output into the next stage. If this is the remote @@ -593,8 +645,18 @@ func (h *htlcTimeoutResolver) spendHtlcOutput( return nil, err } - // If we have no SignDetails, and we haven't already sent the output to - // the utxo nursery, then we'll do so now. + // If this is a remote commitment there's no second level timeout txn, + // and we can just send this directly to the sweeper. + case h.htlcResolution.SignedTimeoutTx == nil && !h.outputIncubating: + if err := h.sweepDirectHtlcOutput(immediate); err != nil { + log.Errorf("Sending direct spend to sweeper: %v", err) + + return nil, err + } + + // If we have a SignedTimeoutTx but no SignDetails, this is a local + // commitment for a non-anchor channel, so we'll send it to the utxo + // nursery. case h.htlcResolution.SignDetails == nil && !h.outputIncubating: if err := h.sendSecondLevelTxLegacy(); err != nil { log.Errorf("Sending timeout tx to nursery: %v", err) @@ -701,6 +763,13 @@ func (h *htlcTimeoutResolver) handleCommitSpend( ) switch { + + // If we swept an HTLC directly off the remote party's commitment + // transaction, then we can exit here as there's no second level sweep + // to do. + case h.htlcResolution.SignedTimeoutTx == nil: + break + // If the sweeper is handling the second level transaction, wait for // the CSV and possible CLTV lock to expire, before sweeping the output // on the second-level. @@ -774,6 +843,7 @@ func (h *htlcTimeoutResolver) handleCommitSpend( h.htlcResolution.CsvDelay, uint32(commitSpend.SpendingHeight), h.htlc.RHash, ) + // Calculate the budget for this sweep. budget := calculateBudget( btcutil.Amount(inp.SignDesc().Output.Value), @@ -811,6 +881,7 @@ func (h *htlcTimeoutResolver) handleCommitSpend( case h.htlcResolution.SignedTimeoutTx != nil: log.Infof("%T(%v): waiting for nursery/sweeper to spend CSV "+ "delayed output", h, claimOutpoint) + sweepTx, err := waitForSpend( &claimOutpoint, h.htlcResolution.SweepSignDesc.Output.PkScript, @@ -877,9 +948,11 @@ func (h *htlcTimeoutResolver) IsResolved() bool { // report returns a report on the resolution state of the contract. func (h *htlcTimeoutResolver) report() *ContractReport { - // If the sign details are nil, the report will be created by handled - // by the nursery. - if h.htlcResolution.SignDetails == nil { + // If we have a SignedTimeoutTx but no SignDetails, this is a local + // commitment for a non-anchor channel, which was handled by the utxo + // nursery. + if h.htlcResolution.SignDetails == nil && h. + htlcResolution.SignedTimeoutTx != nil { return nil } @@ -899,13 +972,20 @@ func (h *htlcTimeoutResolver) initReport() { ) } + // If there's no timeout transaction, then we're already effectively in + // level two. + stage := uint32(1) + if h.htlcResolution.SignedTimeoutTx == nil { + stage = 2 + } + h.currentReport = ContractReport{ Outpoint: h.htlcResolution.ClaimOutpoint, Type: ReportOutputOutgoingHtlc, Amount: finalAmt, MaturityHeight: h.htlcResolution.Expiry, LimboBalance: finalAmt, - Stage: 1, + Stage: stage, } } diff --git a/contractcourt/htlc_timeout_resolver_test.go b/contractcourt/htlc_timeout_resolver_test.go index 8ccd4aaaca..47be71d3ec 100644 --- a/contractcourt/htlc_timeout_resolver_test.go +++ b/contractcourt/htlc_timeout_resolver_test.go @@ -294,12 +294,13 @@ func testHtlcTimeoutResolver(t *testing.T, testCase htlcTimeoutTestCase) { chainCfg := ChannelArbitratorConfig{ ChainArbitratorConfig: ChainArbitratorConfig{ Notifier: notifier, + Sweeper: newMockSweeper(), PreimageDB: witnessBeacon, IncubateOutputs: func(wire.OutPoint, fn.Option[lnwallet.OutgoingHtlcResolution], fn.Option[lnwallet.IncomingHtlcResolution], - uint32, fn.Option[int32], - ) error { + uint32, fn.Option[int32]) error { + incubateChan <- struct{}{} return nil }, @@ -311,17 +312,19 @@ func testHtlcTimeoutResolver(t *testing.T, testCase htlcTimeoutTestCase) { } resolutionChan <- msgs[0] + return nil }, Budget: *DefaultBudgetConfig(), QueryIncomingCircuit: func(circuit models.CircuitKey, ) *models.CircuitKey { + return nil }, }, PutResolverReport: func(_ kvdb.RwTx, - _ *channeldb.ResolverReport, - ) error { + _ *channeldb.ResolverReport) error { + return nil }, } @@ -329,8 +332,8 @@ func testHtlcTimeoutResolver(t *testing.T, testCase htlcTimeoutTestCase) { cfg := ResolverConfig{ ChannelArbitratorConfig: chainCfg, Checkpoint: func(_ ContractResolver, - reports ...*channeldb.ResolverReport, - ) error { + reports ...*channeldb.ResolverReport) error { + checkPointChan <- struct{}{} // Send all of our reports into the channel. @@ -367,13 +370,15 @@ func testHtlcTimeoutResolver(t *testing.T, testCase htlcTimeoutTestCase) { if testCase.timeout { timeoutTxID := timeoutTx.TxHash() - reports = append(reports, &channeldb.ResolverReport{ - OutPoint: timeoutTx.TxIn[0].PreviousOutPoint, + report := &channeldb.ResolverReport{ + OutPoint: timeoutTx.TxIn[0].PreviousOutPoint, //nolint:lll Amount: testHtlcAmt.ToSatoshis(), - ResolverType: channeldb.ResolverTypeOutgoingHtlc, - ResolverOutcome: channeldb.ResolverOutcomeFirstStage, + ResolverType: channeldb.ResolverTypeOutgoingHtlc, //nolint:lll + ResolverOutcome: channeldb.ResolverOutcomeFirstStage, //nolint:lll SpendTxID: &timeoutTxID, - }) + } + + reports = append(reports, report) } } @@ -391,10 +396,21 @@ func testHtlcTimeoutResolver(t *testing.T, testCase htlcTimeoutTestCase) { } }() - // As the output isn't yet in the nursery, we expect that we - // should receive an incubation request. + // If this is a remote commit, then we expct the outputs should receive + // an incubation request to go through the sweeper, otherwise the + // nursery. + var sweepChan chan input.Input + if testCase.remoteCommit { + mockSweeper, ok := resolver.Sweeper.(*mockSweeper) + require.True(t, ok) + sweepChan = mockSweeper.sweptInputs + } + + // The output should be offered to either the sweeper or + // the nursery. select { case <-incubateChan: + case <-sweepChan: case err := <-resolveErr: t.Fatalf("unable to resolve HTLC: %v", err) case <-time.After(time.Second * 5): @@ -450,7 +466,6 @@ func testHtlcTimeoutResolver(t *testing.T, testCase htlcTimeoutTestCase) { t.Fatalf("resolution not sent") } } else { - // Otherwise, the HTLC should now timeout. First, we // should get a resolution message with a populated // failure message. @@ -559,10 +574,6 @@ func TestHtlcTimeoutSingleStage(t *testing.T) { } checkpoints := []checkpoint{ - { - // Output should be handed off to the nursery. - incubating: true, - }, { // We send a confirmation the sweep tx from published // by the nursery. @@ -594,7 +605,7 @@ func TestHtlcTimeoutSingleStage(t *testing.T) { // After the sweep has confirmed, we expect the // checkpoint to be resolved, and with the above // report. - incubating: true, + incubating: false, resolved: true, reports: []*channeldb.ResolverReport{ claim, @@ -849,9 +860,9 @@ func TestHtlcTimeoutSingleStageRemoteSpend(t *testing.T) { ) } -// TestHtlcTimeoutSecondStageRemoteSpend tests that when a remite commitment -// confirms, and the remote spends the output using the success tx, we -// properly detect this and extract the preimage. +// TestHtlcTimeoutSecondStageRemoteSpend tests that when a remote commitment +// confirms, and the remote spends the output using the success tx, we properly +// detect this and extract the preimage. func TestHtlcTimeoutSecondStageRemoteSpend(t *testing.T) { commitOutpoint := wire.OutPoint{Index: 2} @@ -895,10 +906,6 @@ func TestHtlcTimeoutSecondStageRemoteSpend(t *testing.T) { } checkpoints := []checkpoint{ - { - // Output should be handed off to the nursery. - incubating: true, - }, { // We send a confirmation for the remote's second layer // success transcation. @@ -944,7 +951,7 @@ func TestHtlcTimeoutSecondStageRemoteSpend(t *testing.T) { // After the sweep has confirmed, we expect the // checkpoint to be resolved, and with the above // report. - incubating: true, + incubating: false, resolved: true, reports: []*channeldb.ResolverReport{ claim, @@ -1321,8 +1328,8 @@ func TestHtlcTimeoutSecondStageSweeperRemoteSpend(t *testing.T) { } func testHtlcTimeout(t *testing.T, resolution lnwallet.OutgoingHtlcResolution, - checkpoints []checkpoint, -) { + checkpoints []checkpoint) { + t.Helper() defer timeout()() From a4414fbbb9ebcda2bcd5ebe6278a8388b343c711 Mon Sep 17 00:00:00 2001 From: Matt Morehouse Date: Tue, 10 Sep 2024 11:41:31 -0500 Subject: [PATCH 198/218] lnwire: manually compare Timestamps in fuzz test We can't use require.Equal because it considers nil slices and empty slices to be not equal. --- lnwire/fuzz_test.go | 44 +++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 41 insertions(+), 3 deletions(-) diff --git a/lnwire/fuzz_test.go b/lnwire/fuzz_test.go index 9a759604aa..d6ec3b61b3 100644 --- a/lnwire/fuzz_test.go +++ b/lnwire/fuzz_test.go @@ -494,9 +494,47 @@ func FuzzReplyChannelRange(f *testing.F) { // Prefix with MsgReplyChannelRange. data = prefixWithMsgType(data, MsgReplyChannelRange) - // Pass the message into our general fuzz harness for wire - // messages! - harness(t, data) + // Because require.Equal considers nil slices and empty slices + // to be non-equal, we must manually compare the Timestamps + // field rather than using the harness. + + if len(data) > MaxSliceLength { + return + } + + r := bytes.NewReader(data) + msg, err := ReadMessage(r, 0) + if err != nil { + return + } + + // We will serialize the message into a new bytes buffer. + var b bytes.Buffer + _, err = WriteMessage(&b, msg, 0) + require.NoError(t, err) + + // Deserialize the message from the serialized bytes buffer, and + // then assert that the original message is equal to the newly + // deserialized message. + newMsg, err := ReadMessage(&b, 0) + require.NoError(t, err) + + require.IsType(t, &ReplyChannelRange{}, msg) + first, _ := msg.(*ReplyChannelRange) + require.IsType(t, &ReplyChannelRange{}, newMsg) + second, _ := newMsg.(*ReplyChannelRange) + + // We can't use require.Equal for Timestamps, since we consider + // the empty slice and nil to be equivalent. + require.Equal(t, len(first.Timestamps), len(second.Timestamps)) + for i, ts1 := range first.Timestamps { + ts2 := second.Timestamps[i] + require.Equal(t, ts1, ts2) + } + first.Timestamps = nil + second.Timestamps = nil + + require.Equal(t, first, second) }) } From c8033e172542a35dcf79322c64998a8c20f1365a Mon Sep 17 00:00:00 2001 From: Matt Morehouse Date: Tue, 10 Sep 2024 14:36:30 -0500 Subject: [PATCH 199/218] lnwire: fail decoding on incorrect number of timestamps Currently if an incorrect number of timestamps is given, we fail later on in the GossipSyncer. It makes more sense to fail right away, since we already do that for incorrect SCID formats (e.g., unsorted or duplicate SCIDs). There is already a matching check in Encode for incorrect number of timestamps, so adding this check to Decode makes things symmetric. --- lnwire/reply_channel_range.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lnwire/reply_channel_range.go b/lnwire/reply_channel_range.go index ea45a5843c..591fc2bd60 100644 --- a/lnwire/reply_channel_range.go +++ b/lnwire/reply_channel_range.go @@ -104,6 +104,12 @@ func (c *ReplyChannelRange) Decode(r io.Reader, pver uint32) error { // Set the corresponding TLV types if they were included in the stream. if val, ok := typeMap[TimestampsRecordType]; ok && val == nil { c.Timestamps = timeStamps + + // Check that a timestamp was provided for each SCID. + if len(c.Timestamps) != len(c.ShortChanIDs) { + return fmt.Errorf("number of timestamps does not " + + "match number of SCIDs") + } } if len(tlvRecords) != 0 { From 2c25f38a5a914760c8dd7bbd987d980fbad6881b Mon Sep 17 00:00:00 2001 From: Matt Morehouse Date: Tue, 10 Sep 2024 14:52:16 -0500 Subject: [PATCH 200/218] discovery: remove check for incorrect number of timestamps The check is no longer required, as it is now done during decoding. --- discovery/syncer.go | 6 ------ 1 file changed, 6 deletions(-) diff --git a/discovery/syncer.go b/discovery/syncer.go index b6adb447a6..cdb041d99b 100644 --- a/discovery/syncer.go +++ b/discovery/syncer.go @@ -835,12 +835,6 @@ func (g *GossipSyncer) processChanRangeReply(msg *lnwire.ReplyChannelRange) erro } g.prevReplyChannelRange = msg - if len(msg.Timestamps) != 0 && - len(msg.Timestamps) != len(msg.ShortChanIDs) { - - return fmt.Errorf("number of timestamps not equal to " + - "number of SCIDs") - } for i, scid := range msg.ShortChanIDs { info := channeldb.NewChannelUpdateInfo( From a5beb340e48d981dcd4feb47da2c04f7680f728d Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Tue, 15 Oct 2024 19:06:15 -0700 Subject: [PATCH 201/218] lnwallet: expand attributes in ResolutionReq In this commit, we add some additional attributes to the ResolutionReq struct. These will be used to make sure that we can properly handle all the HTLC variants, on chain. The `AuxSigDesc` will be used to communicate if an HTLC needs to go to the second level or not. It contains the second-level sig information needed to finalize a broadcast to the second level. --- lnwallet/aux_resolutions.go | 38 ++++++++++++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/lnwallet/aux_resolutions.go b/lnwallet/aux_resolutions.go index e3b04fc5aa..382232640d 100644 --- a/lnwallet/aux_resolutions.go +++ b/lnwallet/aux_resolutions.go @@ -3,6 +3,7 @@ package lnwallet import ( "github.com/btcsuite/btcd/btcutil" "github.com/btcsuite/btcd/wire" + "github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/fn" "github.com/lightningnetwork/lnd/input" "github.com/lightningnetwork/lnd/lnwire" @@ -24,6 +25,19 @@ const ( Breach ) +// AuxSigDesc stores optional information related to 2nd level HTLCs for aux +// channels. +type AuxSigDesc struct { + // AuxSig is the second-level signature for the HTLC that we are trying + // to resolve. This is only present if this is a resolution request for + // an HTLC on our commitment transaction. + AuxSig []byte + + // SignDetails is the sign details for the second-level HTLC. This may + // be used to generate the second signature needed for broadcast. + SignDetails input.SignDetails +} + // ResolutionReq is used to ask an outside sub-system for additional // information needed to resolve a contract. type ResolutionReq struct { @@ -31,6 +45,9 @@ type ResolutionReq struct { // resolve. ChanPoint wire.OutPoint + // ChanType is the type of the channel that we are trying to resolve. + ChanType channeldb.ChannelType + // ShortChanID is the short channel ID of the channel that we are // trying to resolve. ShortChanID lnwire.ShortChannelID @@ -44,6 +61,13 @@ type ResolutionReq struct { // FundingBlob is an optional funding blob for the channel. FundingBlob fn.Option[tlv.Blob] + // HtlcID is the ID of the HTLC that we are trying to resolve. This is + // only set if this is a resolution request for an HTLC. + HtlcID fn.Option[input.HtlcIndex] + + // HtlcAmt is the amount of the HTLC that we are trying to resolve. + HtlcAmt btcutil.Amount + // Type is the type of the witness that we are trying to resolve. Type input.WitnessType @@ -69,14 +93,26 @@ type ResolutionReq struct { // CsvDelay is the CSV delay for the local output for this commitment. CsvDelay uint32 + // CommitCsvDelay is the CSV delay for the remote output for this + // commitment. + CommitCsvDelay uint32 + // BreachCsvDelay is the CSV delay for the remote output. This is only // set when the CloseType is Breach. This indicates the CSV delay to // use for the remote party's to_local delayed output, that is now // rightfully ours in a breach situation. BreachCsvDelay fn.Option[uint32] - // CltvDelay is the CLTV delay for the outpoint. + // CltvDelay is the CLTV delay for the outpoint/transaction. CltvDelay fn.Option[uint32] + + // PayHash is the payment hash for the HTLC that we are trying to + // resolve. This is optional as it only applies HTLC outputs. + PayHash fn.Option[[32]byte] + + // AuxSigDesc is an optional field that contains additional information + // needed to sweep second level HTLCs. + AuxSigDesc fn.Option[AuxSigDesc] } // AuxContractResolver is an interface that is used to resolve contracts that From 57617dc4f79c0678054fef42260ba50514e746c8 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Tue, 15 Oct 2024 19:09:44 -0700 Subject: [PATCH 202/218] channel: always specify ChanType in ResolutionReq --- lnwallet/channel.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lnwallet/channel.go b/lnwallet/channel.go index cefee3ef12..f978b2d0d0 100644 --- a/lnwallet/channel.go +++ b/lnwallet/channel.go @@ -2166,6 +2166,7 @@ func NewBreachRetribution(chanState *channeldb.OpenChannel, stateNum uint64, // resolution data for this output. resolveReq := ResolutionReq{ ChanPoint: chanState.FundingOutpoint, + ChanType: chanState.ChanType, ShortChanID: chanState.ShortChanID(), Initiator: chanState.IsInitiator, FundingBlob: chanState.CustomBlob, @@ -2245,6 +2246,7 @@ func NewBreachRetribution(chanState *channeldb.OpenChannel, stateNum uint64, // resolution data for this output. resolveReq := ResolutionReq{ ChanPoint: chanState.FundingOutpoint, + ChanType: chanState.ChanType, ShortChanID: chanState.ShortChanID(), Initiator: chanState.IsInitiator, FundingBlob: chanState.CustomBlob, @@ -6780,6 +6782,7 @@ func NewUnilateralCloseSummary(chanState *channeldb.OpenChannel, // resolution data for this output. resolveReq := ResolutionReq{ ChanPoint: chanState.FundingOutpoint, + ChanType: chanState.ChanType, ShortChanID: chanState.ShortChanID(), Initiator: chanState.IsInitiator, CommitBlob: chanState.RemoteCommitment.CustomBlob, From 84aacc6c394c702866c7282ad6568accc8ab2f54 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Tue, 15 Oct 2024 19:10:38 -0700 Subject: [PATCH 203/218] channel: add ResolutionBlob to Incoming+Outgoing HtlcResolution Similar to the other blobs we have for the commitment output force close resolution, these blobs will be used to ensure that we have everything needed to sweep aux HTLCs. --- lnwallet/channel.go | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/lnwallet/channel.go b/lnwallet/channel.go index f978b2d0d0..b1c4f03847 100644 --- a/lnwallet/channel.go +++ b/lnwallet/channel.go @@ -6900,6 +6900,11 @@ type IncomingHtlcResolution struct { // necessary items required to spend the sole output of the above // transaction. SweepSignDesc input.SignDescriptor + + // ResolutionBlob is a blob used for aux channels that permits a + // spender of the output to properly resolve it in the case of a force + // close. + ResolutionBlob fn.Option[tlv.Blob] } // OutgoingHtlcResolution houses the information necessary to sweep any @@ -6949,6 +6954,11 @@ type OutgoingHtlcResolution struct { // necessary items required to spend the sole output of the above // transaction. SweepSignDesc input.SignDescriptor + + // ResolutionBlob is a blob used for aux channels that permits a + // spender of the output to properly resolve it in the case of a force + // close. + ResolutionBlob fn.Option[tlv.Blob] } // HtlcResolutions contains the items necessary to sweep HTLC's on chain From e3eef0adff80f2231422bc6e473dbae196dcefe9 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Tue, 15 Oct 2024 19:15:43 -0700 Subject: [PATCH 204/218] lnwallet: populate resolution blob for incoming+outgoing HTLC resolutions In this commit, we populate the resolution blobs for the incoming and outgoing HTLCs. We take care to populate the AuxSigDesc with the correct information, as we need to pass along the second-level aux signature and also sign desc along with it. --- lnwallet/channel.go | 277 +++++++++++++++++++++++++++++++++-------- macaroons/fuzz_test.go | 51 ++++++++ 2 files changed, 276 insertions(+), 52 deletions(-) create mode 100644 macaroons/fuzz_test.go diff --git a/lnwallet/channel.go b/lnwallet/channel.go index b1c4f03847..67cd7b9472 100644 --- a/lnwallet/channel.go +++ b/lnwallet/channel.go @@ -4518,7 +4518,9 @@ func (lc *LightningChannel) ProcessChanSyncMsg(ctx context.Context, // Next, we'll need to send over any updates we sent as part of // this new proposed commitment state. for _, logUpdate := range commitDiff.LogUpdates { - commitUpdates = append(commitUpdates, logUpdate.UpdateMsg) + commitUpdates = append( + commitUpdates, logUpdate.UpdateMsg, + ) } // If this is a taproot channel, then we need to regenerate the @@ -6647,7 +6649,7 @@ type UnilateralCloseSummary struct { // happen in case we have lost state) it should be set to an empty struct, in // which case we will attempt to sweep the non-HTLC output using the passed // commitPoint. -func NewUnilateralCloseSummary(chanState *channeldb.OpenChannel, +func NewUnilateralCloseSummary(chanState *channeldb.OpenChannel, //nolint:funlen signer input.Signer, commitSpend *chainntnfs.SpendDetail, remoteCommit channeldb.ChannelCommitment, commitPoint *btcec.PublicKey, leafStore fn.Option[AuxLeafStore], @@ -6691,8 +6693,8 @@ func NewUnilateralCloseSummary(chanState *channeldb.OpenChannel, chainfee.SatPerKWeight(remoteCommit.FeePerKw), commitType, signer, remoteCommit.Htlcs, keyRing, &chanState.LocalChanCfg, &chanState.RemoteChanCfg, commitSpend.SpendingTx, - chanState.ChanType, isRemoteInitiator, leaseExpiry, - auxResult.AuxLeaves, + chanState.ChanType, isRemoteInitiator, leaseExpiry, chanState, + auxResult.AuxLeaves, auxResolver, ) if err != nil { return nil, fmt.Errorf("unable to create htlc resolutions: %w", @@ -6983,8 +6985,10 @@ func newOutgoingHtlcResolution(signer input.Signer, htlc *channeldb.HTLC, keyRing *CommitmentKeyRing, feePerKw chainfee.SatPerKWeight, csvDelay, leaseExpiry uint32, whoseCommit lntypes.ChannelParty, isCommitFromInitiator bool, - chanType channeldb.ChannelType, - auxLeaves fn.Option[CommitAuxLeaves]) (*OutgoingHtlcResolution, error) { + chanType channeldb.ChannelType, chanState *channeldb.OpenChannel, + auxLeaves fn.Option[CommitAuxLeaves], + auxResolver fn.Option[AuxContractResolver], +) (*OutgoingHtlcResolution, error) { op := wire.OutPoint{ Hash: commitTx.TxHash(), @@ -7015,6 +7019,8 @@ func newOutgoingHtlcResolution(signer input.Signer, return nil, err } + htlcCsvDelay := HtlcSecondLevelInputSequence(chanType) + // If we're spending this HTLC output from the remote node's // commitment, then we won't need to go to the second level as our // outputs don't have a CSV delay. @@ -7052,11 +7058,43 @@ func newOutgoingHtlcResolution(signer input.Signer, } } + resReq := ResolutionReq{ + ChanPoint: chanState.FundingOutpoint, + ChanType: chanType, + ShortChanID: chanState.ShortChanID(), + Initiator: chanState.IsInitiator, + CommitBlob: chanState.RemoteCommitment.CustomBlob, + FundingBlob: chanState.CustomBlob, + Type: input.TaprootHtlcOfferedRemoteTimeout, + CloseType: RemoteForceClose, + CommitTx: commitTx, + ContractPoint: op, + SignDesc: signDesc, + KeyRing: keyRing, + CsvDelay: htlcCsvDelay, + CltvDelay: fn.Some(htlc.RefundTimeout), + CommitFee: chanState.RemoteCommitment.CommitFee, + HtlcID: fn.Some(htlc.HtlcIndex), + PayHash: fn.Some(htlc.RHash), + } + resolveRes := fn.MapOptionZ( + auxResolver, + func(a AuxContractResolver) fn.Result[tlv.Blob] { + return a.ResolveContract(resReq) + }, + ) + if err := resolveRes.Err(); err != nil { + return nil, fmt.Errorf("unable to aux resolve: %w", err) + } + + resolutionBlob := resolveRes.Option() + return &OutgoingHtlcResolution{ - Expiry: htlc.RefundTimeout, - ClaimOutpoint: op, - SweepSignDesc: signDesc, - CsvDelay: HtlcSecondLevelInputSequence(chanType), + Expiry: htlc.RefundTimeout, + ClaimOutpoint: op, + SweepSignDesc: signDesc, + CsvDelay: csvDelay, + ResolutionBlob: resolutionBlob, }, nil } @@ -7207,31 +7245,78 @@ func newOutgoingHtlcResolution(signer input.Signer, keyRing.CommitPoint, localChanCfg.DelayBasePoint.PubKey, ) + // In addition to the info in txSignDetails, we also need extra + // information to sweep the second level output after confirmation. + sweepSignDesc := input.SignDescriptor{ + KeyDesc: localChanCfg.DelayBasePoint, + SingleTweak: localDelayTweak, + WitnessScript: htlcSweepWitnessScript, + Output: &wire.TxOut{ + PkScript: htlcSweepScript.PkScript(), + Value: int64(secondLevelOutputAmt), + }, + HashType: sweepSigHash(chanType), + PrevOutputFetcher: txscript.NewCannedPrevOutputFetcher( + htlcSweepScript.PkScript(), + int64(secondLevelOutputAmt), + ), + SignMethod: signMethod, + ControlBlock: ctrlBlock, + } + + // This might be an aux channel, so we'll go ahead and attempt to + // generate the resolution blob for the channel so we can pass along to + // the sweeping sub-system. + resolveRes := fn.MapOptionZ( + auxResolver, func(a AuxContractResolver) fn.Result[tlv.Blob] { + resReq := ResolutionReq{ + ChanPoint: chanState.FundingOutpoint, + ChanType: chanType, + ShortChanID: chanState.ShortChanID(), + Initiator: chanState.IsInitiator, + CommitBlob: chanState.LocalCommitment.CustomBlob, //nolint:lll + FundingBlob: chanState.CustomBlob, + Type: input.TaprootHtlcLocalOfferedTimeout, //nolint:lll + CloseType: LocalForceClose, + CommitTx: commitTx, + ContractPoint: op, + SignDesc: sweepSignDesc, + KeyRing: keyRing, + CsvDelay: htlcCsvDelay, + HtlcAmt: btcutil.Amount(txOut.Value), + CommitCsvDelay: csvDelay, + CltvDelay: fn.Some(htlc.RefundTimeout), + CommitFee: chanState.LocalCommitment.CommitFee, //nolint:lll + HtlcID: fn.Some(htlc.HtlcIndex), + PayHash: fn.Some(htlc.RHash), + AuxSigDesc: fn.Some(AuxSigDesc{ + SignDetails: *txSignDetails, + AuxSig: func() []byte { + tlvType := htlcCustomSigType.TypeVal() //nolint:lll + return htlc.CustomRecords[uint64(tlvType)] //nolint:lll + }(), + }), + } + + return a.ResolveContract(resReq) + }, + ) + if err := resolveRes.Err(); err != nil { + return nil, fmt.Errorf("unable to aux resolve: %w", err) + } + resolutionBlob := resolveRes.Option() + return &OutgoingHtlcResolution{ Expiry: htlc.RefundTimeout, SignedTimeoutTx: timeoutTx, SignDetails: txSignDetails, CsvDelay: csvDelay, + ResolutionBlob: resolutionBlob, ClaimOutpoint: wire.OutPoint{ Hash: timeoutTx.TxHash(), Index: 0, }, - SweepSignDesc: input.SignDescriptor{ - KeyDesc: localChanCfg.DelayBasePoint, - SingleTweak: localDelayTweak, - WitnessScript: htlcSweepWitnessScript, - Output: &wire.TxOut{ - PkScript: htlcSweepScript.PkScript(), - Value: int64(secondLevelOutputAmt), - }, - HashType: sweepSigHash(chanType), - PrevOutputFetcher: txscript.NewCannedPrevOutputFetcher( - htlcSweepScript.PkScript(), - int64(secondLevelOutputAmt), - ), - SignMethod: signMethod, - ControlBlock: ctrlBlock, - }, + SweepSignDesc: sweepSignDesc, }, nil } @@ -7247,8 +7332,10 @@ func newIncomingHtlcResolution(signer input.Signer, htlc *channeldb.HTLC, keyRing *CommitmentKeyRing, feePerKw chainfee.SatPerKWeight, csvDelay, leaseExpiry uint32, whoseCommit lntypes.ChannelParty, isCommitFromInitiator bool, - chanType channeldb.ChannelType, - auxLeaves fn.Option[CommitAuxLeaves]) (*IncomingHtlcResolution, error) { + chanType channeldb.ChannelType, chanState *channeldb.OpenChannel, + auxLeaves fn.Option[CommitAuxLeaves], + auxResolver fn.Option[AuxContractResolver], +) (*IncomingHtlcResolution, error) { op := wire.OutPoint{ Hash: commitTx.TxHash(), @@ -7280,6 +7367,8 @@ func newIncomingHtlcResolution(signer input.Signer, return nil, err } + htlcCsvDelay := HtlcSecondLevelInputSequence(chanType) + // If we're spending this output from the remote node's commitment, // then we can skip the second layer and spend the output directly. if whoseCommit.IsRemote() { @@ -7315,10 +7404,44 @@ func newIncomingHtlcResolution(signer input.Signer, } } + resReq := ResolutionReq{ + ChanPoint: chanState.FundingOutpoint, + ChanType: chanType, + ShortChanID: chanState.ShortChanID(), + Initiator: chanState.IsInitiator, + CommitBlob: chanState.RemoteCommitment.CustomBlob, + Type: input.TaprootHtlcAcceptedRemoteSuccess, + FundingBlob: chanState.CustomBlob, + CloseType: RemoteForceClose, + CommitTx: commitTx, + ContractPoint: op, + SignDesc: signDesc, + KeyRing: keyRing, + HtlcID: fn.Some(htlc.HtlcIndex), + CsvDelay: htlcCsvDelay, + CltvDelay: fn.Some(htlc.RefundTimeout), + CommitFee: chanState.RemoteCommitment.CommitFee, + PayHash: fn.Some(htlc.RHash), + CommitCsvDelay: csvDelay, + HtlcAmt: htlc.Amt.ToSatoshis(), + } + resolveRes := fn.MapOptionZ( + auxResolver, + func(a AuxContractResolver) fn.Result[tlv.Blob] { + return a.ResolveContract(resReq) + }, + ) + if err := resolveRes.Err(); err != nil { + return nil, fmt.Errorf("unable to aux resolve: %w", err) + } + + resolutionBlob := resolveRes.Option() + return &IncomingHtlcResolution{ - ClaimOutpoint: op, - SweepSignDesc: signDesc, - CsvDelay: HtlcSecondLevelInputSequence(chanType), + ClaimOutpoint: op, + SweepSignDesc: signDesc, + CsvDelay: htlcCsvDelay, + ResolutionBlob: resolutionBlob, }, nil } @@ -7464,30 +7587,76 @@ func newIncomingHtlcResolution(signer input.Signer, localDelayTweak := input.SingleTweakBytes( keyRing.CommitPoint, localChanCfg.DelayBasePoint.PubKey, ) + + // In addition to the info in txSignDetails, we also need extra + // information to sweep the second level output after confirmation. + sweepSignDesc := input.SignDescriptor{ + KeyDesc: localChanCfg.DelayBasePoint, + SingleTweak: localDelayTweak, + WitnessScript: htlcSweepWitnessScript, + Output: &wire.TxOut{ + PkScript: htlcSweepScript.PkScript(), + Value: int64(secondLevelOutputAmt), + }, + HashType: sweepSigHash(chanType), + PrevOutputFetcher: txscript.NewCannedPrevOutputFetcher( + htlcSweepScript.PkScript(), + int64(secondLevelOutputAmt), + ), + SignMethod: signMethod, + ControlBlock: ctrlBlock, + } + + resolveRes := fn.MapOptionZ( + auxResolver, func(a AuxContractResolver) fn.Result[tlv.Blob] { + resReq := ResolutionReq{ + ChanPoint: chanState.FundingOutpoint, + ChanType: chanType, + ShortChanID: chanState.ShortChanID(), + Initiator: chanState.IsInitiator, + CommitBlob: chanState.LocalCommitment.CustomBlob, //nolint:lll + Type: input.TaprootHtlcAcceptedLocalSuccess, //nolint:lll + FundingBlob: chanState.CustomBlob, + CloseType: LocalForceClose, + CommitTx: commitTx, + ContractPoint: op, + SignDesc: sweepSignDesc, + KeyRing: keyRing, + HtlcID: fn.Some(htlc.HtlcIndex), + CsvDelay: htlcCsvDelay, + CommitFee: chanState.LocalCommitment.CommitFee, //nolint:lll + PayHash: fn.Some(htlc.RHash), + AuxSigDesc: fn.Some(AuxSigDesc{ + SignDetails: *txSignDetails, + AuxSig: func() []byte { + tlvType := htlcCustomSigType.TypeVal() //nolint:lll + return htlc.CustomRecords[uint64(tlvType)] //nolint:lll + }(), + }), + CommitCsvDelay: csvDelay, + HtlcAmt: btcutil.Amount(txOut.Value), + CltvDelay: fn.Some(htlc.RefundTimeout), + } + + return a.ResolveContract(resReq) + }, + ) + if err := resolveRes.Err(); err != nil { + return nil, fmt.Errorf("unable to aux resolve: %w", err) + } + + resolutionBlob := resolveRes.Option() + return &IncomingHtlcResolution{ SignedSuccessTx: successTx, SignDetails: txSignDetails, CsvDelay: csvDelay, + ResolutionBlob: resolutionBlob, ClaimOutpoint: wire.OutPoint{ Hash: successTx.TxHash(), Index: 0, }, - SweepSignDesc: input.SignDescriptor{ - KeyDesc: localChanCfg.DelayBasePoint, - SingleTweak: localDelayTweak, - WitnessScript: htlcSweepWitnessScript, - Output: &wire.TxOut{ - PkScript: htlcSweepScript.PkScript(), - Value: int64(secondLevelOutputAmt), - }, - HashType: sweepSigHash(chanType), - PrevOutputFetcher: txscript.NewCannedPrevOutputFetcher( - htlcSweepScript.PkScript(), - int64(secondLevelOutputAmt), - ), - SignMethod: signMethod, - ControlBlock: ctrlBlock, - }, + SweepSignDesc: sweepSignDesc, }, nil } @@ -7524,7 +7693,8 @@ func extractHtlcResolutions(feePerKw chainfee.SatPerKWeight, localChanCfg, remoteChanCfg *channeldb.ChannelConfig, commitTx *wire.MsgTx, chanType channeldb.ChannelType, isCommitFromInitiator bool, leaseExpiry uint32, - auxLeaves fn.Option[CommitAuxLeaves]) (*HtlcResolutions, error) { + chanState *channeldb.OpenChannel, auxLeaves fn.Option[CommitAuxLeaves], + auxResolver fn.Option[AuxContractResolver]) (*HtlcResolutions, error) { // TODO(roasbeef): don't need to swap csv delay? dustLimit := remoteChanCfg.DustLimit @@ -7559,7 +7729,7 @@ func extractHtlcResolutions(feePerKw chainfee.SatPerKWeight, signer, localChanCfg, commitTx, &htlc, keyRing, feePerKw, uint32(csvDelay), leaseExpiry, whoseCommit, isCommitFromInitiator, - chanType, auxLeaves, + chanType, chanState, auxLeaves, auxResolver, ) if err != nil { return nil, fmt.Errorf("incoming resolution "+ @@ -7573,7 +7743,8 @@ func extractHtlcResolutions(feePerKw chainfee.SatPerKWeight, ohr, err := newOutgoingHtlcResolution( signer, localChanCfg, commitTx, &htlc, keyRing, feePerKw, uint32(csvDelay), leaseExpiry, whoseCommit, - isCommitFromInitiator, chanType, auxLeaves, + isCommitFromInitiator, chanType, chanState, auxLeaves, + auxResolver, ) if err != nil { return nil, fmt.Errorf("outgoing resolution "+ @@ -7825,7 +7996,8 @@ func NewLocalForceCloseSummary(chanState *channeldb.OpenChannel, func(a AuxContractResolver) fn.Result[tlv.Blob] { //nolint:lll return a.ResolveContract(ResolutionReq{ - ChanPoint: chanState.FundingOutpoint, + ChanPoint: chanState.FundingOutpoint, //nolint:lll + ChanType: chanState.ChanType, ShortChanID: chanState.ShortChanID(), Initiator: chanState.IsInitiator, CommitBlob: chanState.LocalCommitment.CustomBlob, @@ -7858,7 +8030,8 @@ func NewLocalForceCloseSummary(chanState *channeldb.OpenChannel, chainfee.SatPerKWeight(localCommit.FeePerKw), lntypes.Local, signer, localCommit.Htlcs, keyRing, &chanState.LocalChanCfg, &chanState.RemoteChanCfg, commitTx, chanState.ChanType, - chanState.IsInitiator, leaseExpiry, auxResult.AuxLeaves, + chanState.IsInitiator, leaseExpiry, chanState, + auxResult.AuxLeaves, auxResolver, ) if err != nil { return nil, fmt.Errorf("unable to gen htlc resolution: %w", err) diff --git a/macaroons/fuzz_test.go b/macaroons/fuzz_test.go new file mode 100644 index 0000000000..defae4143c --- /dev/null +++ b/macaroons/fuzz_test.go @@ -0,0 +1,51 @@ +package macaroons + +import ( + "context" + "testing" + + "gopkg.in/macaroon-bakery.v2/bakery" + "gopkg.in/macaroon.v2" +) + +func FuzzUnmarshalMacaroon(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + mac := &macaroon.Macaroon{} + _ = mac.UnmarshalBinary(data) + }) +} + +func FuzzAuthChecker(f *testing.F) { + rootKeyStore := bakery.NewMemRootKeyStore() + ctx := context.Background() + + f.Fuzz(func(t *testing.T, location, entity, action, method string, + rootKey, id []byte) { + + macService, err := NewService( + rootKeyStore, location, true, IPLockChecker, + ) + if err != nil { + return + } + + requiredPermissions := []bakery.Op{{ + Entity: entity, + Action: action, + }} + + mac, err := macaroon.New(rootKey, id, location, macaroon.V2) + if err != nil { + return + } + + macBytes, err := mac.MarshalBinary() + if err != nil { + return + } + + _ = macService.CheckMacAuth( + ctx, macBytes, requiredPermissions, method, + ) + }) +} From 413fdaf3fbbfa71395e89a54e81f0a0f18ff3764 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Tue, 15 Oct 2024 19:17:06 -0700 Subject: [PATCH 205/218] input: add new Preimage method to input.Input In this commit, we add a new method to obtain an option of a preimage to the input.Input struct. This is useful for callers that have an Input, and want to optionally obtain the preimage. --- contractcourt/breach_arbitrator.go | 5 +++++ input/input.go | 31 +++++++++++++++++++++++++++++- input/mocks.go | 11 +++++++++++ 3 files changed, 46 insertions(+), 1 deletion(-) diff --git a/contractcourt/breach_arbitrator.go b/contractcourt/breach_arbitrator.go index dc690e85c7..82b9977460 100644 --- a/contractcourt/breach_arbitrator.go +++ b/contractcourt/breach_arbitrator.go @@ -1160,6 +1160,11 @@ func (bo *breachedOutput) SignDesc() *input.SignDescriptor { return &bo.signDesc } +// Preimage returns the preimage that was used to create the breached output. +func (bo *breachedOutput) Preimage() fn.Option[lntypes.Preimage] { + return fn.None[lntypes.Preimage]() +} + // CraftInputScript computes a valid witness that allows us to spend from the // breached output. It does so by first generating and memoizing the witness // generation function, which parameterized primarily by the witness type and diff --git a/input/input.go b/input/input.go index 6693e9fa8a..6835f82c8e 100644 --- a/input/input.go +++ b/input/input.go @@ -69,6 +69,9 @@ type Input interface { // ResolutionBlob returns a special opaque blob to be used to // sweep/resolve this input. ResolutionBlob() fn.Option[tlv.Blob] + + // Preimage returns the preimage for the input if it is an HTLC input. + Preimage() fn.Option[lntypes.Preimage] } // TxInfo describes properties of a parent tx that are relevant for CPFP. @@ -285,6 +288,11 @@ func (bi *BaseInput) CraftInputScript(signer Signer, txn *wire.MsgTx, return witnessFunc(txn, hashCache, txinIdx) } +// Preimage returns the preimage for the input if it is an HTLC input. +func (bi *BaseInput) Preimage() fn.Option[lntypes.Preimage] { + return fn.None[lntypes.Preimage]() +} + // HtlcSucceedInput constitutes a sweep input that needs a pre-image. The input // is expected to reside on the commitment tx of the remote party and should // not be a second level tx output. @@ -357,7 +365,6 @@ func (h *HtlcSucceedInput) CraftInputScript(signer Signer, txn *wire.MsgTx, } desc.SignMethod = TaprootScriptSpendSignMethod - witness, err = SenderHTLCScriptTaprootRedeem( signer, &desc, txn, h.preimage, nil, nil, ) @@ -375,6 +382,15 @@ func (h *HtlcSucceedInput) CraftInputScript(signer Signer, txn *wire.MsgTx, }, nil } +// Preimage returns the preimage for the input if it is an HTLC input. +func (h *HtlcSucceedInput) Preimage() fn.Option[lntypes.Preimage] { + if len(h.preimage) == 0 { + return fn.None[lntypes.Preimage]() + } + + return fn.Some(lntypes.Preimage(h.preimage)) +} + // HtlcSecondLevelAnchorInput is an input type used to spend HTLC outputs // using a re-signed second level transaction, either via the timeout or success // paths. @@ -391,6 +407,8 @@ type HtlcSecondLevelAnchorInput struct { hashCache *txscript.TxSigHashes, prevOutputFetcher txscript.PrevOutputFetcher, txinIdx int) (wire.TxWitness, error) + + preimage []byte } // RequiredTxOut returns the tx out needed to be present on the sweep tx for @@ -427,6 +445,15 @@ func (i *HtlcSecondLevelAnchorInput) CraftInputScript(signer Signer, }, nil } +// Preimage returns the preimage for the input if it is an HTLC input. +func (i *HtlcSecondLevelAnchorInput) Preimage() fn.Option[lntypes.Preimage] { + if len(i.preimage) == 0 { + return fn.None[lntypes.Preimage]() + } + + return fn.Some(lntypes.Preimage(i.preimage)) +} + // MakeHtlcSecondLevelTimeoutAnchorInput creates an input allowing the sweeper // to spend the HTLC output on our commit using the second level timeout // transaction. @@ -545,6 +572,7 @@ func MakeHtlcSecondLevelSuccessAnchorInput(signedTx *wire.MsgTx, SignedTx: signedTx, inputKit: input.inputKit, createWitness: createWitness, + preimage: preimage[:], } } @@ -588,6 +616,7 @@ func MakeHtlcSecondLevelSuccessTaprootInput(signedTx *wire.MsgTx, inputKit: input.inputKit, SignedTx: signedTx, createWitness: createWitness, + preimage: preimage[:], } } diff --git a/input/mocks.go b/input/mocks.go index 695525955c..c2af637eda 100644 --- a/input/mocks.go +++ b/input/mocks.go @@ -140,6 +140,17 @@ func (m *MockInput) ResolutionBlob() fn.Option[tlv.Blob] { return info.(fn.Option[tlv.Blob]) } +func (m *MockInput) Preimage() fn.Option[lntypes.Preimage] { + args := m.Called() + + info := args.Get(0) + if info == nil { + return fn.None[lntypes.Preimage]() + } + + return info.(fn.Option[lntypes.Preimage]) +} + // MockWitnessType implements the `WitnessType` interface and is used by other // packages for mock testing. type MockWitnessType struct { From 6914e6a4a553196bddffefe3ed814414dd1280b0 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Tue, 15 Oct 2024 19:18:23 -0700 Subject: [PATCH 206/218] contractcourt: add HtlcBlobs to taprootBriefcase In this commit, we add the set of HtlcBlobs to the taprootBriefcase struct. This new field will store all the resolution blobs for a given HTLC. We also add some new property based tests along the way for adequate test coverage. --- contractcourt/taproot_briefcase.go | 124 +++++++++++++++++- contractcourt/taproot_briefcase_test.go | 41 ++++++ ...BlobEncodeDecode-20240902140253-81338.fail | 78 +++++++++++ go.mod | 1 + go.sum | 2 + 5 files changed, 242 insertions(+), 4 deletions(-) create mode 100644 contractcourt/testdata/rapid/TestHtlcAuxBlobEncodeDecode/TestHtlcAuxBlobEncodeDecode-20240902140253-81338.fail diff --git a/contractcourt/taproot_briefcase.go b/contractcourt/taproot_briefcase.go index 0a2beeff79..4b703dd295 100644 --- a/contractcourt/taproot_briefcase.go +++ b/contractcourt/taproot_briefcase.go @@ -39,7 +39,9 @@ type taprootBriefcase struct { // used to sweep a remote party's breached output. BreachedCommitBlob tlv.OptionalRecordT[tlv.TlvType3, tlv.Blob] - // TODO(roasbeef): htlc blobs + // HtlcBlobs is an optikonal record that contains the opaque blobs for + // the set of active HTLCs on the commitment transaction. + HtlcBlobs tlv.OptionalRecordT[tlv.TlvType4, htlcAuxBlobs] } // TODO(roasbeef): morph into new tlv record @@ -70,6 +72,9 @@ func (t *taprootBriefcase) EncodeRecords() []tlv.Record { records = append(records, r.Record()) }, ) + t.HtlcBlobs.WhenSome(func(r tlv.RecordT[tlv.TlvType4, htlcAuxBlobs]) { + records = append(records, r.Record()) + }) return records } @@ -96,10 +101,11 @@ func (t *taprootBriefcase) Encode(w io.Writer) error { func (t *taprootBriefcase) Decode(r io.Reader) error { settledCommitBlob := t.SettledCommitBlob.Zero() breachedCommitBlob := t.BreachedCommitBlob.Zero() + htlcBlobs := t.HtlcBlobs.Zero() + records := append( - t.DecodeRecords(), - settledCommitBlob.Record(), - breachedCommitBlob.Record(), + t.DecodeRecords(), settledCommitBlob.Record(), + breachedCommitBlob.Record(), htlcBlobs.Record(), ) stream, err := tlv.NewStream(records...) if err != nil { @@ -117,6 +123,9 @@ func (t *taprootBriefcase) Decode(r io.Reader) error { if v, ok := typeMap[t.BreachedCommitBlob.TlvType()]; ok && v == nil { t.BreachedCommitBlob = tlv.SomeRecordT(breachedCommitBlob) } + if v, ok := typeMap[t.HtlcBlobs.TlvType()]; ok && v == nil { + t.HtlcBlobs = tlv.SomeRecordT(htlcBlobs) + } return nil } @@ -686,3 +695,110 @@ func (t *tapTweaks) Decode(r io.Reader) error { return stream.Decode(r) } + +// htlcAuxBlobs is a map of resolver IDs to their corresponding HTLC blobs. +// This is used to store the resolution blobs for HTLCs that are not yet +// resolved. +type htlcAuxBlobs map[resolverID]tlv.Blob + +// newAuxHtlcBlobs returns a new instance of the htlcAuxBlobs struct. +func newAuxHtlcBlobs() htlcAuxBlobs { + return make(htlcAuxBlobs) +} + +// Encode encodes the set of HTLC blobs into the target writer. +func (h *htlcAuxBlobs) Encode(w io.Writer) error { + var buf [8]byte + + numBlobs := uint64(len(*h)) + if err := tlv.WriteVarInt(w, numBlobs, &buf); err != nil { + return err + } + + for id, blob := range *h { + if _, err := w.Write(id[:]); err != nil { + return err + } + + if err := varBytesEncoder(w, &blob, &buf); err != nil { + return err + } + } + + return nil +} + +// Decode decodes the set of HTLC blobs from the target reader. +func (h *htlcAuxBlobs) Decode(r io.Reader) error { + var buf [8]byte + + numBlobs, err := tlv.ReadVarInt(r, &buf) + if err != nil { + return err + } + + for i := uint64(0); i < numBlobs; i++ { + var id resolverID + if _, err := io.ReadFull(r, id[:]); err != nil { + return err + } + + var blob tlv.Blob + if err := varBytesDecoder(r, &blob, &buf, 0); err != nil { + return err + } + + (*h)[id] = blob + } + + return nil +} + +// eHtlcAuxBlobsEncoder is a custom TLV encoder for the htlcAuxBlobs struct. +func htlcAuxBlobsEncoder(w io.Writer, val any, _ *[8]byte) error { + if t, ok := val.(*htlcAuxBlobs); ok { + return (*t).Encode(w) + } + + return tlv.NewTypeForEncodingErr(val, "htlcAuxBlobs") +} + +// dHtlcAuxBlobsDecoder is a custom TLV decoder for the htlcAuxBlobs struct. +func htlcAuxBlobsDecoder(r io.Reader, val any, _ *[8]byte, + l uint64) error { + + if typ, ok := val.(*htlcAuxBlobs); ok { + blobReader := io.LimitReader(r, int64(l)) + + htlcBlobs := newAuxHtlcBlobs() + err := htlcBlobs.Decode(blobReader) + if err != nil { + return err + } + + *typ = htlcBlobs + + return nil + } + + return tlv.NewTypeForDecodingErr(val, "htlcAuxBlobs", l, l) +} + +// Record returns a tlv.Record for the htlcAuxBlobs struct. +func (h *htlcAuxBlobs) Record() tlv.Record { + recordSize := func() uint64 { + var ( + b bytes.Buffer + buf [8]byte + ) + if err := htlcAuxBlobsEncoder(&b, h, &buf); err != nil { + panic(err) + } + + return uint64(len(b.Bytes())) + } + + return tlv.MakeDynamicRecord( + 0, h, recordSize, htlcAuxBlobsEncoder, htlcAuxBlobsDecoder, + ) +} diff --git a/contractcourt/taproot_briefcase_test.go b/contractcourt/taproot_briefcase_test.go index a7d52d9635..441aebf1d7 100644 --- a/contractcourt/taproot_briefcase_test.go +++ b/contractcourt/taproot_briefcase_test.go @@ -7,6 +7,7 @@ import ( "github.com/lightningnetwork/lnd/tlv" "github.com/stretchr/testify/require" + "pgregory.net/rapid" ) func randResolverCtrlBlocks(t *testing.T) resolverCtrlBlocks { @@ -53,6 +54,25 @@ func randHtlcTweaks(t *testing.T) htlcTapTweaks { return tweaks } +func randHtlcAuxBlobs(t *testing.T) htlcAuxBlobs { + numBlobs := rand.Int() % 256 + blobs := make(htlcAuxBlobs, numBlobs) + + for i := 0; i < numBlobs; i++ { + var id resolverID + _, err := rand.Read(id[:]) + require.NoError(t, err) + + var blob [100]byte + _, err = rand.Read(blob[:]) + require.NoError(t, err) + + blobs[id] = blob[:] + } + + return blobs +} + // TestTaprootBriefcase tests the encode/decode methods of the taproot // briefcase extension. func TestTaprootBriefcase(t *testing.T) { @@ -93,6 +113,9 @@ func TestTaprootBriefcase(t *testing.T) { BreachedCommitBlob: tlv.SomeRecordT( tlv.NewPrimitiveRecord[tlv.TlvType3](commitBlob[:]), ), + HtlcBlobs: tlv.SomeRecordT( + tlv.NewRecordT[tlv.TlvType4](randHtlcAuxBlobs(t)), + ), } var b bytes.Buffer @@ -103,3 +126,21 @@ func TestTaprootBriefcase(t *testing.T) { require.Equal(t, testCase, &decodedCase) } + +// TestHtlcAuxBlobEncodeDecode tests the encode/decode methods of the HTLC aux +// blobs. +func TestHtlcAuxBlobEncodeDecode(t *testing.T) { + t.Parallel() + + rapid.Check(t, func(t *rapid.T) { + htlcBlobs := rapid.Make[htlcAuxBlobs]().Draw(t, "htlcAuxBlobs") + + var b bytes.Buffer + require.NoError(t, htlcBlobs.Encode(&b)) + + decodedBlobs := newAuxHtlcBlobs() + require.NoError(t, decodedBlobs.Decode(&b)) + + require.Equal(t, htlcBlobs, decodedBlobs) + }) +} diff --git a/contractcourt/testdata/rapid/TestHtlcAuxBlobEncodeDecode/TestHtlcAuxBlobEncodeDecode-20240902140253-81338.fail b/contractcourt/testdata/rapid/TestHtlcAuxBlobEncodeDecode/TestHtlcAuxBlobEncodeDecode-20240902140253-81338.fail new file mode 100644 index 0000000000..86bb07ed75 --- /dev/null +++ b/contractcourt/testdata/rapid/TestHtlcAuxBlobEncodeDecode/TestHtlcAuxBlobEncodeDecode-20240902140253-81338.fail @@ -0,0 +1,78 @@ +# 2024/09/02 14:02:53.354676 [TestHtlcAuxBlobEncodeDecode] [rapid] draw htlcAuxBlobs: contractcourt.htlcAuxBlobs{contractcourt.resolverID{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}:[]uint8{}} +# +v0.4.8#15807814492030881602 +0x5555555555555 +0x0 +0x0 +0x0 +0x0 +0x0 +0x0 +0x0 +0x0 +0x0 +0x0 +0x0 +0x0 +0x0 +0x0 +0x0 +0x0 +0x0 +0x0 +0x0 +0x0 +0x0 +0x0 +0x0 +0x0 +0x0 +0x0 +0x0 +0x0 +0x0 +0x0 +0x0 +0x0 +0x0 +0x0 +0x0 +0x0 +0x0 +0x0 +0x0 +0x0 +0x0 +0x0 +0x0 +0x0 +0x0 +0x0 +0x0 +0x0 +0x0 +0x0 +0x0 +0x0 +0x0 +0x0 +0x0 +0x0 +0x0 +0x0 +0x0 +0x0 +0x0 +0x0 +0x0 +0x0 +0x0 +0x0 +0x0 +0x0 +0x0 +0x0 +0x0 +0x0 +0x0 +0x0 \ No newline at end of file diff --git a/go.mod b/go.mod index 57c08f6996..288f98f727 100644 --- a/go.mod +++ b/go.mod @@ -62,6 +62,7 @@ require ( google.golang.org/protobuf v1.33.0 gopkg.in/macaroon-bakery.v2 v2.0.1 gopkg.in/macaroon.v2 v2.0.0 + pgregory.net/rapid v1.1.0 ) require ( diff --git a/go.sum b/go.sum index 20739ffbd8..3b3b0f5d09 100644 --- a/go.sum +++ b/go.sum @@ -1069,6 +1069,8 @@ modernc.org/strutil v1.2.0 h1:agBi9dp1I+eOnxXeiZawM8F4LawKv4NzGWSaLfyeNZA= modernc.org/strutil v1.2.0/go.mod h1:/mdcBmfOibveCTBxUl5B5l6W+TTH1FXPLHZE6bTosX0= modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y= modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= +pgregory.net/rapid v1.1.0 h1:CMa0sjHSru3puNx+J0MIAuiiEV4N0qj8/cMWGBBCsjw= +pgregory.net/rapid v1.1.0/go.mod h1:PY5XlDGj0+V1FCq0o192FdRhpKHGTRIWBgqjDBTrq04= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= From f1a1c033d167054003254ab48341a9d8b2adc82c Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Tue, 15 Oct 2024 19:19:22 -0700 Subject: [PATCH 207/218] contractcourt: update encode/decode for taproot aux data When we read/write the aux data, we need to make sure we always set the new fields for aux HTLCs. --- contractcourt/briefcase.go | 36 ++++++++++++++++++++++++++++++++---- 1 file changed, 32 insertions(+), 4 deletions(-) diff --git a/contractcourt/briefcase.go b/contractcourt/briefcase.go index 26df50b307..65608e7708 100644 --- a/contractcourt/briefcase.go +++ b/contractcourt/briefcase.go @@ -1564,6 +1564,7 @@ func encodeTaprootAuxData(w io.Writer, c *ContractResolutions) error { }) } + htlcBlobs := newAuxHtlcBlobs() for _, htlc := range c.HtlcResolutions.IncomingHTLCs { htlc := htlc @@ -1574,8 +1575,9 @@ func encodeTaprootAuxData(w io.Writer, c *ContractResolutions) error { continue } + var resID resolverID if htlc.SignedSuccessTx != nil { - resID := newResolverID( + resID = newResolverID( htlc.SignedSuccessTx.TxIn[0].PreviousOutPoint, ) //nolint:lll @@ -1591,10 +1593,14 @@ func encodeTaprootAuxData(w io.Writer, c *ContractResolutions) error { tapCase.CtrlBlocks.Val.IncomingHtlcCtrlBlocks[resID] = bridgeCtrlBlock } } else { - resID := newResolverID(htlc.ClaimOutpoint) + resID = newResolverID(htlc.ClaimOutpoint) //nolint:lll tapCase.CtrlBlocks.Val.IncomingHtlcCtrlBlocks[resID] = ctrlBlock } + + htlc.ResolutionBlob.WhenSome(func(b []byte) { + htlcBlobs[resID] = b + }) } for _, htlc := range c.HtlcResolutions.OutgoingHTLCs { htlc := htlc @@ -1606,8 +1612,9 @@ func encodeTaprootAuxData(w io.Writer, c *ContractResolutions) error { continue } + var resID resolverID if htlc.SignedTimeoutTx != nil { - resID := newResolverID( + resID = newResolverID( htlc.SignedTimeoutTx.TxIn[0].PreviousOutPoint, ) //nolint:lll @@ -1625,10 +1632,14 @@ func encodeTaprootAuxData(w io.Writer, c *ContractResolutions) error { tapCase.CtrlBlocks.Val.OutgoingHtlcCtrlBlocks[resID] = bridgeCtrlBlock } } else { - resID := newResolverID(htlc.ClaimOutpoint) + resID = newResolverID(htlc.ClaimOutpoint) //nolint:lll tapCase.CtrlBlocks.Val.OutgoingHtlcCtrlBlocks[resID] = ctrlBlock } + + htlc.ResolutionBlob.WhenSome(func(b []byte) { + htlcBlobs[resID] = b + }) } if c.AnchorResolution != nil { @@ -1636,6 +1647,12 @@ func encodeTaprootAuxData(w io.Writer, c *ContractResolutions) error { tapCase.TapTweaks.Val.AnchorTweak = anchorSignDesc.TapTweak } + if len(htlcBlobs) != 0 { + tapCase.HtlcBlobs = tlv.SomeRecordT( + tlv.NewRecordT[tlv.TlvType4](htlcBlobs), + ) + } + return tapCase.Encode(w) } @@ -1654,6 +1671,8 @@ func decodeTapRootAuxData(r io.Reader, c *ContractResolutions) error { }) } + htlcBlobs := tapCase.HtlcBlobs.ValOpt().UnwrapOr(newAuxHtlcBlobs()) + for i := range c.HtlcResolutions.IncomingHTLCs { htlc := c.HtlcResolutions.IncomingHTLCs[i] @@ -1680,7 +1699,12 @@ func decodeTapRootAuxData(r io.Reader, c *ContractResolutions) error { htlc.SweepSignDesc.ControlBlock = ctrlBlock } + if htlcBlob, ok := htlcBlobs[resID]; ok { + htlc.ResolutionBlob = fn.Some(htlcBlob) + } + c.HtlcResolutions.IncomingHTLCs[i] = htlc + } for i := range c.HtlcResolutions.OutgoingHTLCs { htlc := c.HtlcResolutions.OutgoingHTLCs[i] @@ -1708,6 +1732,10 @@ func decodeTapRootAuxData(r io.Reader, c *ContractResolutions) error { htlc.SweepSignDesc.ControlBlock = ctrlBlock } + if htlcBlob, ok := htlcBlobs[resID]; ok { + htlc.ResolutionBlob = fn.Some(htlcBlob) + } + c.HtlcResolutions.OutgoingHTLCs[i] = htlc } From 83f1c883bad633a02593045f7b4558705a130d5d Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Tue, 15 Oct 2024 19:36:11 -0700 Subject: [PATCH 208/218] contractcourt: pass in new aux resolution blob to sweeper in resolvers With this commit, we update all the resolvers to pass in the new htlc resolution blobs. Along the way, we remove the old blocking guard on this resolution logic for HTLCs with blobs. --- .../htlc_incoming_contest_resolver.go | 11 ----------- contractcourt/htlc_lease_resolver.go | 12 +++++++++--- .../htlc_outgoing_contest_resolver.go | 11 ----------- contractcourt/htlc_success_resolver.go | 19 +++++++------------ contractcourt/htlc_timeout_resolver.go | 16 +++++----------- 5 files changed, 21 insertions(+), 48 deletions(-) diff --git a/contractcourt/htlc_incoming_contest_resolver.go b/contractcourt/htlc_incoming_contest_resolver.go index 9ffa799437..6bda4e398b 100644 --- a/contractcourt/htlc_incoming_contest_resolver.go +++ b/contractcourt/htlc_incoming_contest_resolver.go @@ -99,17 +99,6 @@ func (h *htlcIncomingContestResolver) Resolve( return nil, nil } - // If the HTLC has custom records, then for now we'll pause resolution. - // - // TODO(roasbeef): Implement resolving HTLCs with custom records - // (follow-up PR). - if len(h.htlc.CustomRecords) != 0 { - select { //nolint:gosimple - case <-h.quit: - return nil, errResolverShuttingDown - } - } - // First try to parse the payload. If that fails, we can stop resolution // now. payload, nextHopOnionBlob, err := h.decodePayload() diff --git a/contractcourt/htlc_lease_resolver.go b/contractcourt/htlc_lease_resolver.go index 87e55d5cc4..53fa893553 100644 --- a/contractcourt/htlc_lease_resolver.go +++ b/contractcourt/htlc_lease_resolver.go @@ -6,7 +6,9 @@ import ( "github.com/btcsuite/btcd/wire" "github.com/lightningnetwork/lnd/chainntnfs" "github.com/lightningnetwork/lnd/channeldb" + "github.com/lightningnetwork/lnd/fn" "github.com/lightningnetwork/lnd/input" + "github.com/lightningnetwork/lnd/tlv" ) // htlcLeaseResolver is a struct that houses the lease specific HTLC resolution @@ -52,8 +54,8 @@ func (h *htlcLeaseResolver) deriveWaitHeight(csvDelay uint32, // send to the sweeper so the output can ultimately be swept. func (h *htlcLeaseResolver) makeSweepInput(op *wire.OutPoint, wType, cltvWtype input.StandardWitnessType, - signDesc *input.SignDescriptor, - csvDelay, broadcastHeight uint32, payHash [32]byte) *input.BaseInput { + signDesc *input.SignDescriptor, csvDelay, broadcastHeight uint32, + payHash [32]byte, resBlob fn.Option[tlv.Blob]) *input.BaseInput { if h.hasCLTV() { log.Infof("%T(%x): CSV and CLTV locks expired, offering "+ @@ -63,13 +65,17 @@ func (h *htlcLeaseResolver) makeSweepInput(op *wire.OutPoint, op, cltvWtype, signDesc, broadcastHeight, csvDelay, h.leaseExpiry, + input.WithResolutionBlob(resBlob), ) } log.Infof("%T(%x): CSV lock expired, offering second-layer output to "+ "sweeper: %v", h, payHash, op) - return input.NewCsvInput(op, wType, signDesc, broadcastHeight, csvDelay) + return input.NewCsvInput( + op, wType, signDesc, broadcastHeight, csvDelay, + input.WithResolutionBlob(resBlob), + ) } // SupplementState allows the user of a ContractResolver to supplement it with diff --git a/contractcourt/htlc_outgoing_contest_resolver.go b/contractcourt/htlc_outgoing_contest_resolver.go index c75b898222..2466544c98 100644 --- a/contractcourt/htlc_outgoing_contest_resolver.go +++ b/contractcourt/htlc_outgoing_contest_resolver.go @@ -58,17 +58,6 @@ func (h *htlcOutgoingContestResolver) Resolve( return nil, nil } - // If the HTLC has custom records, then for now we'll pause resolution. - // - // TODO(roasbeef): Implement resolving HTLCs with custom records - // (follow-up PR). - if len(h.htlc.CustomRecords) != 0 { - select { //nolint:gosimple - case <-h.quit: - return nil, errResolverShuttingDown - } - } - // Otherwise, we'll watch for two external signals to decide if we'll // morph into another resolver, or fully resolve the contract. // diff --git a/contractcourt/htlc_success_resolver.go b/contractcourt/htlc_success_resolver.go index 3b07828d48..4c9d2b200b 100644 --- a/contractcourt/htlc_success_resolver.go +++ b/contractcourt/htlc_success_resolver.go @@ -123,17 +123,6 @@ func (h *htlcSuccessResolver) Resolve( return nil, nil } - // If the HTLC has custom records, then for now we'll pause resolution. - // - // TODO(roasbeef): Implement resolving HTLCs with custom records - // (follow-up PR). - if len(h.htlc.CustomRecords) != 0 { - select { //nolint:gosimple - case <-h.quit: - return nil, errResolverShuttingDown - } - } - // If we don't have a success transaction, then this means that this is // an output on the remote party's commitment transaction. if h.htlcResolution.SignedSuccessTx == nil { @@ -258,6 +247,9 @@ func (h *htlcSuccessResolver) broadcastReSignedSuccessTx(immediate bool) ( h.htlcResolution.SignedSuccessTx, h.htlcResolution.SignDetails, h.htlcResolution.Preimage, h.broadcastHeight, + input.WithResolutionBlob( + h.htlcResolution.ResolutionBlob, + ), ) } else { //nolint:lll @@ -414,7 +406,7 @@ func (h *htlcSuccessResolver) broadcastReSignedSuccessTx(immediate bool) ( input.LeaseHtlcAcceptedSuccessSecondLevel, &h.htlcResolution.SweepSignDesc, h.htlcResolution.CsvDelay, uint32(commitSpend.SpendingHeight), - h.htlc.RHash, + h.htlc.RHash, h.htlcResolution.ResolutionBlob, ) // Calculate the budget for this sweep. @@ -470,6 +462,9 @@ func (h *htlcSuccessResolver) resolveRemoteCommitOutput(immediate bool) ( h.htlcResolution.Preimage[:], h.broadcastHeight, h.htlcResolution.CsvDelay, + input.WithResolutionBlob( + h.htlcResolution.ResolutionBlob, + ), )) } else { inp = lnutils.Ptr(input.MakeHtlcSucceedInput( diff --git a/contractcourt/htlc_timeout_resolver.go b/contractcourt/htlc_timeout_resolver.go index 670da607d4..f2de5f4a37 100644 --- a/contractcourt/htlc_timeout_resolver.go +++ b/contractcourt/htlc_timeout_resolver.go @@ -426,17 +426,6 @@ func (h *htlcTimeoutResolver) Resolve( return nil, nil } - // If the HTLC has custom records, then for now we'll pause resolution. - // - // TODO(roasbeef): Implement resolving HTLCs with custom records - // (follow-up PR). - if len(h.htlc.CustomRecords) != 0 { - select { //nolint:gosimple - case <-h.quit: - return nil, errResolverShuttingDown - } - } - // Start by spending the HTLC output, either by broadcasting the // second-level timeout transaction, or directly if this is the remote // commitment. @@ -495,6 +484,9 @@ func (h *htlcTimeoutResolver) sweepSecondLevelTx(immediate bool) error { h.htlcResolution.SignedTimeoutTx, h.htlcResolution.SignDetails, h.broadcastHeight, + input.WithResolutionBlob( + h.htlcResolution.ResolutionBlob, + ), )) } else { inp = lnutils.Ptr(input.MakeHtlcSecondLevelTimeoutAnchorInput( @@ -588,6 +580,7 @@ func (h *htlcTimeoutResolver) sweepDirectHtlcOutput(immediate bool) error { &h.htlcResolution.ClaimOutpoint, htlcWitnessType, &h.htlcResolution.SweepSignDesc, h.broadcastHeight, h.htlcResolution.CsvDelay, h.htlcResolution.Expiry, + input.WithResolutionBlob(h.htlcResolution.ResolutionBlob), ) // Calculate the budget. @@ -842,6 +835,7 @@ func (h *htlcTimeoutResolver) handleCommitSpend( &h.htlcResolution.SweepSignDesc, h.htlcResolution.CsvDelay, uint32(commitSpend.SpendingHeight), h.htlc.RHash, + h.htlcResolution.ResolutionBlob, ) // Calculate the budget for this sweep. From b8035d9db786b6f94f0d8437d671141b3b3c6757 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Thu, 7 Nov 2024 18:28:08 -0800 Subject: [PATCH 209/218] sweep: expand NotifyBroadcast to include an outpoint index In this commit, we expand the `NotifyBroadcast` to include an outpoint index. This is useful as it indicates the index of a given required tx out input. --- contractcourt/breach_arbitrator.go | 2 +- sweep/fee_bumper.go | 30 ++++++++++++++++++++++-------- sweep/interface.go | 3 ++- sweep/mock_test.go | 2 +- 4 files changed, 26 insertions(+), 11 deletions(-) diff --git a/contractcourt/breach_arbitrator.go b/contractcourt/breach_arbitrator.go index 82b9977460..a8154d0e61 100644 --- a/contractcourt/breach_arbitrator.go +++ b/contractcourt/breach_arbitrator.go @@ -752,7 +752,7 @@ justiceTxBroadcast: } return aux.NotifyBroadcast( - &bumpReq, finalTx.justiceTx, finalTx.fee, + &bumpReq, finalTx.justiceTx, finalTx.fee, nil, ) }) if err != nil { diff --git a/sweep/fee_bumper.go b/sweep/fee_bumper.go index fd3dfba9e1..5ea4d8e4bd 100644 --- a/sweep/fee_bumper.go +++ b/sweep/fee_bumper.go @@ -603,7 +603,9 @@ func (t *TxPublisher) broadcast(requestID uint64) (*BumpResult, error) { // Before we go to broadcast, we'll notify the aux sweeper, if it's // present of this new broadcast attempt. err := fn.MapOptionZ(t.cfg.AuxSweeper, func(aux AuxSweeper) error { - return aux.NotifyBroadcast(record.req, tx, record.fee) + return aux.NotifyBroadcast( + record.req, tx, record.fee, record.outpointToTxIndex, + ) }) if err != nil { return nil, fmt.Errorf("unable to notify aux sweeper: %w", err) @@ -725,6 +727,9 @@ type monitorRecord struct { // fee is the fee paid by the tx. fee btcutil.Amount + + // outpointToTxIndex is a map of outpoint to tx index. + outpointToTxIndex map[wire.OutPoint]int } // Start starts the publisher by subscribing to block epoch updates and kicking @@ -1042,10 +1047,11 @@ func (t *TxPublisher) createAndPublishTx(requestID uint64, // The tx has been created without any errors, we now register a new // record by overwriting the same requestID. t.records.Store(requestID, &monitorRecord{ - tx: sweepCtx.tx, - req: r.req, - feeFunction: r.feeFunction, - fee: sweepCtx.fee, + tx: sweepCtx.tx, + req: r.req, + feeFunction: r.feeFunction, + fee: sweepCtx.fee, + outpointToTxIndex: sweepCtx.outpointToTxIndex, }) // Attempt to broadcast this new tx. @@ -1199,6 +1205,10 @@ type sweepTxCtx struct { fee btcutil.Amount extraTxOut fn.Option[SweepOutput] + + // outpointToTxIndex maps the outpoint of the inputs to their index in + // the sweep transaction. + outpointToTxIndex map[wire.OutPoint]int } // createSweepTx creates a sweeping tx based on the given inputs, change @@ -1229,6 +1239,7 @@ func (t *TxPublisher) createSweepTx(inputs []input.Input, // We start by adding all inputs that commit to an output. We do this // since the input and output index must stay the same for the // signatures to be valid. + outpointToTxIndex := make(map[wire.OutPoint]int) for _, o := range inputs { if o.RequiredTxOut() == nil { continue @@ -1240,6 +1251,8 @@ func (t *TxPublisher) createSweepTx(inputs []input.Input, Sequence: o.BlocksToMaturity(), }) sweepTx.AddTxOut(o.RequiredTxOut()) + + outpointToTxIndex[o.OutPoint()] = len(sweepTx.TxOut) - 1 } // Sum up the value contained in the remaining inputs, and add them to @@ -1331,9 +1344,10 @@ func (t *TxPublisher) createSweepTx(inputs []input.Input, )(changeOutputsOpt) return &sweepTxCtx{ - tx: sweepTx, - fee: txFee, - extraTxOut: fn.FlattenOption(extraTxOut), + tx: sweepTx, + fee: txFee, + extraTxOut: fn.FlattenOption(extraTxOut), + outpointToTxIndex: outpointToTxIndex, }, nil } diff --git a/sweep/interface.go b/sweep/interface.go index acece31430..f2fff84b08 100644 --- a/sweep/interface.go +++ b/sweep/interface.go @@ -93,5 +93,6 @@ type AuxSweeper interface { // NotifyBroadcast is used to notify external callers of the broadcast // of a sweep transaction, generated by the passed BumpRequest. NotifyBroadcast(req *BumpRequest, tx *wire.MsgTx, - totalFees btcutil.Amount) error + totalFees btcutil.Amount, + outpointToTxIndex map[wire.OutPoint]int) error } diff --git a/sweep/mock_test.go b/sweep/mock_test.go index c623ca3c0b..34202b1453 100644 --- a/sweep/mock_test.go +++ b/sweep/mock_test.go @@ -352,7 +352,7 @@ func (m *MockAuxSweeper) ExtraBudgetForInputs( // NotifyBroadcast is used to notify external callers of the broadcast // of a sweep transaction, generated by the passed BumpRequest. func (*MockAuxSweeper) NotifyBroadcast(_ *BumpRequest, _ *wire.MsgTx, - _ btcutil.Amount) error { + _ btcutil.Amount, _ map[wire.OutPoint]int) error { return nil } From 6db7d426545ccdc17937a65475d0bbd742c5d59a Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Thu, 7 Nov 2024 18:42:42 -0800 Subject: [PATCH 210/218] lnwallet: add whoseCommit to FetchLeavesFromCommit This is useful for additional context to know which commit the AuxLeafStore is fetching the leaves for. --- contractcourt/chain_watcher.go | 1 + lnwallet/aux_leaf_store.go | 3 ++- lnwallet/channel.go | 10 ++++++---- lnwallet/commitment.go | 2 +- lnwallet/mock.go | 5 +++-- 5 files changed, 13 insertions(+), 8 deletions(-) diff --git a/contractcourt/chain_watcher.go b/contractcourt/chain_watcher.go index ef4a4e2008..ed34b5fa45 100644 --- a/contractcourt/chain_watcher.go +++ b/contractcourt/chain_watcher.go @@ -436,6 +436,7 @@ func (c *chainWatcher) handleUnknownLocalState( return s.FetchLeavesFromCommit( lnwallet.NewAuxChanState(c.cfg.chanState), c.cfg.chanState.LocalCommitment, *commitKeyRing, + lntypes.Local, ) }, ).Unpack() diff --git a/lnwallet/aux_leaf_store.go b/lnwallet/aux_leaf_store.go index 4558c2f81c..c457a92509 100644 --- a/lnwallet/aux_leaf_store.go +++ b/lnwallet/aux_leaf_store.go @@ -178,7 +178,8 @@ type AuxLeafStore interface { // commitment. FetchLeavesFromCommit(chanState AuxChanState, commit channeldb.ChannelCommitment, - keyRing CommitmentKeyRing) fn.Result[CommitDiffAuxResult] + keyRing CommitmentKeyRing, whoseCommit lntypes.ChannelParty, + ) fn.Result[CommitDiffAuxResult] // FetchLeavesFromRevocation attempts to fetch the auxiliary leaves // from a channel revocation that stores balance + blob information. diff --git a/lnwallet/channel.go b/lnwallet/channel.go index 67cd7b9472..ace6a8adcd 100644 --- a/lnwallet/channel.go +++ b/lnwallet/channel.go @@ -666,6 +666,7 @@ func (lc *LightningChannel) diskCommitToMemCommit( return s.FetchLeavesFromCommit( NewAuxChanState(lc.channelState), *diskCommit, *commitKeys.GetForParty(whoseCommit), + whoseCommit, ) }, ).Unpack() @@ -1798,7 +1799,7 @@ func (lc *LightningChannel) restorePendingLocalUpdates( func(s AuxLeafStore) fn.Result[CommitDiffAuxResult] { return s.FetchLeavesFromCommit( NewAuxChanState(lc.channelState), pendingCommit, - *pendingRemoteKeys, + *pendingRemoteKeys, lntypes.Remote, ) }, ).Unpack() @@ -3219,7 +3220,7 @@ func genRemoteHtlcSigJobs(keyRing *CommitmentKeyRing, leafStore, func(s AuxLeafStore) fn.Result[CommitDiffAuxResult] { return s.FetchLeavesFromCommit( NewAuxChanState(chanState), *diskCommit, - *keyRing, + *keyRing, lntypes.Remote, ) }, ).Unpack() @@ -4764,7 +4765,7 @@ func genHtlcSigValidationJobs(chanState *channeldb.OpenChannel, leafStore, func(s AuxLeafStore) fn.Result[CommitDiffAuxResult] { return s.FetchLeavesFromCommit( NewAuxChanState(chanState), *diskCommit, - *keyRing, + *keyRing, lntypes.Local, ) }, ).Unpack() @@ -6668,7 +6669,7 @@ func NewUnilateralCloseSummary(chanState *channeldb.OpenChannel, //nolint:funlen leafStore, func(s AuxLeafStore) fn.Result[CommitDiffAuxResult] { return s.FetchLeavesFromCommit( NewAuxChanState(chanState), remoteCommit, - *keyRing, + *keyRing, lntypes.Remote, ) }, ).Unpack() @@ -7890,6 +7891,7 @@ func NewLocalForceCloseSummary(chanState *channeldb.OpenChannel, return s.FetchLeavesFromCommit( NewAuxChanState(chanState), chanState.LocalCommitment, *keyRing, + lntypes.Local, ) }, ).Unpack() diff --git a/lnwallet/commitment.go b/lnwallet/commitment.go index 170efece1b..3f47819ead 100644 --- a/lnwallet/commitment.go +++ b/lnwallet/commitment.go @@ -1307,7 +1307,7 @@ func findOutputIndexesFromRemote(revocationPreimage *chainhash.Hash, leafStore, func(a AuxLeafStore) fn.Result[CommitDiffAuxResult] { return a.FetchLeavesFromCommit( NewAuxChanState(chanState), chanCommit, - *keyRing, + *keyRing, lntypes.Remote, ) }, ).Unpack() diff --git a/lnwallet/mock.go b/lnwallet/mock.go index e588c91c07..16f2d464bf 100644 --- a/lnwallet/mock.go +++ b/lnwallet/mock.go @@ -19,6 +19,7 @@ import ( "github.com/lightningnetwork/lnd/chainntnfs" "github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/fn" + "github.com/lightningnetwork/lnd/lntypes" "github.com/lightningnetwork/lnd/lnwallet/chainfee" "github.com/lightningnetwork/lnd/tlv" "github.com/stretchr/testify/mock" @@ -412,8 +413,8 @@ func (*MockAuxLeafStore) FetchLeavesFromView( // correspond to the passed aux blob, and an existing channel // commitment. func (*MockAuxLeafStore) FetchLeavesFromCommit(_ AuxChanState, - _ channeldb.ChannelCommitment, - _ CommitmentKeyRing) fn.Result[CommitDiffAuxResult] { + _ channeldb.ChannelCommitment, _ CommitmentKeyRing, + _ lntypes.ChannelParty) fn.Result[CommitDiffAuxResult] { return fn.Ok(CommitDiffAuxResult{}) } From e1a25f6c304f3bf00b2b5a0300f955a725c16d2b Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Thu, 7 Nov 2024 18:54:33 -0800 Subject: [PATCH 211/218] sweep: update BudgetInputSet.Budget() to factor in extra budget In this commit, we update the `Budget()` call to factor in the `extraBudget` value. Otherwise, when we go to intialize the fee function, we won't factor in the extra budget, and will determine that we can't broadcast/bump. --- sweep/tx_input_set.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/sweep/tx_input_set.go b/sweep/tx_input_set.go index 3a95fff2fb..ce144a8eb3 100644 --- a/sweep/tx_input_set.go +++ b/sweep/tx_input_set.go @@ -373,7 +373,9 @@ func (b *BudgetInputSet) Budget() btcutil.Amount { budget += input.params.Budget } - return budget + // We'll also tack on the extra budget which will eventually be + // accounted for by the wallet txns when we're broadcasting. + return budget + b.extraBudget } // DeadlineHeight returns the deadline height of the set. From 04b053e40506885bd9d5f9091bb2367bea1da1e5 Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Fri, 15 Nov 2024 09:50:01 +0100 Subject: [PATCH 212/218] docs: move 0.18.4 items, add full list of custom chan PRs --- docs/release-notes/release-notes-0.18.4.md | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/docs/release-notes/release-notes-0.18.4.md b/docs/release-notes/release-notes-0.18.4.md index dd3c979829..5d052cd5fc 100644 --- a/docs/release-notes/release-notes-0.18.4.md +++ b/docs/release-notes/release-notes-0.18.4.md @@ -26,8 +26,15 @@ # New Features The main channel state machine and database now allow for processing and storing -custom Taproot script leaves, [allowing the implementation of custom channel -types](https://github.com/lightningnetwork/lnd/pull/8960). +custom Taproot script leaves, allowing the implementation of custom channel +types in a series of changes: + * https://github.com/lightningnetwork/lnd/pull/9025 + * https://github.com/lightningnetwork/lnd/pull/9030 + * https://github.com/lightningnetwork/lnd/pull/9049 + * https://github.com/lightningnetwork/lnd/pull/9072 + * https://github.com/lightningnetwork/lnd/pull/9095 + * https://github.com/lightningnetwork/lnd/pull/8960 + * https://github.com/lightningnetwork/lnd/pull/9194 ## Functional Enhancements @@ -89,6 +96,10 @@ types](https://github.com/lightningnetwork/lnd/pull/8960). ## Breaking Changes ## Performance Improvements +* [A new method](https://github.com/lightningnetwork/lnd/pull/9195) + `AssertTxnsNotInMempool` has been added to `lntest` package to allow batch + exclusion check in itest. + # Technical and Architectural Updates ## BOLT Spec Updates From 86de1ebdc2c7d1330f8ffabb263aef2d799fabed Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Fri, 15 Nov 2024 17:14:15 -0800 Subject: [PATCH 213/218] sweep: update storeRecord to include utxo index In this commit, we complete a recently added feature by ensuring that even if we go through the RBF loop to create a txn, that we still populate the `outpointToIndex` map. Unit tests have been updated to ensure this is always set as expected. --- sweep/fee_bumper.go | 13 +++++--- sweep/fee_bumper_test.go | 71 +++++++++++++++++++++++++++++++++++----- 2 files changed, 71 insertions(+), 13 deletions(-) diff --git a/sweep/fee_bumper.go b/sweep/fee_bumper.go index 5ea4d8e4bd..adb4db65ed 100644 --- a/sweep/fee_bumper.go +++ b/sweep/fee_bumper.go @@ -455,6 +455,7 @@ func (t *TxPublisher) createRBFCompliantTx(req *BumpRequest, // The tx is valid, return the request ID. requestID := t.storeRecord( sweepCtx.tx, req, f, sweepCtx.fee, + sweepCtx.outpointToTxIndex, ) log.Infof("Created tx %v for %v inputs: feerate=%v, "+ @@ -510,7 +511,8 @@ func (t *TxPublisher) createRBFCompliantTx(req *BumpRequest, // storeRecord stores the given record in the records map. func (t *TxPublisher) storeRecord(tx *wire.MsgTx, req *BumpRequest, - f FeeFunction, fee btcutil.Amount) uint64 { + f FeeFunction, fee btcutil.Amount, + outpointToTxIndex map[wire.OutPoint]int) uint64 { // Increase the request counter. // @@ -520,10 +522,11 @@ func (t *TxPublisher) storeRecord(tx *wire.MsgTx, req *BumpRequest, // Register the record. t.records.Store(requestID, &monitorRecord{ - tx: tx, - req: req, - feeFunction: f, - fee: fee, + tx: tx, + req: req, + feeFunction: f, + fee: fee, + outpointToTxIndex: outpointToTxIndex, }) return requestID diff --git a/sweep/fee_bumper_test.go b/sweep/fee_bumper_test.go index 53b38607f7..5030dee227 100644 --- a/sweep/fee_bumper_test.go +++ b/sweep/fee_bumper_test.go @@ -323,8 +323,16 @@ func TestStoreRecord(t *testing.T) { // Get the current counter and check it's increased later. initialCounter := tp.requestCounter.Load() + op := wire.OutPoint{ + Hash: chainhash.Hash{1}, + Index: 0, + } + utxoIndex := map[wire.OutPoint]int{ + op: 0, + } + // Call the method under test. - requestID := tp.storeRecord(tx, req, feeFunc, fee) + requestID := tp.storeRecord(tx, req, feeFunc, fee, utxoIndex) // Check the request ID is as expected. require.Equal(t, initialCounter+1, requestID) @@ -336,6 +344,7 @@ func TestStoreRecord(t *testing.T) { require.Equal(t, feeFunc, record.feeFunction) require.Equal(t, fee, record.fee) require.Equal(t, req, record.req) + require.Equal(t, utxoIndex, record.outpointToTxIndex) } // mockers wraps a list of mocked interfaces used inside tx publisher. @@ -665,9 +674,17 @@ func TestTxPublisherBroadcast(t *testing.T) { feerate := chainfee.SatPerKWeight(1000) m.feeFunc.On("FeeRate").Return(feerate) + op := wire.OutPoint{ + Hash: chainhash.Hash{1}, + Index: 0, + } + utxoIndex := map[wire.OutPoint]int{ + op: 0, + } + // Create a testing record and put it in the map. fee := btcutil.Amount(1000) - requestID := tp.storeRecord(tx, req, m.feeFunc, fee) + requestID := tp.storeRecord(tx, req, m.feeFunc, fee, utxoIndex) // Quickly check when the requestID cannot be found, an error is // returned. @@ -754,6 +771,14 @@ func TestRemoveResult(t *testing.T) { // Create a testing record and put it in the map. fee := btcutil.Amount(1000) + op := wire.OutPoint{ + Hash: chainhash.Hash{1}, + Index: 0, + } + utxoIndex := map[wire.OutPoint]int{ + op: 0, + } + testCases := []struct { name string setupRecord func() uint64 @@ -765,7 +790,9 @@ func TestRemoveResult(t *testing.T) { // removed. name: "remove on TxConfirmed", setupRecord: func() uint64 { - id := tp.storeRecord(tx, req, m.feeFunc, fee) + id := tp.storeRecord( + tx, req, m.feeFunc, fee, utxoIndex, + ) tp.subscriberChans.Store(id, nil) return id @@ -780,7 +807,9 @@ func TestRemoveResult(t *testing.T) { // When the tx is failed, the records will be removed. name: "remove on TxFailed", setupRecord: func() uint64 { - id := tp.storeRecord(tx, req, m.feeFunc, fee) + id := tp.storeRecord( + tx, req, m.feeFunc, fee, utxoIndex, + ) tp.subscriberChans.Store(id, nil) return id @@ -796,7 +825,9 @@ func TestRemoveResult(t *testing.T) { // Noop when the tx is neither confirmed or failed. name: "noop when tx is not confirmed or failed", setupRecord: func() uint64 { - id := tp.storeRecord(tx, req, m.feeFunc, fee) + id := tp.storeRecord( + tx, req, m.feeFunc, fee, utxoIndex, + ) tp.subscriberChans.Store(id, nil) return id @@ -844,9 +875,17 @@ func TestNotifyResult(t *testing.T) { // Create a test tx. tx := &wire.MsgTx{LockTime: 1} + op := wire.OutPoint{ + Hash: chainhash.Hash{1}, + Index: 0, + } + utxoIndex := map[wire.OutPoint]int{ + op: 0, + } + // Create a testing record and put it in the map. fee := btcutil.Amount(1000) - requestID := tp.storeRecord(tx, req, m.feeFunc, fee) + requestID := tp.storeRecord(tx, req, m.feeFunc, fee, utxoIndex) // Create a subscription to the event. subscriber := make(chan *BumpResult, 1) @@ -1201,9 +1240,17 @@ func TestHandleTxConfirmed(t *testing.T) { // Create a test tx. tx := &wire.MsgTx{LockTime: 1} + op := wire.OutPoint{ + Hash: chainhash.Hash{1}, + Index: 0, + } + utxoIndex := map[wire.OutPoint]int{ + op: 0, + } + // Create a testing record and put it in the map. fee := btcutil.Amount(1000) - requestID := tp.storeRecord(tx, req, m.feeFunc, fee) + requestID := tp.storeRecord(tx, req, m.feeFunc, fee, utxoIndex) record, ok := tp.records.Load(requestID) require.True(t, ok) @@ -1273,9 +1320,17 @@ func TestHandleFeeBumpTx(t *testing.T) { tx: tx, } + op := wire.OutPoint{ + Hash: chainhash.Hash{1}, + Index: 0, + } + utxoIndex := map[wire.OutPoint]int{ + op: 0, + } + // Create a testing record and put it in the map. fee := btcutil.Amount(1000) - requestID := tp.storeRecord(tx, req, m.feeFunc, fee) + requestID := tp.storeRecord(tx, req, m.feeFunc, fee, utxoIndex) // Create a subscription to the event. subscriber := make(chan *BumpResult, 1) From 79d0655a96de5814759aab4800f1d79251e10f91 Mon Sep 17 00:00:00 2001 From: ziggie Date: Tue, 12 Nov 2024 20:15:20 +0100 Subject: [PATCH 214/218] multi: introduce an option for resolutions We don't always need the resolutions in the local force close summary so we make it an option. --- contractcourt/chain_arbitrator.go | 15 +++-- contractcourt/chain_watcher.go | 25 ++++++-- contractcourt/chain_watcher_test.go | 14 ++++- contractcourt/channel_arbitrator.go | 39 +++++++++--- contractcourt/channel_arbitrator_test.go | 51 ++++++++++------ lnwallet/channel.go | 76 +++++++++++++++++++----- lnwallet/channel_test.go | 36 ++++++----- lnwallet/transactions_test.go | 7 ++- 8 files changed, 194 insertions(+), 69 deletions(-) diff --git a/contractcourt/chain_arbitrator.go b/contractcourt/chain_arbitrator.go index c29178b438..d7d10ba252 100644 --- a/contractcourt/chain_arbitrator.go +++ b/contractcourt/chain_arbitrator.go @@ -335,11 +335,10 @@ func (a *arbChannel) NewAnchorResolutions() (*lnwallet.AnchorResolutions, // ForceCloseChan should force close the contract that this attendant is // watching over. We'll use this when we decide that we need to go to chain. It // should in addition tell the switch to remove the corresponding link, such -// that we won't accept any new updates. The returned summary contains all items -// needed to eventually resolve all outputs on chain. +// that we won't accept any new updates. // // NOTE: Part of the ArbChannel interface. -func (a *arbChannel) ForceCloseChan() (*lnwallet.LocalForceCloseSummary, error) { +func (a *arbChannel) ForceCloseChan() (*wire.MsgTx, error) { // First, we mark the channel as borked, this ensure // that no new state transitions can happen, and also // that the link won't be loaded into the switch. @@ -386,7 +385,15 @@ func (a *arbChannel) ForceCloseChan() (*lnwallet.LocalForceCloseSummary, error) if err != nil { return nil, err } - return chanMachine.ForceClose() + + closeSummary, err := chanMachine.ForceClose( + lnwallet.WithSkipContractResolutions(), + ) + if err != nil { + return nil, err + } + + return closeSummary.CloseTx, nil } // newActiveChannelArbitrator creates a new instance of an active channel diff --git a/contractcourt/chain_watcher.go b/contractcourt/chain_watcher.go index ed34b5fa45..48d092ae97 100644 --- a/contractcourt/chain_watcher.go +++ b/contractcourt/chain_watcher.go @@ -1175,16 +1175,29 @@ func (c *chainWatcher) dispatchLocalForceClose( LocalChanConfig: c.cfg.chanState.LocalChanCfg, } + resolutions, err := forceClose.ContractResolutions.UnwrapOrErr( + fmt.Errorf("resolutions not found"), + ) + if err != nil { + return err + } + // If our commitment output isn't dust or we have active HTLC's on the // commitment transaction, then we'll populate the balances on the // close channel summary. - if forceClose.CommitResolution != nil { - closeSummary.SettledBalance = chanSnapshot.LocalBalance.ToSatoshis() - closeSummary.TimeLockedBalance = chanSnapshot.LocalBalance.ToSatoshis() + if resolutions.CommitResolution != nil { + localBalance := chanSnapshot.LocalBalance.ToSatoshis() + closeSummary.SettledBalance = localBalance + closeSummary.TimeLockedBalance = localBalance } - for _, htlc := range forceClose.HtlcResolutions.OutgoingHTLCs { - htlcValue := btcutil.Amount(htlc.SweepSignDesc.Output.Value) - closeSummary.TimeLockedBalance += htlcValue + + if resolutions.HtlcResolutions != nil { + for _, htlc := range resolutions.HtlcResolutions.OutgoingHTLCs { + htlcValue := btcutil.Amount( + htlc.SweepSignDesc.Output.Value, + ) + closeSummary.TimeLockedBalance += htlcValue + } } // Attempt to add a channel sync message to the close summary. diff --git a/contractcourt/chain_watcher_test.go b/contractcourt/chain_watcher_test.go index 2781170f0c..baea8d8738 100644 --- a/contractcourt/chain_watcher_test.go +++ b/contractcourt/chain_watcher_test.go @@ -504,14 +504,24 @@ func TestChainWatcherLocalForceCloseDetect(t *testing.T) { // outputs. select { case summary := <-chanEvents.LocalUnilateralClosure: + resOpt := summary.LocalForceCloseSummary. + ContractResolutions + + resolutions, err := resOpt.UnwrapOrErr( + fmt.Errorf("resolutions not found"), + ) + if err != nil { + t.Fatalf("unable to get resolutions: %v", err) + } + // Make sure we correctly extracted the commit // resolution if we had a local output. if remoteOutputOnly { - if summary.CommitResolution != nil { + if resolutions.CommitResolution != nil { t.Fatalf("expected no commit resolution") } } else { - if summary.CommitResolution == nil { + if resolutions.CommitResolution == nil { t.Fatalf("expected commit resolution") } } diff --git a/contractcourt/channel_arbitrator.go b/contractcourt/channel_arbitrator.go index 4ddb52f645..46af3e5aeb 100644 --- a/contractcourt/channel_arbitrator.go +++ b/contractcourt/channel_arbitrator.go @@ -98,7 +98,7 @@ type ArbChannel interface { // corresponding link, such that we won't accept any new updates. The // returned summary contains all items needed to eventually resolve all // outputs on chain. - ForceCloseChan() (*lnwallet.LocalForceCloseSummary, error) + ForceCloseChan() (*wire.MsgTx, error) // NewAnchorResolutions returns the anchor resolutions for currently // valid commitment transactions. @@ -1058,7 +1058,7 @@ func (c *ChannelArbitrator) stateStep( // We'll tell the switch that it should remove the link for // this channel, in addition to fetching the force close // summary needed to close this channel on chain. - closeSummary, err := c.cfg.Channel.ForceCloseChan() + forceCloseTx, err := c.cfg.Channel.ForceCloseChan() if err != nil { log.Errorf("ChannelArbitrator(%v): unable to "+ "force close: %v", c.cfg.ChanPoint, err) @@ -1078,7 +1078,7 @@ func (c *ChannelArbitrator) stateStep( return StateError, closeTx, err } - closeTx = closeSummary.CloseTx + closeTx = forceCloseTx // Before publishing the transaction, we store it to the // database, such that we can re-publish later in case it @@ -2871,11 +2871,36 @@ func (c *ChannelArbitrator) channelAttendant(bestHeight int32) { } closeTx := closeInfo.CloseTx + resolutions, err := closeInfo.ContractResolutions. + UnwrapOrErr( + fmt.Errorf("resolutions not found"), + ) + if err != nil { + log.Errorf("ChannelArbitrator(%v): unable to "+ + "get resolutions: %v", c.cfg.ChanPoint, + err) + + return + } + + // We make sure that the htlc resolutions are present + // otherwise we would panic dereferencing the pointer. + // + // TODO(ziggie): Refactor ContractResolutions to use + // options. + if resolutions.HtlcResolutions == nil { + log.Errorf("ChannelArbitrator(%v): htlc "+ + "resolutions not found", + c.cfg.ChanPoint) + + return + } + contractRes := &ContractResolutions{ CommitHash: closeTx.TxHash(), - CommitResolution: closeInfo.CommitResolution, - HtlcResolutions: *closeInfo.HtlcResolutions, - AnchorResolution: closeInfo.AnchorResolution, + CommitResolution: resolutions.CommitResolution, + HtlcResolutions: *resolutions.HtlcResolutions, + AnchorResolution: resolutions.AnchorResolution, } // When processing a unilateral close event, we'll @@ -2884,7 +2909,7 @@ func (c *ChannelArbitrator) channelAttendant(bestHeight int32) { // available to fetch in that state, we'll also write // the commit set so we can reconstruct our chain // actions on restart. - err := c.log.LogContractResolutions(contractRes) + err = c.log.LogContractResolutions(contractRes) if err != nil { log.Errorf("Unable to write resolutions: %v", err) diff --git a/contractcourt/channel_arbitrator_test.go b/contractcourt/channel_arbitrator_test.go index 916cd5f580..7f8c4b087f 100644 --- a/contractcourt/channel_arbitrator_test.go +++ b/contractcourt/channel_arbitrator_test.go @@ -693,11 +693,15 @@ func TestChannelArbitratorLocalForceClose(t *testing.T) { chanArbCtx.AssertState(StateCommitmentBroadcasted) // Now notify about the local force close getting confirmed. + // + //nolint:lll chanArb.cfg.ChainEvents.LocalUnilateralClosure <- &LocalUnilateralCloseInfo{ SpendDetail: &chainntnfs.SpendDetail{}, LocalForceCloseSummary: &lnwallet.LocalForceCloseSummary{ - CloseTx: &wire.MsgTx{}, - HtlcResolutions: &lnwallet.HtlcResolutions{}, + CloseTx: &wire.MsgTx{}, + ContractResolutions: fn.Some(lnwallet.ContractResolutions{ + HtlcResolutions: &lnwallet.HtlcResolutions{}, + }), }, ChannelCloseSummary: &channeldb.ChannelCloseSummary{}, } @@ -969,15 +973,18 @@ func TestChannelArbitratorLocalForceClosePendingHtlc(t *testing.T) { }, } + //nolint:lll chanArb.cfg.ChainEvents.LocalUnilateralClosure <- &LocalUnilateralCloseInfo{ SpendDetail: &chainntnfs.SpendDetail{}, LocalForceCloseSummary: &lnwallet.LocalForceCloseSummary{ CloseTx: closeTx, - HtlcResolutions: &lnwallet.HtlcResolutions{ - OutgoingHTLCs: []lnwallet.OutgoingHtlcResolution{ - outgoingRes, + ContractResolutions: fn.Some(lnwallet.ContractResolutions{ + HtlcResolutions: &lnwallet.HtlcResolutions{ + OutgoingHTLCs: []lnwallet.OutgoingHtlcResolution{ + outgoingRes, + }, }, - }, + }), }, ChannelCloseSummary: &channeldb.ChannelCloseSummary{}, CommitSet: CommitSet{ @@ -1611,12 +1618,15 @@ func TestChannelArbitratorCommitFailure(t *testing.T) { }, { closeType: channeldb.LocalForceClose, + //nolint:lll sendEvent: func(chanArb *ChannelArbitrator) { chanArb.cfg.ChainEvents.LocalUnilateralClosure <- &LocalUnilateralCloseInfo{ SpendDetail: &chainntnfs.SpendDetail{}, LocalForceCloseSummary: &lnwallet.LocalForceCloseSummary{ - CloseTx: &wire.MsgTx{}, - HtlcResolutions: &lnwallet.HtlcResolutions{}, + CloseTx: &wire.MsgTx{}, + ContractResolutions: fn.Some(lnwallet.ContractResolutions{ + HtlcResolutions: &lnwallet.HtlcResolutions{}, + }), }, ChannelCloseSummary: &channeldb.ChannelCloseSummary{}, } @@ -1944,11 +1954,15 @@ func TestChannelArbitratorDanglingCommitForceClose(t *testing.T) { // being canalled back. Also note that there're no HTLC // resolutions sent since we have none on our // commitment transaction. + // + //nolint:lll uniCloseInfo := &LocalUnilateralCloseInfo{ SpendDetail: &chainntnfs.SpendDetail{}, LocalForceCloseSummary: &lnwallet.LocalForceCloseSummary{ - CloseTx: closeTx, - HtlcResolutions: &lnwallet.HtlcResolutions{}, + CloseTx: closeTx, + ContractResolutions: fn.Some(lnwallet.ContractResolutions{ + HtlcResolutions: &lnwallet.HtlcResolutions{}, + }), }, ChannelCloseSummary: &channeldb.ChannelCloseSummary{}, CommitSet: CommitSet{ @@ -2754,12 +2768,15 @@ func TestChannelArbitratorAnchors(t *testing.T) { }, } + //nolint:lll chanArb.cfg.ChainEvents.LocalUnilateralClosure <- &LocalUnilateralCloseInfo{ SpendDetail: &chainntnfs.SpendDetail{}, LocalForceCloseSummary: &lnwallet.LocalForceCloseSummary{ - CloseTx: closeTx, - HtlcResolutions: &lnwallet.HtlcResolutions{}, - AnchorResolution: anchorResolution, + CloseTx: closeTx, + ContractResolutions: fn.Some(lnwallet.ContractResolutions{ + HtlcResolutions: &lnwallet.HtlcResolutions{}, + AnchorResolution: anchorResolution, + }), }, ChannelCloseSummary: &channeldb.ChannelCloseSummary{}, CommitSet: CommitSet{ @@ -2993,14 +3010,10 @@ func (m *mockChannel) NewAnchorResolutions() (*lnwallet.AnchorResolutions, return &lnwallet.AnchorResolutions{}, nil } -func (m *mockChannel) ForceCloseChan() (*lnwallet.LocalForceCloseSummary, error) { +func (m *mockChannel) ForceCloseChan() (*wire.MsgTx, error) { if m.forceCloseErr != nil { return nil, m.forceCloseErr } - summary := &lnwallet.LocalForceCloseSummary{ - CloseTx: &wire.MsgTx{}, - HtlcResolutions: &lnwallet.HtlcResolutions{}, - } - return summary, nil + return &wire.MsgTx{}, nil } diff --git a/lnwallet/channel.go b/lnwallet/channel.go index ace6a8adcd..e2cc3e0540 100644 --- a/lnwallet/channel.go +++ b/lnwallet/channel.go @@ -7793,6 +7793,18 @@ type LocalForceCloseSummary struct { // commitment state. CloseTx *wire.MsgTx + // ChanSnapshot is a snapshot of the final state of the channel at the + // time the summary was created. + ChanSnapshot channeldb.ChannelSnapshot + + // ContractResolutions contains all the data required for resolving the + // different output types of a commitment transaction. + ContractResolutions fn.Option[ContractResolutions] +} + +// ContractResolutions contains all the data required for resolving the +// different output types of a commitment transaction. +type ContractResolutions struct { // CommitResolution contains all the data required to sweep the output // to ourselves. Since this is our commitment transaction, we'll need // to wait a time delay before we can sweep the output. @@ -7801,19 +7813,38 @@ type LocalForceCloseSummary struct { // then this will be nil. CommitResolution *CommitOutputResolution + // AnchorResolution contains the data required to sweep the anchor + // output. If the channel type doesn't include anchors, the value of + // this field will be nil. + AnchorResolution *AnchorResolution + // HtlcResolutions contains all the data required to sweep any outgoing // HTLC's and incoming HTLc's we know the preimage to. For each of these // HTLC's, we'll need to go to the second level to sweep them fully. HtlcResolutions *HtlcResolutions +} - // ChanSnapshot is a snapshot of the final state of the channel at the - // time the summary was created. - ChanSnapshot channeldb.ChannelSnapshot +// ForceCloseOpt is a functional option argument for the ForceClose method. +type ForceCloseOpt func(*forceCloseConfig) - // AnchorResolution contains the data required to sweep the anchor - // output. If the channel type doesn't include anchors, the value of - // this field will be nil. - AnchorResolution *AnchorResolution +// forceCloseConfig holds the configuration options for force closing a channel. +type forceCloseConfig struct { + // skipResolution if true will skip creating the contract resolutions + // when generating the force close summary. + skipResolution bool +} + +// defaultForceCloseConfig returns the default force close configuration. +func defaultForceCloseConfig() *forceCloseConfig { + return &forceCloseConfig{} +} + +// WithSkipContractResolutions creates an option to skip the contract +// resolutions from the returned summary. +func WithSkipContractResolutions() ForceCloseOpt { + return func(cfg *forceCloseConfig) { + cfg.skipResolution = true + } } // ForceClose executes a unilateral closure of the transaction at the current @@ -7824,10 +7855,17 @@ type LocalForceCloseSummary struct { // outputs within the commitment transaction. // // TODO(roasbeef): all methods need to abort if in dispute state -func (lc *LightningChannel) ForceClose() (*LocalForceCloseSummary, error) { +func (lc *LightningChannel) ForceClose(opts ...ForceCloseOpt) ( + *LocalForceCloseSummary, error) { + lc.Lock() defer lc.Unlock() + cfg := defaultForceCloseConfig() + for _, opt := range opts { + opt(cfg) + } + // If we've detected local data loss for this channel, then we won't // allow a force close, as it may be the case that we have a dated // version of the commitment, or this is actually a channel shell. @@ -7842,6 +7880,14 @@ func (lc *LightningChannel) ForceClose() (*LocalForceCloseSummary, error) { return nil, err } + if cfg.skipResolution { + return &LocalForceCloseSummary{ + ChanPoint: lc.channelState.FundingOutpoint, + ChanSnapshot: *lc.channelState.Snapshot(), + CloseTx: commitTx, + }, nil + } + localCommitment := lc.channelState.LocalCommitment summary, err := NewLocalForceCloseSummary( lc.channelState, lc.Signer, commitTx, @@ -8048,12 +8094,14 @@ func NewLocalForceCloseSummary(chanState *channeldb.OpenChannel, } return &LocalForceCloseSummary{ - ChanPoint: chanState.FundingOutpoint, - CloseTx: commitTx, - CommitResolution: commitResolution, - HtlcResolutions: htlcResolutions, - ChanSnapshot: *chanState.Snapshot(), - AnchorResolution: anchorResolution, + ChanPoint: chanState.FundingOutpoint, + CloseTx: commitTx, + ChanSnapshot: *chanState.Snapshot(), + ContractResolutions: fn.Some(ContractResolutions{ + CommitResolution: commitResolution, + HtlcResolutions: htlcResolutions, + AnchorResolution: anchorResolution, + }), }, nil } diff --git a/lnwallet/channel_test.go b/lnwallet/channel_test.go index f6dd434f67..232497e255 100644 --- a/lnwallet/channel_test.go +++ b/lnwallet/channel_test.go @@ -958,24 +958,26 @@ func testForceClose(t *testing.T, testCase *forceCloseTestCase) { closeSummary, err := aliceChannel.ForceClose() require.NoError(t, err, "unable to force close channel") + resolutionsAlice := closeSummary.ContractResolutions.UnwrapOrFail(t) + // Alice should detect that she can sweep the outgoing HTLC after a // timeout, but also that she's able to sweep in incoming HTLC Bob sent // her. - if len(closeSummary.HtlcResolutions.OutgoingHTLCs) != 1 { + if len(resolutionsAlice.HtlcResolutions.OutgoingHTLCs) != 1 { t.Fatalf("alice out htlc resolutions not populated: expected %v "+ "htlcs, got %v htlcs", - 1, len(closeSummary.HtlcResolutions.OutgoingHTLCs)) + 1, len(resolutionsAlice.HtlcResolutions.OutgoingHTLCs)) } - if len(closeSummary.HtlcResolutions.IncomingHTLCs) != 1 { + if len(resolutionsAlice.HtlcResolutions.IncomingHTLCs) != 1 { t.Fatalf("alice in htlc resolutions not populated: expected %v "+ "htlcs, got %v htlcs", - 1, len(closeSummary.HtlcResolutions.IncomingHTLCs)) + 1, len(resolutionsAlice.HtlcResolutions.IncomingHTLCs)) } // Verify the anchor resolutions for the anchor commitment format. if testCase.chanType.HasAnchors() { // Check the close summary resolution. - anchorRes := closeSummary.AnchorResolution + anchorRes := resolutionsAlice.AnchorResolution if anchorRes == nil { t.Fatal("expected anchor resolution") } @@ -1009,7 +1011,7 @@ func testForceClose(t *testing.T, testCase *forceCloseTestCase) { // The SelfOutputSignDesc should be non-nil since the output to-self is // non-dust. - aliceCommitResolution := closeSummary.CommitResolution + aliceCommitResolution := resolutionsAlice.CommitResolution if aliceCommitResolution == nil { t.Fatalf("alice fails to include to-self output in " + "ForceCloseSummary") @@ -1055,7 +1057,7 @@ func testForceClose(t *testing.T, testCase *forceCloseTestCase) { // Next, we'll ensure that the second level HTLC transaction it itself // spendable, and also that the delivery output (with delay) itself has // a valid sign descriptor. - htlcResolution := closeSummary.HtlcResolutions.OutgoingHTLCs[0] + htlcResolution := resolutionsAlice.HtlcResolutions.OutgoingHTLCs[0] outHtlcIndex := htlcResolution.SignedTimeoutTx.TxIn[0].PreviousOutPoint.Index senderHtlcPkScript := closeSummary.CloseTx.TxOut[outHtlcIndex].PkScript @@ -1139,7 +1141,7 @@ func testForceClose(t *testing.T, testCase *forceCloseTestCase) { // We'll now perform similar set of checks to ensure that Alice is able // to sweep the output that Bob sent to her on-chain with knowledge of // the preimage. - inHtlcResolution := closeSummary.HtlcResolutions.IncomingHTLCs[0] + inHtlcResolution := resolutionsAlice.HtlcResolutions.IncomingHTLCs[0] inHtlcIndex := inHtlcResolution.SignedSuccessTx.TxIn[0].PreviousOutPoint.Index receiverHtlcScript := closeSummary.CloseTx.TxOut[inHtlcIndex].PkScript @@ -1220,7 +1222,8 @@ func testForceClose(t *testing.T, testCase *forceCloseTestCase) { // Check the same for Bob's ForceCloseSummary. closeSummary, err = bobChannel.ForceClose() require.NoError(t, err, "unable to force close channel") - bobCommitResolution := closeSummary.CommitResolution + resolutionsBob := closeSummary.ContractResolutions.UnwrapOrFail(t) + bobCommitResolution := resolutionsBob.CommitResolution if bobCommitResolution == nil { t.Fatalf("bob fails to include to-self output in ForceCloseSummary") } @@ -1254,19 +1257,19 @@ func testForceClose(t *testing.T, testCase *forceCloseTestCase) { // As we didn't add the preimage of Alice's HTLC to bob's preimage // cache, he should only detect that he can sweep only his outgoing // HTLC upon force close. - if len(closeSummary.HtlcResolutions.OutgoingHTLCs) != 1 { + if len(resolutionsBob.HtlcResolutions.OutgoingHTLCs) != 1 { t.Fatalf("alice out htlc resolutions not populated: expected %v "+ "htlcs, got %v htlcs", - 1, len(closeSummary.HtlcResolutions.OutgoingHTLCs)) + 1, len(resolutionsBob.HtlcResolutions.OutgoingHTLCs)) } // Bob should recognize that the incoming HTLC is there, but the // preimage should be empty as he doesn't have the knowledge required // to sweep it. - if len(closeSummary.HtlcResolutions.IncomingHTLCs) != 1 { + if len(resolutionsBob.HtlcResolutions.IncomingHTLCs) != 1 { t.Fatalf("bob in htlc resolutions not populated: expected %v "+ "htlcs, got %v htlcs", - 1, len(closeSummary.HtlcResolutions.IncomingHTLCs)) + 1, len(resolutionsBob.HtlcResolutions.IncomingHTLCs)) } } @@ -1325,9 +1328,11 @@ func TestForceCloseDustOutput(t *testing.T) { closeSummary, err := aliceChannel.ForceClose() require.NoError(t, err, "unable to force close channel") + resolutionsAlice := closeSummary.ContractResolutions.UnwrapOrFail(t) + // Alice's to-self output should still be in the commitment // transaction. - commitResolution := closeSummary.CommitResolution + commitResolution := resolutionsAlice.CommitResolution if commitResolution == nil { t.Fatalf("alice fails to include to-self output in " + "ForceCloseSummary") @@ -1361,10 +1366,11 @@ func TestForceCloseDustOutput(t *testing.T) { closeSummary, err = bobChannel.ForceClose() require.NoError(t, err, "unable to force close channel") + resolutionsBob := closeSummary.ContractResolutions.UnwrapOrFail(t) // Bob's to-self output is below Bob's dust value and should be // reflected in the ForceCloseSummary. - commitResolution = closeSummary.CommitResolution + commitResolution = resolutionsBob.CommitResolution if commitResolution != nil { t.Fatalf("bob incorrectly includes to-self output in " + "ForceCloseSummary") diff --git a/lnwallet/transactions_test.go b/lnwallet/transactions_test.go index 0f7988a32c..7912772962 100644 --- a/lnwallet/transactions_test.go +++ b/lnwallet/transactions_test.go @@ -406,6 +406,8 @@ func testVectors(t *testing.T, chanType channeldb.ChannelType, test testCase) { hex.EncodeToString(txBytes.Bytes()), ) + resolutions := forceCloseSum.ContractResolutions.UnwrapOrFail(t) + // Obtain the second level transactions that the local node's channel // state machine has produced. Store them in a map indexed by commit tx // output index. Also complete the second level transaction with the @@ -419,7 +421,8 @@ func testVectors(t *testing.T, chanType channeldb.ChannelType, test testCase) { secondLevelTxes[index] = tx } - for _, r := range forceCloseSum.HtlcResolutions.IncomingHTLCs { + htlcResolutions := resolutions.HtlcResolutions + for _, r := range htlcResolutions.IncomingHTLCs { successTx := r.SignedSuccessTx witnessScript := successTx.TxIn[0].Witness[4] var hash160 [20]byte @@ -428,7 +431,7 @@ func testVectors(t *testing.T, chanType channeldb.ChannelType, test testCase) { successTx.TxIn[0].Witness[3] = preimage[:] storeTx(r.HtlcPoint().Index, successTx) } - for _, r := range forceCloseSum.HtlcResolutions.OutgoingHTLCs { + for _, r := range htlcResolutions.OutgoingHTLCs { storeTx(r.HtlcPoint().Index, r.SignedTimeoutTx) } From d6953833861af1b1c3628ad5acb53faa9917aae6 Mon Sep 17 00:00:00 2001 From: ziggie Date: Wed, 13 Nov 2024 17:22:30 +0100 Subject: [PATCH 215/218] rpcserver: add robustness check --- rpcserver.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/rpcserver.go b/rpcserver.go index d9fdb82bb2..8ed33d95af 100644 --- a/rpcserver.go +++ b/rpcserver.go @@ -2736,6 +2736,14 @@ func (r *rpcServer) CloseChannel(in *lnrpc.CloseChannelRequest, return err } + // Safety check which should never happen. + // + // TODO(ziggie): remove pointer as return value from + // ForceCloseContract. + if closingTx == nil { + return fmt.Errorf("force close transaction is nil") + } + closingTxid := closingTx.TxHash() // With the transaction broadcast, we send our first update to From a4211251bd641078b03d9ea3f2c4c45f3550a88f Mon Sep 17 00:00:00 2001 From: ziggie Date: Tue, 12 Nov 2024 22:40:11 +0100 Subject: [PATCH 216/218] docs: add release notes --- docs/release-notes/release-notes-0.18.4.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/release-notes/release-notes-0.18.4.md b/docs/release-notes/release-notes-0.18.4.md index 5d052cd5fc..c3b1ecdb7b 100644 --- a/docs/release-notes/release-notes-0.18.4.md +++ b/docs/release-notes/release-notes-0.18.4.md @@ -23,6 +23,9 @@ cause a nil pointer dereference during the probing of a payment request that does not contain a payment address. +* [Make the contract resolutions for the channel arbitrator optional]( + https://github.com/lightningnetwork/lnd/pull/9253). + # New Features The main channel state machine and database now allow for processing and storing From e074a8fab3acbb8d6a6802822799d31f6dcdf789 Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Wed, 20 Nov 2024 09:45:01 +0100 Subject: [PATCH 217/218] chanacceptor: add custom channel commitment type --- chanacceptor/rpcacceptor.go | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/chanacceptor/rpcacceptor.go b/chanacceptor/rpcacceptor.go index 779ba6f286..6ec7878954 100644 --- a/chanacceptor/rpcacceptor.go +++ b/chanacceptor/rpcacceptor.go @@ -356,6 +356,30 @@ func (r *RPCAcceptor) sendAcceptRequests(errChan chan error, ): commitmentType = lnrpc.CommitmentType_SIMPLE_TAPROOT + case channelFeatures.OnlyContains( + lnwire.SimpleTaprootOverlayChansRequired, + lnwire.ZeroConfRequired, + lnwire.ScidAliasRequired, + ): + commitmentType = lnrpc.CommitmentType_SIMPLE_TAPROOT_OVERLAY + + case channelFeatures.OnlyContains( + lnwire.SimpleTaprootOverlayChansRequired, + lnwire.ZeroConfRequired, + ): + commitmentType = lnrpc.CommitmentType_SIMPLE_TAPROOT_OVERLAY + + case channelFeatures.OnlyContains( + lnwire.SimpleTaprootOverlayChansRequired, + lnwire.ScidAliasRequired, + ): + commitmentType = lnrpc.CommitmentType_SIMPLE_TAPROOT_OVERLAY + + case channelFeatures.OnlyContains( + lnwire.SimpleTaprootOverlayChansRequired, + ): + commitmentType = lnrpc.CommitmentType_SIMPLE_TAPROOT_OVERLAY + case channelFeatures.OnlyContains( lnwire.StaticRemoteKeyRequired, ): From 117a145b9718eb2c71a5150f5f531493dfef882c Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Wed, 20 Nov 2024 10:37:17 +0100 Subject: [PATCH 218/218] docs: add release notes --- docs/release-notes/release-notes-0.18.4.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/release-notes/release-notes-0.18.4.md b/docs/release-notes/release-notes-0.18.4.md index c3b1ecdb7b..56270c0a77 100644 --- a/docs/release-notes/release-notes-0.18.4.md +++ b/docs/release-notes/release-notes-0.18.4.md @@ -38,6 +38,7 @@ types in a series of changes: * https://github.com/lightningnetwork/lnd/pull/9095 * https://github.com/lightningnetwork/lnd/pull/8960 * https://github.com/lightningnetwork/lnd/pull/9194 + * https://github.com/lightningnetwork/lnd/pull/9288 ## Functional Enhancements