From c2c0158c84936d2e96669b1dee476b7e5b9aa3b4 Mon Sep 17 00:00:00 2001 From: Carla Kirk-Cohen Date: Fri, 8 Mar 2024 13:33:10 -0500 Subject: [PATCH 01/17] htlcswitch: handle malformed HTLC with invalid onion blinding code This commit adds handling for malformed HTLC errors related to blinded paths. We expect to receive these errors _within_ a blinded path, because all non-introduction nodes are instructed to return malformed errors for failures. Note that we may actually switch back to a malformed error later on if we too are a relaying node in the route, but we handle that case the incoming link. --- htlcswitch/link.go | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/htlcswitch/link.go b/htlcswitch/link.go index 778e78d7008..fd87c2f2956 100644 --- a/htlcswitch/link.go +++ b/htlcswitch/link.go @@ -2030,6 +2030,19 @@ func (l *channelLink) handleUpstreamMsg(msg lnwire.Message) { failure = &lnwire.FailInvalidOnionKey{ OnionSHA256: msg.ShaOnionBlob, } + + // Handle malformed errors that are part of a blinded route. + // This case is slightly different, because we expect every + // relaying node in the blinded portion of the route to send + // malformed errors. If we're also a relaying node, we're + // likely going to switch this error out anyway for our own + // malformed error, but we handle the case here for + // completeness. + case lnwire.CodeInvalidBlinding: + failure = &lnwire.FailInvalidBlinding{ + OnionSHA256: msg.ShaOnionBlob, + } + default: l.log.Warnf("unexpected failure code received in "+ "UpdateFailMailformedHTLC: %v", msg.FailureCode) From 4d051b4170da75e64747a06a5aab0dacb0b14b4e Mon Sep 17 00:00:00 2001 From: Carla Kirk-Cohen Date: Mon, 22 Apr 2024 14:06:17 -0400 Subject: [PATCH 02/17] multi: handle all blinding point validation in ValidateParsedPayloadTypes This commit moves all our validation related to the presence of fields into ValidateParsedPayloadTypes so that we can handle them in a single place. We draw the distinction between: - Validation of the payload (and the context within it's being parsed, final hop / blinded hop etc) - Processing and validation of encrypted data, where we perform additional cryptographic operations and validate that the fields contained in the blob are valid. This helps draw the line more clearly between the two validation types, rather than splitting some payload-releated blinded hop processing into the encrypted data processing part. The downside of this approach (vs doing the blinded path payload check _after_ payload validation) is that we have to pass additional context into payload validation (ie, whether we got a blinding point in our UpdateAddHtlc - as we already do for isFinalHop). --- htlcswitch/hop/fuzz_test.go | 40 ++++++++++---- htlcswitch/hop/iterator.go | 50 +++++++----------- htlcswitch/hop/iterator_test.go | 31 +++++------ htlcswitch/hop/payload.go | 37 +++++++++++-- htlcswitch/hop/payload_test.go | 93 ++++++++++++++++++++++++++++----- 5 files changed, 175 insertions(+), 76 deletions(-) diff --git a/htlcswitch/hop/fuzz_test.go b/htlcswitch/hop/fuzz_test.go index cbbe882601f..f5fe648f58b 100644 --- a/htlcswitch/hop/fuzz_test.go +++ b/htlcswitch/hop/fuzz_test.go @@ -97,19 +97,37 @@ func hopFromPayload(p *Payload) (*route.Hop, uint64) { // FuzzPayloadFinal fuzzes final hop payloads, providing the additional context // that the hop should be final (which is usually obtained by the structure -// of the sphinx packet). -func FuzzPayloadFinal(f *testing.F) { - fuzzPayload(f, true) +// of the sphinx packet) for the case where a blinding point was provided in +// UpdateAddHtlc. +func FuzzPayloadFinalBlinding(f *testing.F) { + fuzzPayload(f, true, true) +} + +// FuzzPayloadFinal fuzzes final hop payloads, providing the additional context +// that the hop should be final (which is usually obtained by the structure +// of the sphinx packet) for the case where no blinding point was provided in +// UpdateAddHtlc. +func FuzzPayloadFinalNoBlinding(f *testing.F) { + fuzzPayload(f, true, false) +} + +// FuzzPayloadIntermediate fuzzes intermediate hop payloads, providing the +// additional context that a hop should be intermediate (which is usually +// obtained by the structure of the sphinx packet) for the case where a +// blinding point was provided in UpdateAddHtlc. +func FuzzPayloadIntermediateBlinding(f *testing.F) { + fuzzPayload(f, false, true) } // FuzzPayloadIntermediate fuzzes intermediate hop payloads, providing the // additional context that a hop should be intermediate (which is usually -// obtained by the structure of the sphinx packet). -func FuzzPayloadIntermediate(f *testing.F) { - fuzzPayload(f, false) +// obtained by the structure of the sphinx packet) for the case where no +// blinding point was provided in UpdateAddHtlc. +func FuzzPayloadIntermediateNoBlinding(f *testing.F) { + fuzzPayload(f, false, false) } -func fuzzPayload(f *testing.F, finalPayload bool) { +func fuzzPayload(f *testing.F, finalPayload, updateAddBlinded bool) { f.Fuzz(func(t *testing.T, data []byte) { if len(data) > sphinx.MaxPayloadSize { return @@ -117,7 +135,9 @@ func fuzzPayload(f *testing.F, finalPayload bool) { r := bytes.NewReader(data) - payload1, _, err := NewPayloadFromReader(r, finalPayload) + payload1, _, err := NewPayloadFromReader( + r, finalPayload, updateAddBlinded, + ) if err != nil { return } @@ -146,7 +166,9 @@ func fuzzPayload(f *testing.F, finalPayload bool) { require.NoError(t, err) } - payload2, _, err := NewPayloadFromReader(&b, finalPayload) + payload2, _, err := NewPayloadFromReader( + &b, finalPayload, updateAddBlinded, + ) require.NoError(t, err) require.Equal(t, payload1, payload2) diff --git a/htlcswitch/hop/iterator.go b/htlcswitch/hop/iterator.go index df6f5aac727..7088127c3f3 100644 --- a/htlcswitch/hop/iterator.go +++ b/htlcswitch/hop/iterator.go @@ -17,6 +17,11 @@ import ( var ( // ErrDecodeFailed is returned when we can't decode blinded data. ErrDecodeFailed = errors.New("could not decode blinded data") + + // ErrNoBlindingPoint is returned when we have not provided a blinding + // point for a validated payload with encrypted data set. + ErrNoBlindingPoint = errors.New("no blinding point set for validated " + + "blinded hop") ) // Iterator is an interface that abstracts away the routing information @@ -109,7 +114,7 @@ func (r *sphinxHopIterator) HopPayload() (*Payload, error) { isFinal := r.processedPacket.Action == sphinx.ExitNode payload, parsed, err := NewPayloadFromReader( bytes.NewReader(r.processedPacket.Payload.Payload), - isFinal, + isFinal, r.blindingKit.UpdateAddBlinding.IsSome(), ) if err != nil { return nil, err @@ -182,35 +187,16 @@ type BlindingKit struct { IncomingAmount lnwire.MilliSatoshi } -// validateBlindingPoint validates that only one blinding point is present for -// the hop and returns the relevant one. -func (b *BlindingKit) validateBlindingPoint(payloadBlinding *btcec.PublicKey, - isFinalHop bool) (*btcec.PublicKey, error) { - - // Bolt 04: if encrypted_recipient_data is present: - // - if blinding_point (in update add) is set: - // - MUST error if current_blinding_point is set (in payload) - // - otherwise: - // - MUST return an error if current_blinding_point is not present - // (in payload) +// getBlindingPoint returns either the payload or updateAddHtlc blinding point, +// assuming that validation that these values are appropriately set has already +// been handled elsewhere. +func (b *BlindingKit) getBlindingPoint(payloadBlinding *btcec.PublicKey) ( + *btcec.PublicKey, error) { + payloadBlindingSet := payloadBlinding != nil updateBlindingSet := b.UpdateAddBlinding.IsSome() switch { - case !(payloadBlindingSet || updateBlindingSet): - return nil, ErrInvalidPayload{ - Type: record.BlindingPointOnionType, - Violation: OmittedViolation, - FinalHop: isFinalHop, - } - - case payloadBlindingSet && updateBlindingSet: - return nil, ErrInvalidPayload{ - Type: record.BlindingPointOnionType, - Violation: IncludedViolation, - FinalHop: isFinalHop, - } - case payloadBlindingSet: return payloadBlinding, nil @@ -223,9 +209,10 @@ func (b *BlindingKit) validateBlindingPoint(payloadBlinding *btcec.PublicKey, } return pk.Val, nil - } - return nil, fmt.Errorf("expected blinded point set") + default: + return nil, ErrNoBlindingPoint + } } // DecryptAndValidateFwdInfo performs all operations required to decrypt and @@ -235,11 +222,10 @@ func (b *BlindingKit) DecryptAndValidateFwdInfo(payload *Payload, *ForwardingInfo, error) { // We expect this function to be called when we have encrypted data - // present, and a blinding key is set either in the payload or the + // present, and expect validation to already have ensured that a + // blinding key is set either in the payload or the // update_add_htlc message. - blindingPoint, err := b.validateBlindingPoint( - payload.blindingPoint, isFinalHop, - ) + blindingPoint, err := b.getBlindingPoint(payload.blindingPoint) if err != nil { return nil, err } diff --git a/htlcswitch/hop/iterator_test.go b/htlcswitch/hop/iterator_test.go index 60919333b37..a850c6dc1bc 100644 --- a/htlcswitch/hop/iterator_test.go +++ b/htlcswitch/hop/iterator_test.go @@ -216,24 +216,10 @@ func TestDecryptAndValidateFwdInfo(t *testing.T) { expectedErr error }{ { - name: "no blinding point", - data: validData, - processor: &mockProcessor{}, - expectedErr: ErrInvalidPayload{ - Type: record.BlindingPointOnionType, - Violation: OmittedViolation, - }, - }, - { - name: "both blinding points", - data: validData, - updateAddBlinding: &btcec.PublicKey{}, - payloadBlinding: &btcec.PublicKey{}, - processor: &mockProcessor{}, - expectedErr: ErrInvalidPayload{ - Type: record.BlindingPointOnionType, - Violation: IncludedViolation, - }, + name: "no blinding point", + data: validData, + processor: &mockProcessor{}, + expectedErr: ErrNoBlindingPoint, }, { name: "decryption failed", @@ -265,12 +251,19 @@ func TestDecryptAndValidateFwdInfo(t *testing.T) { }, }, { - name: "valid", + name: "valid using update add", updateAddBlinding: &btcec.PublicKey{}, data: validData, processor: &mockProcessor{}, expectedErr: nil, }, + { + name: "valid using payload", + payloadBlinding: &btcec.PublicKey{}, + data: validData, + processor: &mockProcessor{}, + expectedErr: nil, + }, } for _, testCase := range tests { diff --git a/htlcswitch/hop/payload.go b/htlcswitch/hop/payload.go index 70fdb1403fc..8454bc847cf 100644 --- a/htlcswitch/hop/payload.go +++ b/htlcswitch/hop/payload.go @@ -138,8 +138,8 @@ func NewLegacyPayload(f *sphinx.HopData) *Payload { // should correspond to the bytes encapsulated in a TLV onion payload. The // final hop bool signals that this payload was the final packet parsed by // sphinx. -func NewPayloadFromReader(r io.Reader, finalHop bool) (*Payload, - map[tlv.Type][]byte, error) { +func NewPayloadFromReader(r io.Reader, finalHop, + updateAddBlinding bool) (*Payload, map[tlv.Type][]byte, error) { var ( cid uint64 @@ -177,7 +177,9 @@ func NewPayloadFromReader(r io.Reader, finalHop bool) (*Payload, // Validate whether the sender properly included or omitted tlv records // in accordance with BOLT 04. - err = ValidateParsedPayloadTypes(parsedTypes, finalHop) + err = ValidateParsedPayloadTypes( + parsedTypes, finalHop, updateAddBlinding, + ) if err != nil { return nil, nil, err } @@ -259,7 +261,7 @@ func NewCustomRecords(parsedTypes tlv.TypeMap) record.CustomSet { // boolean should be true if the payload was parsed for an exit hop. The // requirements for this method are described in BOLT 04. func ValidateParsedPayloadTypes(parsedTypes tlv.TypeMap, - isFinalHop bool) error { + isFinalHop, updateAddBlinding bool) error { _, hasAmt := parsedTypes[record.AmtOnionType] _, hasLockTime := parsedTypes[record.LockTimeOnionType] @@ -267,6 +269,7 @@ func ValidateParsedPayloadTypes(parsedTypes tlv.TypeMap, _, hasMPP := parsedTypes[record.MPPOnionType] _, hasAMP := parsedTypes[record.AMPOnionType] _, hasEncryptedData := parsedTypes[record.EncryptedDataOnionType] + _, hasBlinding := parsedTypes[record.BlindingPointOnionType] // All cleartext hops (including final hop) and the final hop in a // blinded path require the forwading amount and expiry TLVs to be set. @@ -277,6 +280,32 @@ func ValidateParsedPayloadTypes(parsedTypes tlv.TypeMap, needNextHop := !(hasEncryptedData || isFinalHop) switch { + // Both blinding point being set is invalid. + case hasBlinding && updateAddBlinding: + return ErrInvalidPayload{ + Type: record.BlindingPointOnionType, + Violation: IncludedViolation, + FinalHop: isFinalHop, + } + + // If encrypted data is not provided, blinding points should not be + // set. + case !hasEncryptedData && (hasBlinding || updateAddBlinding): + return ErrInvalidPayload{ + Type: record.EncryptedDataOnionType, + Violation: OmittedViolation, + FinalHop: isFinalHop, + } + + // If encrypted data is present, we require that one blinding point + // is set. + case hasEncryptedData && !(hasBlinding || updateAddBlinding): + return ErrInvalidPayload{ + Type: record.EncryptedDataOnionType, + Violation: IncludedViolation, + FinalHop: isFinalHop, + } + // Hops that need forwarding info must include an amount to forward. case needFwdInfo && !hasAmt: return ErrInvalidPayload{ diff --git a/htlcswitch/hop/payload_test.go b/htlcswitch/hop/payload_test.go index 301e5771660..666cf64bc89 100644 --- a/htlcswitch/hop/payload_test.go +++ b/htlcswitch/hop/payload_test.go @@ -26,6 +26,7 @@ type decodePayloadTest struct { name string payload []byte isFinalHop bool + updateAddBlinded bool expErr error expCustomRecords map[uint64][]byte shouldHaveMPP bool @@ -271,8 +272,9 @@ var decodePayloadTests = []decodePayloadTest{ }, }, { - name: "intermediate hop with encrypted data", - isFinalHop: false, + name: "intermediate hop with encrypted data", + isFinalHop: false, + updateAddBlinded: true, payload: []byte{ // encrypted data 0x0a, 0x03, 0x03, 0x02, 0x01, @@ -365,8 +367,9 @@ var decodePayloadTests = []decodePayloadTest{ shouldHaveTotalAmt: true, }, { - name: "final blinded hop with total amount", - isFinalHop: true, + name: "final blinded hop with total amount", + isFinalHop: true, + updateAddBlinded: true, payload: []byte{ // amount 0x02, 0x00, @@ -378,8 +381,9 @@ var decodePayloadTests = []decodePayloadTest{ shouldHaveEncData: true, }, { - name: "final blinded missing amt", - isFinalHop: true, + name: "final blinded missing amt", + isFinalHop: true, + updateAddBlinded: true, payload: []byte{ // cltv 0x04, 0x00, @@ -394,8 +398,9 @@ var decodePayloadTests = []decodePayloadTest{ }, }, { - name: "final blinded missing cltv", - isFinalHop: true, + name: "final blinded missing cltv", + isFinalHop: true, + updateAddBlinded: true, payload: []byte{ // amount 0x02, 0x00, @@ -410,8 +415,9 @@ var decodePayloadTests = []decodePayloadTest{ }, }, { - name: "intermediate blinded has amount", - isFinalHop: false, + name: "intermediate blinded has amount", + isFinalHop: false, + updateAddBlinded: true, payload: []byte{ // amount 0x02, 0x00, @@ -425,8 +431,9 @@ var decodePayloadTests = []decodePayloadTest{ }, }, { - name: "intermediate blinded has expiry", - isFinalHop: false, + name: "intermediate blinded has expiry", + isFinalHop: false, + updateAddBlinded: true, payload: []byte{ // cltv 0x04, 0x00, @@ -439,6 +446,67 @@ var decodePayloadTests = []decodePayloadTest{ FinalHop: false, }, }, + { + name: "update add blinding no data", + isFinalHop: false, + payload: []byte{ + // cltv + 0x04, 0x00, + }, + updateAddBlinded: true, + expErr: hop.ErrInvalidPayload{ + Type: record.EncryptedDataOnionType, + Violation: hop.OmittedViolation, + FinalHop: false, + }, + }, + { + name: "onion blinding point no data", + isFinalHop: false, + payload: append([]byte{ + // blinding point (type / length) + 0x0c, 0x21, + }, + // blinding point (value) + testPubKey.SerializeCompressed()..., + ), + expErr: hop.ErrInvalidPayload{ + Type: record.EncryptedDataOnionType, + Violation: hop.OmittedViolation, + FinalHop: false, + }, + }, + { + name: "encrypted data no blinding", + isFinalHop: false, + payload: []byte{ + // encrypted data + 0x0a, 0x03, 0x03, 0x02, 0x01, + }, + expErr: hop.ErrInvalidPayload{ + Type: record.EncryptedDataOnionType, + Violation: hop.IncludedViolation, + }, + }, + { + name: "both blinding points", + isFinalHop: false, + updateAddBlinded: true, + payload: append([]byte{ + // encrypted data + 0x0a, 0x03, 0x03, 0x02, 0x01, + // blinding point (type / length) + 0x0c, 0x21, + }, + // blinding point (value) + testPubKey.SerializeCompressed()..., + ), + expErr: hop.ErrInvalidPayload{ + Type: record.BlindingPointOnionType, + Violation: hop.IncludedViolation, + FinalHop: false, + }, + }, } // TestDecodeHopPayloadRecordValidation asserts that parsing the payloads in the @@ -481,6 +549,7 @@ func testDecodeHopPayloadValidation(t *testing.T, test decodePayloadTest) { p, _, err := hop.NewPayloadFromReader( bytes.NewReader(test.payload), test.isFinalHop, + test.updateAddBlinded, ) if !reflect.DeepEqual(test.expErr, err) { t.Fatalf("expected error mismatch, want: %v, got: %v", From b81a6f3d2f16b2d5c3cd8dd8cc0204c21cf6a748 Mon Sep 17 00:00:00 2001 From: Carla Kirk-Cohen Date: Tue, 23 Apr 2024 11:27:14 -0400 Subject: [PATCH 03/17] htlcswitch: split parsing and validation of TLV payloads When handling blinded errors, we need to know whether there was a blinding key in our payload when we successfully parsed our payload but then found an invalid set of fields. The combination of parsing and validation in NewPayloadFromReader means that we don't know whether a blinding point was available to us by the time the error is returned. This commit splits parsing and validation into two functions so that we can take a look at what we actually pulled of the payload in between parsing and TLV validation. --- htlcswitch/hop/fuzz_test.go | 25 +++++++++----- htlcswitch/hop/iterator.go | 10 ++++-- htlcswitch/hop/payload.go | 60 ++++++++++++++++++---------------- htlcswitch/hop/payload_test.go | 10 ++++-- 4 files changed, 63 insertions(+), 42 deletions(-) diff --git a/htlcswitch/hop/fuzz_test.go b/htlcswitch/hop/fuzz_test.go index f5fe648f58b..e5c00b52571 100644 --- a/htlcswitch/hop/fuzz_test.go +++ b/htlcswitch/hop/fuzz_test.go @@ -135,19 +135,23 @@ func fuzzPayload(f *testing.F, finalPayload, updateAddBlinded bool) { r := bytes.NewReader(data) - payload1, _, err := NewPayloadFromReader( - r, finalPayload, updateAddBlinded, - ) + payload1, parsed, err := ParseTLVPayload(r) if err != nil { return } + if err = ValidateParsedPayloadTypes( + parsed, finalPayload, updateAddBlinded, + ); err != nil { + return + } + var b bytes.Buffer hop, nextChanID := hopFromPayload(payload1) err = hop.PackHopPayload(&b, nextChanID, finalPayload) switch { // PackHopPayload refuses to encode an AMP record - // without an MPP record. However, NewPayloadFromReader + // without an MPP record. However, ValidateParsedPayloadTypes // does allow decoding an AMP record without an MPP // record, since validation is done at a later stage. Do // not report a bug for this case. @@ -156,9 +160,9 @@ func fuzzPayload(f *testing.F, finalPayload, updateAddBlinded bool) { // PackHopPayload will not encode regular payloads or final // hops in blinded routes that do not have an amount or expiry - // TLV set. However, NewPayloadFromReader will allow creation - // of payloads where these TLVs are present, but they have - // zero values because validation is done at a later stage. + // TLV set. However, ValidateParsedPayloadTypes will allow + // creation of payloads where these TLVs are present, but they + // have zero values because validation is done at a later stage. case errors.Is(err, route.ErrMissingField): return @@ -166,8 +170,11 @@ func fuzzPayload(f *testing.F, finalPayload, updateAddBlinded bool) { require.NoError(t, err) } - payload2, _, err := NewPayloadFromReader( - &b, finalPayload, updateAddBlinded, + payload2, parsed, err := ParseTLVPayload(&b) + require.NoError(t, err) + + err = ValidateParsedPayloadTypes( + parsed, finalPayload, updateAddBlinded, ) require.NoError(t, err) diff --git a/htlcswitch/hop/iterator.go b/htlcswitch/hop/iterator.go index 7088127c3f3..7ba0e9dd8f1 100644 --- a/htlcswitch/hop/iterator.go +++ b/htlcswitch/hop/iterator.go @@ -112,14 +112,20 @@ func (r *sphinxHopIterator) HopPayload() (*Payload, error) { // to decode only what we need to make routing decisions. case sphinx.PayloadTLV: isFinal := r.processedPacket.Action == sphinx.ExitNode - payload, parsed, err := NewPayloadFromReader( + payload, parsed, err := ParseTLVPayload( bytes.NewReader(r.processedPacket.Payload.Payload), - isFinal, r.blindingKit.UpdateAddBlinding.IsSome(), ) if err != nil { return nil, err } + if err := ValidateTLVPayload( + parsed, isFinal, + r.blindingKit.UpdateAddBlinding.IsSome(), + ); err != nil { + return nil, err + } + // If we had an encrypted data payload present, pull out our // forwarding info from the blob. if payload.encryptedData != nil { diff --git a/htlcswitch/hop/payload.go b/htlcswitch/hop/payload.go index 8454bc847cf..9e717bbd26b 100644 --- a/htlcswitch/hop/payload.go +++ b/htlcswitch/hop/payload.go @@ -133,14 +133,10 @@ func NewLegacyPayload(f *sphinx.HopData) *Payload { } } -// NewPayloadFromReader builds a new Hop from the passed io.Reader and returns -// a map of all the types that were found in the payload. The reader -// should correspond to the bytes encapsulated in a TLV onion payload. The -// final hop bool signals that this payload was the final packet parsed by -// sphinx. -func NewPayloadFromReader(r io.Reader, finalHop, - updateAddBlinding bool) (*Payload, map[tlv.Type][]byte, error) { - +// ParseTLVPayload builds a new Hop from the passed io.Reader and returns +// a map of all the types that were found in the payload. This function +// does not perform validation of TLV types included in the payload. +func ParseTLVPayload(r io.Reader) (*Payload, map[tlv.Type][]byte, error) { var ( cid uint64 amt uint64 @@ -175,25 +171,6 @@ func NewPayloadFromReader(r io.Reader, finalHop, return nil, nil, err } - // Validate whether the sender properly included or omitted tlv records - // in accordance with BOLT 04. - err = ValidateParsedPayloadTypes( - parsedTypes, finalHop, updateAddBlinding, - ) - if err != nil { - return nil, nil, err - } - - // Check for violation of the rules for mandatory fields. - violatingType := getMinRequiredViolation(parsedTypes) - if violatingType != nil { - return nil, nil, ErrInvalidPayload{ - Type: *violatingType, - Violation: RequiredViolation, - FinalHop: finalHop, - } - } - // If no MPP field was parsed, set the MPP field on the resulting // payload to nil. if _, ok := parsedTypes[record.MPPOnionType]; !ok { @@ -234,7 +211,34 @@ func NewPayloadFromReader(r io.Reader, finalHop, blindingPoint: blindingPoint, customRecords: customRecords, totalAmtMsat: lnwire.MilliSatoshi(totalAmtMsat), - }, nil, nil + }, parsedTypes, nil +} + +// ValidateTLVPayload validates the TLV fields that were included in a TLV +// payload. +func ValidateTLVPayload(parsedTypes map[tlv.Type][]byte, + finalHop bool, updateAddBlinding bool) error { + + // Validate whether the sender properly included or omitted tlv records + // in accordance with BOLT 04. + err := ValidateParsedPayloadTypes( + parsedTypes, finalHop, updateAddBlinding, + ) + if err != nil { + return err + } + + // Check for violation of the rules for mandatory fields. + violatingType := getMinRequiredViolation(parsedTypes) + if violatingType != nil { + return ErrInvalidPayload{ + Type: *violatingType, + Violation: RequiredViolation, + FinalHop: finalHop, + } + } + + return nil } // ForwardingInfo returns the basic parameters required for HTLC forwarding, diff --git a/htlcswitch/hop/payload_test.go b/htlcswitch/hop/payload_test.go index 666cf64bc89..c22144d7b86 100644 --- a/htlcswitch/hop/payload_test.go +++ b/htlcswitch/hop/payload_test.go @@ -547,9 +547,13 @@ func testDecodeHopPayloadValidation(t *testing.T, test decodePayloadTest) { testChildIndex = uint32(9) ) - p, _, err := hop.NewPayloadFromReader( - bytes.NewReader(test.payload), test.isFinalHop, - test.updateAddBlinded, + p, parsedTypes, err := hop.ParseTLVPayload( + bytes.NewReader(test.payload), + ) + require.NoError(t, err) + + err = hop.ValidateTLVPayload( + parsedTypes, test.isFinalHop, test.updateAddBlinded, ) if !reflect.DeepEqual(test.expErr, err) { t.Fatalf("expected error mismatch, want: %v, got: %v", From 776c8892677d4b32d99ba62bed56f962f84d15e7 Mon Sep 17 00:00:00 2001 From: Carla Kirk-Cohen Date: Thu, 25 Apr 2024 09:46:31 -0400 Subject: [PATCH 04/17] multi: return route role from HopPayload We need to know what role we're playing to be able to handle errors correctly, but the information that we need for this is held by our iterator: - Whether we had a blinding point in update add (blinding kit) - Whether we had a blinding point in payload As we're now going to use the route role return value even when our err!=nil, we rename the error to signal that we're using less canonical golang here. An alternative to this approach is to attach a RouteRole to our ErrInvalidPayload. The downside of that approach is: - Propagate context through parsing (whether we had updateAddHtlc) - Clumsy handling for errors that are not of type ErrInvalidPayload --- .../htlc_incoming_contest_resolver.go | 2 +- .../htlc_incoming_contest_resolver_test.go | 4 +- htlcswitch/hop/iterator.go | 105 ++++++++++++++++-- htlcswitch/hop/iterator_test.go | 6 +- htlcswitch/link.go | 13 ++- htlcswitch/mock.go | 4 +- 6 files changed, 110 insertions(+), 24 deletions(-) diff --git a/contractcourt/htlc_incoming_contest_resolver.go b/contractcourt/htlc_incoming_contest_resolver.go index e73e3e45b2f..b104f8d70a6 100644 --- a/contractcourt/htlc_incoming_contest_resolver.go +++ b/contractcourt/htlc_incoming_contest_resolver.go @@ -543,7 +543,7 @@ func (h *htlcIncomingContestResolver) decodePayload() (*hop.Payload, return nil, nil, err } - payload, err := iterator.HopPayload() + payload, _, err := iterator.HopPayload() if err != nil { return nil, nil, err } diff --git a/contractcourt/htlc_incoming_contest_resolver_test.go b/contractcourt/htlc_incoming_contest_resolver_test.go index 6c4ba6bc480..55d93a6fb37 100644 --- a/contractcourt/htlc_incoming_contest_resolver_test.go +++ b/contractcourt/htlc_incoming_contest_resolver_test.go @@ -263,7 +263,7 @@ type mockHopIterator struct { hop.Iterator } -func (h *mockHopIterator) HopPayload() (*hop.Payload, error) { +func (h *mockHopIterator) HopPayload() (*hop.Payload, hop.RouteRole, error) { var nextAddress [8]byte if !h.isExit { nextAddress = [8]byte{0x01} @@ -275,7 +275,7 @@ func (h *mockHopIterator) HopPayload() (*hop.Payload, error) { ForwardAmount: 100, OutgoingCltv: 40, ExtraBytes: [12]byte{}, - }), nil + }), hop.RouteRoleCleartext, nil } func (h *mockHopIterator) EncodeNextHop(w io.Writer) error { diff --git a/htlcswitch/hop/iterator.go b/htlcswitch/hop/iterator.go index 7ba0e9dd8f1..c387bae3abc 100644 --- a/htlcswitch/hop/iterator.go +++ b/htlcswitch/hop/iterator.go @@ -24,6 +24,62 @@ var ( "blinded hop") ) +// RouteRole represents the different types of roles a node can have as a +// recipient of a HTLC. +type RouteRole uint8 + +const ( + // RouteRoleCleartext represents a regular route hop. + RouteRoleCleartext RouteRole = iota + + // RouteRoleIntroduction represents an introduction node in a blinded + // path, characterized by a blinding point in the onion payload. + RouteRoleIntroduction + + // RouteRoleRelaying represents a relaying node in a blinded path, + // characterized by a blinding point in update_add_htlc. + RouteRoleRelaying +) + +// String representation of a role in a route. +func (h RouteRole) String() string { + switch h { + case RouteRoleCleartext: + return "cleartext" + + case RouteRoleRelaying: + return "blinded relay" + + case RouteRoleIntroduction: + return "introduction node" + + default: + return fmt.Sprintf("unknown route role: %d", h) + } +} + +// NewRouteRole returns the role we're playing in a route depending on the +// blinding points set (or not). If we are in the situation where we received +// blinding points in both the update add message and the payload: +// - We must have had a valid update add blinding point, because we were able +// to decrypt our onion to get the payload blinding point. +// - We return a relaying node role, because an introduction node (by +// definition) does not receive a blinding point in update add. +// - We assume the sending node to be buggy (including a payload blinding +// where it shouldn't), and rely on validation elsewhere to handle this. +func NewRouteRole(updateAddBlinding, payloadBlinding bool) RouteRole { + switch { + case updateAddBlinding: + return RouteRoleRelaying + + case payloadBlinding: + return RouteRoleIntroduction + + default: + return RouteRoleCleartext + } +} + // Iterator is an interface that abstracts away the routing information // included in HTLC's which includes the entirety of the payment path of an // HTLC. This interface provides two basic method which carry out: how to @@ -35,8 +91,11 @@ type Iterator interface { // information encoded within the returned ForwardingInfo is to be used // by each hop to authenticate the information given to it by the prior // hop. The payload will also contain any additional TLV fields provided - // by the sender. - HopPayload() (*Payload, error) + // by the sender. The role that this hop plays in the context of + // route blinding (regular, introduction or relaying) is returned + // whenever the payload is successfully parsed, even if we subsequently + // face a validation error. + HopPayload() (*Payload, RouteRole, error) // EncodeNextHop encodes the onion packet destined for the next hop // into the passed io.Writer. @@ -95,18 +154,21 @@ func (r *sphinxHopIterator) EncodeNextHop(w io.Writer) error { // HopPayload returns the set of fields that detail exactly _how_ this hop // should forward the HTLC to the next hop. Additionally, the information // encoded within the returned ForwardingInfo is to be used by each hop to -// authenticate the information given to it by the prior hop. The payload will -// also contain any additional TLV fields provided by the sender. +// authenticate the information given to it by the prior hop. The role that +// this hop plays in the context of route blinding (regular, introduction or +// relaying) is returned whenever the payload is successfully parsed, even if +// we subsequently face a validation error. The payload will also contain any +// additional TLV fields provided by the sender. // // NOTE: Part of the HopIterator interface. -func (r *sphinxHopIterator) HopPayload() (*Payload, error) { +func (r *sphinxHopIterator) HopPayload() (*Payload, RouteRole, error) { switch r.processedPacket.Payload.Type { // If this is the legacy payload, then we'll extract the information // directly from the pre-populated ForwardingInstructions field. case sphinx.PayloadLegacy: fwdInst := r.processedPacket.ForwardingInstructions - return NewLegacyPayload(fwdInst), nil + return NewLegacyPayload(fwdInst), RouteRoleCleartext, nil // Otherwise, if this is the TLV payload, then we'll make a new stream // to decode only what we need to make routing decisions. @@ -116,14 +178,32 @@ func (r *sphinxHopIterator) HopPayload() (*Payload, error) { bytes.NewReader(r.processedPacket.Payload.Payload), ) if err != nil { - return nil, err + // If we couldn't even parse our payload then we do + // a best-effort of determining our role in a blinded + // route, accepting that we can't know whether we + // were the introduction node (as the payload + // is not parseable). + routeRole := RouteRoleCleartext + if r.blindingKit.UpdateAddBlinding.IsSome() { + routeRole = RouteRoleRelaying + } + + return nil, routeRole, err } + // Now that we've parsed our payload we can determine which + // role we're playing in the route. + _, payloadBlinding := parsed[record.BlindingPointOnionType] + routeRole := NewRouteRole( + r.blindingKit.UpdateAddBlinding.IsSome(), + payloadBlinding, + ) + if err := ValidateTLVPayload( parsed, isFinal, r.blindingKit.UpdateAddBlinding.IsSome(), ); err != nil { - return nil, err + return nil, routeRole, err } // If we had an encrypted data payload present, pull out our @@ -133,17 +213,18 @@ func (r *sphinxHopIterator) HopPayload() (*Payload, error) { payload, isFinal, parsed, ) if err != nil { - return nil, err + return nil, routeRole, err } payload.FwdInfo = *fwdInfo } - return payload, err + return payload, routeRole, nil default: - return nil, fmt.Errorf("unknown sphinx payload type: %v", - r.processedPacket.Payload.Type) + return nil, RouteRoleCleartext, + fmt.Errorf("unknown sphinx payload type: %v", + r.processedPacket.Payload.Type) } } diff --git a/htlcswitch/hop/iterator_test.go b/htlcswitch/hop/iterator_test.go index a850c6dc1bc..e69361b0a4b 100644 --- a/htlcswitch/hop/iterator_test.go +++ b/htlcswitch/hop/iterator_test.go @@ -88,10 +88,10 @@ func TestSphinxHopIteratorForwardingInstructions(t *testing.T) { for i, testCase := range testCases { iterator.processedPacket = testCase.sphinxPacket - pld, err := iterator.HopPayload() - if err != nil { + pld, _, pldErr := iterator.HopPayload() + if pldErr != nil { t.Fatalf("#%v: unable to extract forwarding "+ - "instructions: %v", i, err) + "instructions: %v", i, pldErr) } fwdInfo := pld.ForwardingInfo() diff --git a/htlcswitch/link.go b/htlcswitch/link.go index fd87c2f2956..5f250636d9a 100644 --- a/htlcswitch/link.go +++ b/htlcswitch/link.go @@ -3293,14 +3293,18 @@ func (l *channelLink) processRemoteAdds(fwdPkg *channeldb.FwdPkg, heightNow := l.cfg.BestHeight() - pld, err := chanIterator.HopPayload() - if err != nil { + pld, _, pldErr := chanIterator.HopPayload() + if pldErr != nil { // If we're unable to process the onion payload, or we // received invalid onion payload failure, then we // should send an error back to the caller so the HTLC // can be canceled. var failedType uint64 - if e, ok := err.(hop.ErrInvalidPayload); ok { + + // We need to get the underlying error value, so we + // can't use errors.As as suggested by the linter. + //nolint:errorlint + if e, ok := pldErr.(hop.ErrInvalidPayload); ok { failedType = uint64(e.Type) } @@ -3316,7 +3320,8 @@ func (l *channelLink) processRemoteAdds(fwdPkg *channeldb.FwdPkg, ) l.log.Errorf("unable to decode forwarding "+ - "instructions: %v", err) + "instructions: %v", pldErr) + continue } diff --git a/htlcswitch/mock.go b/htlcswitch/mock.go index dfa834c5837..0dbefd45cf8 100644 --- a/htlcswitch/mock.go +++ b/htlcswitch/mock.go @@ -330,10 +330,10 @@ func newMockHopIterator(hops ...*hop.Payload) hop.Iterator { return &mockHopIterator{hops: hops} } -func (r *mockHopIterator) HopPayload() (*hop.Payload, error) { +func (r *mockHopIterator) HopPayload() (*hop.Payload, hop.RouteRole, error) { h := r.hops[0] r.hops = r.hops[1:] - return h, nil + return h, hop.RouteRoleCleartext, nil } func (r *mockHopIterator) ExtraOnionBlob() []byte { From 9f038c6191ee90a19fbf863a01a67d0ee5a3fd10 Mon Sep 17 00:00:00 2001 From: Carla Kirk-Cohen Date: Mon, 8 Apr 2024 10:22:56 -0400 Subject: [PATCH 05/17] htlcswitch: introduce wrapper type error encrypter to identify blinded Introduce two wrapper types for our existing SphinxErrorEncrypter that are used to represent error encrypters where we're a part of a blinded route. These encrypters are functionally the same as a sphinx encrypter, and are just used as "markers" so that we know that we need to handle our error differently due to our different role. We need to persist this information to account for restart cases where we've resovled the outgoing HTLC, then restart and need to handle the error for the incoming link. Specifically, this is relevant for: - On chain resolution messages received after restart - Forwarding packages that are re-forwarded after restart This is also generally helpful, because we can store this information in one place (the circuit) rather than trying to reconstruct it in various places when forwarding the failure back over the switch. --- htlcswitch/circuit.go | 6 +++ htlcswitch/hop/error_encryptor.go | 77 ++++++++++++++++++++++++++++++- 2 files changed, 82 insertions(+), 1 deletion(-) diff --git a/htlcswitch/circuit.go b/htlcswitch/circuit.go index efb2a477905..700b087f522 100644 --- a/htlcswitch/circuit.go +++ b/htlcswitch/circuit.go @@ -205,6 +205,12 @@ func (c *PaymentCircuit) Decode(r io.Reader) error { // Test encrypter. c.ErrorEncrypter = NewMockObfuscator() + case hop.EncrypterTypeIntroduction: + c.ErrorEncrypter = hop.NewIntroductionErrorEncrypter() + + case hop.EncrypterTypeRelaying: + c.ErrorEncrypter = hop.NewRelayingErrorEncrypter() + default: return UnknownEncrypterType(encrypterType) } diff --git a/htlcswitch/hop/error_encryptor.go b/htlcswitch/hop/error_encryptor.go index 7b6a3dd1a53..127370e4d49 100644 --- a/htlcswitch/hop/error_encryptor.go +++ b/htlcswitch/hop/error_encryptor.go @@ -25,6 +25,18 @@ const ( // EncrypterTypeMock is used to identify a mock obfuscator instance. EncrypterTypeMock = 2 + + // EncrypterTypeIntroduction is used to identify a sphinx onion error + // encrypter where we are the introduction node in a blinded route. It + // has the same functionality as EncrypterTypeSphinx, but is used to + // mark our special-case error handling. + EncrypterTypeIntroduction = 3 + + // EncrypterTypeRelaying is used to identify a sphinx onion error + // encryper where we are a relaying node in a blinded route. It has + // the same functionality as a EncrypterTypeSphinx, but is used to mark + // our special-case error handling. + EncrypterTypeRelaying = 4 ) // ErrorEncrypterExtracter defines a function signature that extracts an @@ -197,9 +209,72 @@ func (s *SphinxErrorEncrypter) Reextract( s.OnionErrorEncrypter = sphinxEncrypter.OnionErrorEncrypter return nil - } // A compile time check to ensure SphinxErrorEncrypter implements the // ErrorEncrypter interface. var _ ErrorEncrypter = (*SphinxErrorEncrypter)(nil) + +// A compile time check to ensure that IntroductionErrorEncrypter implements +// the ErrorEncrypter interface. +var _ ErrorEncrypter = (*IntroductionErrorEncrypter)(nil) + +// IntroductionErrorEncrypter is a wrapper type on SphinxErrorEncrypter which +// is used to signal that we have special HTLC error handling for this hop. +type IntroductionErrorEncrypter struct { + // ErrorEncrypter is the underlying error encrypter, embedded + // directly in the struct so that we don't have to re-implement the + // ErrorEncrypter interface. + ErrorEncrypter +} + +// NewIntroductionErrorEncrypter returns a blank IntroductionErrorEncrypter. +func NewIntroductionErrorEncrypter() *IntroductionErrorEncrypter { + return &IntroductionErrorEncrypter{ + ErrorEncrypter: NewSphinxErrorEncrypter(), + } +} + +// Type returns the identifier for an introduction error encrypter. +func (i *IntroductionErrorEncrypter) Type() EncrypterType { + return EncrypterTypeIntroduction +} + +// Reextract rederives the error encrypter from the currently held EphemeralKey, +// relying on the logic in the underlying SphinxErrorEncrypter. +func (i *IntroductionErrorEncrypter) Reextract( + extract ErrorEncrypterExtracter) error { + + return i.ErrorEncrypter.Reextract(extract) +} + +// A compile time check to ensure that RelayingErrorEncrypte implements +// the ErrorEncrypter interface. +var _ ErrorEncrypter = (*RelayingErrorEncrypter)(nil) + +// RelayingErrorEncrypter is a wrapper type on SphinxErrorEncrypter which +// is used to signal that we have special HTLC error handling for this hop. +type RelayingErrorEncrypter struct { + ErrorEncrypter +} + +// NewRelayingErrorEncrypter returns a blank RelayingErrorEncrypter with +// an underlying SphinxErrorEncrypter. +func NewRelayingErrorEncrypter() *RelayingErrorEncrypter { + return &RelayingErrorEncrypter{ + ErrorEncrypter: NewSphinxErrorEncrypter(), + } +} + +// Type returns the identifier for a relaying error encrypter. +func (r *RelayingErrorEncrypter) Type() EncrypterType { + return EncrypterTypeRelaying +} + +// Reextract rederives the error encrypter from the currently held EphemeralKey, +// relying on the logic in the underlying SphinxErrorEncrypter. +func (r *RelayingErrorEncrypter) Reextract( + extract ErrorEncrypterExtracter) error { + + return r.ErrorEncrypter.Reextract(extract) +} From 72260adddb30c4e9c18a14e37f6abf5bcd365712 Mon Sep 17 00:00:00 2001 From: Carla Kirk-Cohen Date: Tue, 23 Apr 2024 12:33:04 -0400 Subject: [PATCH 06/17] htlcswitch: create error obfuscator with wrapped type for blinded Create our error encrypter with a wrapped type if we have a blinding point present. Doing this in the iterator allows us to track this information when we have both pieces of information available to us, compared to trying to handle this later down the line: - Downstream link on failure: we know that we've set a blinding point for out outgoing HTLC, but not whether we're introduction or not - Upstream link on failure: once the failure packet has been sent through the switch, we no longer know whether we were the introduction point (without looking it up / examining our payload again / propagating this information through the switch). --- htlcswitch/hop/iterator.go | 30 +++++++++++++--- htlcswitch/link.go | 70 +++++++++++++++++++++++++++----------- htlcswitch/mock.go | 2 +- 3 files changed, 77 insertions(+), 25 deletions(-) diff --git a/htlcswitch/hop/iterator.go b/htlcswitch/hop/iterator.go index c387bae3abc..a89f1b7347c 100644 --- a/htlcswitch/hop/iterator.go +++ b/htlcswitch/hop/iterator.go @@ -103,8 +103,8 @@ type Iterator interface { // ExtractErrorEncrypter returns the ErrorEncrypter needed for this hop, // along with a failure code to signal if the decoding was successful. - ExtractErrorEncrypter(ErrorEncrypterExtracter) (ErrorEncrypter, - lnwire.FailCode) + ExtractErrorEncrypter(extractor ErrorEncrypterExtracter, + introductionNode bool) (ErrorEncrypter, lnwire.FailCode) } // sphinxHopIterator is the Sphinx implementation of hop iterator which uses @@ -235,9 +235,31 @@ func (r *sphinxHopIterator) HopPayload() (*Payload, RouteRole, error) { // // NOTE: Part of the HopIterator interface. func (r *sphinxHopIterator) ExtractErrorEncrypter( - extracter ErrorEncrypterExtracter) (ErrorEncrypter, lnwire.FailCode) { + extracter ErrorEncrypterExtracter, introductionNode bool) ( + ErrorEncrypter, lnwire.FailCode) { + + encrypter, errCode := extracter(r.ogPacket.EphemeralKey) + if errCode != lnwire.CodeNone { + return nil, errCode + } + + // If we're in a blinded path, wrap the error encrypter that we just + // derived in a "marker" type which we'll use to know what type of + // error we're handling. + switch { + case introductionNode: + return &IntroductionErrorEncrypter{ + ErrorEncrypter: encrypter, + }, errCode + + case r.blindingKit.UpdateAddBlinding.IsSome(): + return &RelayingErrorEncrypter{ + ErrorEncrypter: encrypter, + }, errCode - return extracter(r.ogPacket.EphemeralKey) + default: + return encrypter, errCode + } } // BlindingProcessor is an interface that provides the cryptographic operations diff --git a/htlcswitch/link.go b/htlcswitch/link.go index 5f250636d9a..b5cba787cff 100644 --- a/htlcswitch/link.go +++ b/htlcswitch/link.go @@ -3262,7 +3262,7 @@ func (l *channelLink) processRemoteAdds(fwdPkg *channeldb.FwdPkg, // DecodeHopIterator function which process the Sphinx packet. chanIterator, failureCode := decodeResps[i].Result() if failureCode != lnwire.CodeNone { - // If we're unable to process the onion blob than we + // 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, @@ -3273,27 +3273,9 @@ func (l *channelLink) processRemoteAdds(fwdPkg *channeldb.FwdPkg, continue } - // Retrieve onion obfuscator from onion blob in order to - // produce initial obfuscation of the onion failureCode. - obfuscator, failureCode := chanIterator.ExtractErrorEncrypter( - l.cfg.ExtractErrorEncrypter, - ) - if failureCode != lnwire.CodeNone { - // If we're unable to process the onion blob than we - // should send the malformed htlc error to payment - // sender. - l.sendMalformedHTLCError( - pd.HtlcIndex, failureCode, onionBlob[:], pd.SourceRef, - ) - - l.log.Errorf("unable to decode onion "+ - "obfuscator: %v", failureCode) - continue - } - heightNow := l.cfg.BestHeight() - pld, _, pldErr := chanIterator.HopPayload() + pld, routeRole, pldErr := chanIterator.HopPayload() if pldErr != nil { // If we're unable to process the onion payload, or we // received invalid onion payload failure, then we @@ -3308,6 +3290,33 @@ func (l *channelLink) processRemoteAdds(fwdPkg *channeldb.FwdPkg, failedType = uint64(e.Type) } + // If we couldn't parse the payload, make our best + // effort at creating an error encrypter that knows + // what blinding type we were, but if we couldn't + // parse the payload we have no way of knowing whether + // we were the introduction node or not. + // + //nolint:lll + obfuscator, failCode := chanIterator.ExtractErrorEncrypter( + l.cfg.ExtractErrorEncrypter, + // We need our route role here because we + // couldn't parse or validate the payload. + routeRole == hop.RouteRoleIntroduction, + ) + if failCode != lnwire.CodeNone { + l.log.Errorf("could not extract error "+ + "encrypter: %v", pldErr) + + // We can't process this htlc, send back + // malformed. + l.sendMalformedHTLCError( + pd.HtlcIndex, failureCode, + onionBlob[:], pd.SourceRef, + ) + + continue + } + // TODO: currently none of the test unit infrastructure // is setup to handle TLV payloads, so testing this // would require implementing a separate mock iterator @@ -3325,6 +3334,27 @@ func (l *channelLink) processRemoteAdds(fwdPkg *channeldb.FwdPkg, continue } + // Retrieve onion obfuscator from onion blob in order to + // produce initial obfuscation of the onion failureCode. + obfuscator, failureCode := chanIterator.ExtractErrorEncrypter( + l.cfg.ExtractErrorEncrypter, + routeRole == hop.RouteRoleIntroduction, + ) + if failureCode != lnwire.CodeNone { + // If we're unable to process the onion blob than we + // should send the malformed htlc error to payment + // sender. + l.sendMalformedHTLCError( + pd.HtlcIndex, failureCode, onionBlob[:], + pd.SourceRef, + ) + + l.log.Errorf("unable to decode onion "+ + "obfuscator: %v", failureCode) + + continue + } + fwdInfo := pld.ForwardingInfo() // Check whether the payload we've just processed uses our diff --git a/htlcswitch/mock.go b/htlcswitch/mock.go index 0dbefd45cf8..cd3e5026b52 100644 --- a/htlcswitch/mock.go +++ b/htlcswitch/mock.go @@ -341,7 +341,7 @@ func (r *mockHopIterator) ExtraOnionBlob() []byte { } func (r *mockHopIterator) ExtractErrorEncrypter( - extracter hop.ErrorEncrypterExtracter) (hop.ErrorEncrypter, + extracter hop.ErrorEncrypterExtracter, _ bool) (hop.ErrorEncrypter, lnwire.FailCode) { return extracter(nil) From de9c9c028c64566f75dbf00d4b05c448128f53e9 Mon Sep 17 00:00:00 2001 From: Carla Kirk-Cohen Date: Mon, 8 Apr 2024 15:14:11 -0400 Subject: [PATCH 07/17] htlcswitch: set packet obfuscator for failures through switch Set obfuscator for use in blinded error handling when we forward failures through the switch. --- htlcswitch/mailbox.go | 1 + htlcswitch/switch.go | 15 ++++++++++++++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/htlcswitch/mailbox.go b/htlcswitch/mailbox.go index 99066497c76..a729e3ba505 100644 --- a/htlcswitch/mailbox.go +++ b/htlcswitch/mailbox.go @@ -738,6 +738,7 @@ func (m *memoryMailBox) FailAdd(pkt *htlcPacket) { sourceRef: pkt.sourceRef, hasSource: true, localFailure: localFailure, + obfuscator: pkt.obfuscator, linkFailure: linkError, htlc: &lnwire.UpdateFailHTLC{ Reason: reason, diff --git a/htlcswitch/switch.go b/htlcswitch/switch.go index 70b819b1e45..d473d84222d 100644 --- a/htlcswitch/switch.go +++ b/htlcswitch/switch.go @@ -1297,6 +1297,11 @@ func (s *Switch) handlePacketForward(packet *htlcPacket) error { fail, isFail := htlc.(*lnwire.UpdateFailHTLC) if isFail && !packet.hasSource { + // HTLC resolutions and messages restored from disk + // don't have the obfuscator set from the original htlc + // add packet - set it here for use in blinded errors. + packet.obfuscator = circuit.ErrorEncrypter + switch { // No message to encrypt, locally sourced payment. case circuit.ErrorEncrypter == nil: @@ -1485,6 +1490,7 @@ func (s *Switch) failAddPacket(packet *htlcPacket, failure *LinkError) error { incomingTimeout: packet.incomingTimeout, outgoingTimeout: packet.outgoingTimeout, circuit: packet.circuit, + obfuscator: packet.obfuscator, linkFailure: failure, htlc: &lnwire.UpdateFailHTLC{ Reason: reason, @@ -1841,6 +1847,10 @@ out: // resolution message on restart. resolutionMsg.errChan <- nil + // Create a htlc packet for this resolution. We do + // not have some of the information that we'll need + // for blinded error handling here , so we'll rely on + // our forwarding logic to fill it in later. pkt := &htlcPacket{ outgoingChanID: resolutionMsg.SourceChan, outgoingHTLCID: resolutionMsg.HtlcIndex, @@ -2065,6 +2075,8 @@ func (s *Switch) reforwardResolutions() error { // The circuit is still open, so we can assume that the link or // switch (if we are the source) hasn't cleaned it up yet. + // We rely on our forwarding logic to fill in details that + // are not currently available to us. resPkt := &htlcPacket{ outgoingChanID: resMsg.SourceChan, outgoingHTLCID: resMsg.HtlcIndex, @@ -2214,7 +2226,8 @@ func (s *Switch) reforwardSettleFails(fwdPkgs []*channeldb.FwdPkg) { // we can continue to propagate it. This // failure originated from another node, so // the linkFailure field is not set on this - // packet. + // packet. We rely on the link to fill in + // additional circuit information for us. failPacket := &htlcPacket{ outgoingChanID: fwdPkg.Source, outgoingHTLCID: pd.ParentIndex, From 43687181f73a59a65460d562f897acb83f6cddf8 Mon Sep 17 00:00:00 2001 From: Carla Kirk-Cohen Date: Mon, 8 Apr 2024 15:51:15 -0400 Subject: [PATCH 08/17] htlcswitch: convert blinded failures for blinded payments --- htlcswitch/hop/error_encryptor.go | 6 ++ htlcswitch/link.go | 118 ++++++++++++++++++++++++++++-- 2 files changed, 117 insertions(+), 7 deletions(-) diff --git a/htlcswitch/hop/error_encryptor.go b/htlcswitch/hop/error_encryptor.go index 127370e4d49..23272ec00d4 100644 --- a/htlcswitch/hop/error_encryptor.go +++ b/htlcswitch/hop/error_encryptor.go @@ -39,6 +39,12 @@ const ( EncrypterTypeRelaying = 4 ) +// IsBlinded returns a boolean indicating whether the error encrypter belongs +// to a blinded route. +func (e EncrypterType) IsBlinded() bool { + return e == EncrypterTypeIntroduction || e == EncrypterTypeRelaying +} + // ErrorEncrypterExtracter defines a function signature that extracts an // ErrorEncrypter from an sphinx OnionPacket. type ErrorEncrypterExtracter func(*btcec.PublicKey) (ErrorEncrypter, diff --git a/htlcswitch/link.go b/htlcswitch/link.go index b5cba787cff..ed3151728fd 100644 --- a/htlcswitch/link.go +++ b/htlcswitch/link.go @@ -1793,8 +1793,20 @@ func (l *channelLink) handleDownstreamPkt(pkt *htlcPacket) { htlc.ID = pkt.incomingHTLCID // We send the HTLC message to the peer which initially created - // the HTLC. - l.cfg.Peer.SendMessage(false, htlc) + // the HTLC. If the incoming blinding point is non-nil, we + // know that we are a relaying node in a blinded path. + // Otherwise, we're either an introduction node or not part of + // a blinded path at all. + if err := l.sendIncomingHTLCFailureMsg( + htlc.ID, + pkt.obfuscator, + htlc.Reason, + ); err != nil { + l.log.Errorf("unable to send HTLC failure: %v", + err) + + return + } // If the packet does not have a link failure set, it failed // further down the route so we notify a forwarding failure. @@ -3720,11 +3732,14 @@ func (l *channelLink) sendHTLCError(pd *lnwallet.PaymentDescriptor, return } - l.cfg.Peer.SendMessage(false, &lnwire.UpdateFailHTLC{ - ChanID: l.ChanID(), - ID: pd.HtlcIndex, - Reason: reason, - }) + // Send the appropriate failure message depending on whether we're + // in a blinded route or not. + if err := l.sendIncomingHTLCFailureMsg( + pd.HtlcIndex, e, reason, + ); err != nil { + l.log.Errorf("unable to send HTLC failure: %v", err) + return + } // Notify a link failure on our incoming link. Outgoing htlc information // is not available at this point, because we have not decrypted the @@ -3753,6 +3768,95 @@ func (l *channelLink) sendHTLCError(pd *lnwallet.PaymentDescriptor, ) } +// sendPeerHTLCFailure handles sending a HTLC failure message back to the +// peer from which the HTLC was received. This function is primarily used to +// handle the special requirements of route blinding, specifically: +// - Forwarding nodes must switch out any errors with MalformedFailHTLC +// - Introduction nodes should return regular HTLC failure messages. +// +// It accepts the original opaque failure, which will be used in the case +// that we're not part of a blinded route and an error encrypter that'll be +// used if we are the introduction node and need to present an error as if +// we're the failing party. +// +// Note: this function does not yet handle special error cases for receiving +// nodes in blinded paths, as LND does not support blinded receives. +func (l *channelLink) sendIncomingHTLCFailureMsg(htlcIndex uint64, + e hop.ErrorEncrypter, + originalFailure lnwire.OpaqueReason) error { + + var msg lnwire.Message + switch { + // Our circuit's error encrypter will be nil if this was a locally + // initiated payment. We can only hit a blinded error for a locally + // initiated payment if we allow ourselves to be picked as the + // introduction node for our own payments and in that case we + // shouldn't reach this code. To prevent the HTLC getting stuck, + // we fail it back and log an error. + // code. + case e == nil: + msg = &lnwire.UpdateFailHTLC{ + ChanID: l.ChanID(), + ID: htlcIndex, + Reason: originalFailure, + } + + l.log.Errorf("Unexpected blinded failure when "+ + "we are the sending node, incoming htlc: %v(%v)", + l.ShortChanID(), htlcIndex) + + // For cleartext hops (ie, non-blinded/normal) we don't need any + // transformation on the error message and can just send the original. + case !e.Type().IsBlinded(): + msg = &lnwire.UpdateFailHTLC{ + ChanID: l.ChanID(), + ID: htlcIndex, + Reason: originalFailure, + } + + // When we're the introduction node, we need to convert the error to + // a UpdateFailHTLC. + case e.Type() == hop.EncrypterTypeIntroduction: + l.log.Debugf("Introduction blinded node switching out failure "+ + "error: %v", htlcIndex) + + // The specification does not require that we set the onion + // blob. + failureMsg := lnwire.NewInvalidBlinding(nil) + reason, err := e.EncryptFirstHop(failureMsg) + if err != nil { + return err + } + + msg = &lnwire.UpdateFailHTLC{ + ChanID: l.ChanID(), + ID: htlcIndex, + Reason: reason, + } + + // If we are a relaying node, we need to switch out any error that + // we've received to a malformed HTLC error. + case e.Type() == hop.EncrypterTypeRelaying: + l.log.Debugf("Relaying blinded node switching out malformed "+ + "error: %v", htlcIndex) + + msg = &lnwire.UpdateFailMalformedHTLC{ + ChanID: l.ChanID(), + ID: htlcIndex, + FailureCode: lnwire.CodeInvalidBlinding, + } + + default: + return fmt.Errorf("unexpected encrypter: %d", e) + } + + if err := l.cfg.Peer.SendMessage(false, msg); err != nil { + l.log.Warnf("Send update fail failed: %v", err) + } + + return nil +} + // sendMalformedHTLCError helper function which sends the malformed HTLC update // to the payment sender. func (l *channelLink) sendMalformedHTLCError(htlcIndex uint64, From d13a73a93a97bb34a9dbb63ee5a43662a7fa31c2 Mon Sep 17 00:00:00 2001 From: Carla Kirk-Cohen Date: Thu, 15 Feb 2024 09:18:51 -0500 Subject: [PATCH 09/17] itest: add test coverage for failure at blinded receiver --- itest/list_on_test.go | 4 +++ itest/lnd_route_blinding_test.go | 56 ++++++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+) diff --git a/itest/list_on_test.go b/itest/list_on_test.go index 4696fc80ec7..8f8574d5b97 100644 --- a/itest/list_on_test.go +++ b/itest/list_on_test.go @@ -566,6 +566,10 @@ var allTestCases = []*lntest.TestCase{ Name: "forward blinded", TestFunc: testForwardBlindedRoute, }, + { + Name: "receiver blinded error", + TestFunc: testReceiverBlindedError, + }, { Name: "removetx", TestFunc: testRemoveTx, diff --git a/itest/lnd_route_blinding_test.go b/itest/lnd_route_blinding_test.go index e2d1ec033ed..2e41e4ba0c5 100644 --- a/itest/lnd_route_blinding_test.go +++ b/itest/lnd_route_blinding_test.go @@ -788,3 +788,59 @@ func testForwardBlindedRoute(ht *lntest.HarnessTest) { ht.AssertHTLCNotActive(ht.Bob, testCase.channels[1], hash[:]) ht.AssertHTLCNotActive(ht.Alice, testCase.channels[0], hash[:]) } + +// Tests handling of errors from the receiving node in a blinded route, testing +// a payment over: Alice -- Bob -- Carol -- Dave, where Bob is the introduction +// node. +// +// Note that at present the payment fails at Dave because we do not yet support +// receiving to blinded routes. In future, we can substitute this test out to +// trigger an IncorrectPaymentDetails failure. In the meantime, this test +// provides valuable coverage for the case where a node in the route is not +// spec compliant (ie, does not return the blinded failure and just uses a +// normal one) because Dave will not appropriately convert the error. +func testReceiverBlindedError(ht *lntest.HarnessTest) { + ctx, testCase := newBlindedForwardTest(ht) + defer testCase.cleanup() + route := testCase.setup(ctx) + + sendAndResumeBlindedPayment(ctx, ht, testCase, route) +} + +// sendAndResumeBlindedPayment sends a blinded payment through the test +// network provided, intercepting the payment at Carol and allowing it to +// resume. This utility function allows us to ensure that payments at least +// reach Carol and asserts that all errors appear to originate from the +// introduction node. +func sendAndResumeBlindedPayment(ctx context.Context, ht *lntest.HarnessTest, + testCase *blindedForwardTest, route *routing.BlindedPayment) { + + blindedRoute := testCase.createRouteToBlinded(10_000_000, route) + + // Before we dispatch the payment, spin up a goroutine that will + // intercept the HTLC on Carol's forward. This allows us to ensure + // that the HTLC actually reaches the location we expect it to. + resolveHTLC := testCase.interceptFinalHop() + + // First, test sending the payment all the way through to Dave. We + // expect this payment to fail, because he does not know how to + // process payments to a blinded route (not yet supported). + testCase.sendBlindedPayment(ctx, blindedRoute) + + // When Carol intercepts the HTLC, instruct her to resume the payment + // so that it'll reach Dave and fail. + resolveHTLC(routerrpc.ResolveHoldForwardAction_RESUME) + + // Wait for the HTLC to reflect as failed for Alice. + preimage, err := lntypes.MakePreimage(testCase.preimage[:]) + require.NoError(ht, err) + pmt := ht.AssertPaymentStatus(ht.Alice, preimage, lnrpc.Payment_FAILED) + require.Len(ht, pmt.Htlcs, 1) + require.EqualValues( + ht, 1, pmt.Htlcs[0].Failure.FailureSourceIndex, + ) + require.Equal( + ht, lnrpc.Failure_INVALID_ONION_BLINDING, + pmt.Htlcs[0].Failure.Code, + ) +} From 4535cf6c650e374461e8d9e03514274185d7c699 Mon Sep 17 00:00:00 2001 From: Carla Kirk-Cohen Date: Thu, 15 Feb 2024 09:55:41 -0500 Subject: [PATCH 10/17] itest: add coverage for failure within a blinded route --- itest/list_on_test.go | 4 +++ itest/lnd_route_blinding_test.go | 57 ++++++++++++++++++++++++++++++++ 2 files changed, 61 insertions(+) diff --git a/itest/list_on_test.go b/itest/list_on_test.go index 8f8574d5b97..677f4cf9e26 100644 --- a/itest/list_on_test.go +++ b/itest/list_on_test.go @@ -570,6 +570,10 @@ var allTestCases = []*lntest.TestCase{ Name: "receiver blinded error", TestFunc: testReceiverBlindedError, }, + { + Name: "relayer blinded error", + TestFunc: testRelayingBlindedError, + }, { Name: "removetx", TestFunc: testRemoveTx, diff --git a/itest/lnd_route_blinding_test.go b/itest/lnd_route_blinding_test.go index 2e41e4ba0c5..b2e0cf9d3db 100644 --- a/itest/lnd_route_blinding_test.go +++ b/itest/lnd_route_blinding_test.go @@ -524,6 +524,43 @@ func (b *blindedForwardTest) interceptFinalHop() func(routerrpc.ResolveHoldForwa return resolve } +// drainCarolLiquidity will drain all of the liquidity in Carol's channel in +// the direction requested: +// - incoming: Carol has no incoming liquidity from Bob +// - outgoing: Carol has no outgoing liquidity to Dave. +func (b *blindedForwardTest) drainCarolLiquidity(incoming bool) { + sendingNode := b.carol + receivingNode := b.dave + + if incoming { + sendingNode = b.ht.Bob + receivingNode = b.carol + } + + resp := sendingNode.RPC.ListChannels(&lnrpc.ListChannelsRequest{ + Peer: receivingNode.PubKey[:], + }) + require.Len(b.ht, resp.Channels, 1) + + // We can't send our channel reserve, and leave some buffer for fees. + paymentAmt := resp.Channels[0].LocalBalance - + int64(resp.Channels[0].RemoteConstraints.ChanReserveSat) - 25000 + + invoice := receivingNode.RPC.AddInvoice(&lnrpc.Invoice{ + // Leave some leeway for fees for the HTLC. + Value: paymentAmt, + }) + + pmtClient := sendingNode.RPC.SendPayment( + &routerrpc.SendPaymentRequest{ + PaymentRequest: invoice.PaymentRequest, + TimeoutSeconds: 60, + }, + ) + + b.ht.AssertPaymentStatusFromStream(pmtClient, lnrpc.Payment_SUCCEEDED) +} + // setupFourHopNetwork creates a network with the following topology and // liquidity: // Alice (100k)----- Bob (100k) ----- Carol (100k) ----- Dave @@ -807,6 +844,26 @@ func testReceiverBlindedError(ht *lntest.HarnessTest) { sendAndResumeBlindedPayment(ctx, ht, testCase, route) } +// testRelayingBlindedError tests handling of errors from relaying nodes in a +// blinded route, testing a failure over on Carol's outgoing link in the +// following topology: Alice -- Bob -- Carol -- Dave, where Bob is the +// introduction node. +func testRelayingBlindedError(ht *lntest.HarnessTest) { + ctx, testCase := newBlindedForwardTest(ht) + defer testCase.cleanup() + route := testCase.setup(ctx) + + // Before we send our payment, drain all of Carol's liquidity + // so that she can't forward the payment to Dave. + testCase.drainCarolLiquidity(false) + + // Then dispatch the payment through Carol which will fail due to + // a lack of liquidity. This check only happens _after_ the interceptor + // has given the instruction to resume so we can use test + // infrastructure that will go ahead and intercept the payment. + sendAndResumeBlindedPayment(ctx, ht, testCase, route) +} + // sendAndResumeBlindedPayment sends a blinded payment through the test // network provided, intercepting the payment at Carol and allowing it to // resume. This utility function allows us to ensure that payments at least From 428a33fbda6c6c2a7b136af7ef09a5a4a6c48590 Mon Sep 17 00:00:00 2001 From: Carla Kirk-Cohen Date: Thu, 15 Feb 2024 10:13:41 -0500 Subject: [PATCH 11/17] itest: add coverage for failure at the introduction node --- itest/list_on_test.go | 4 ++++ itest/lnd_route_blinding_test.go | 34 +++++++++++++++++++++++++++----- 2 files changed, 33 insertions(+), 5 deletions(-) diff --git a/itest/list_on_test.go b/itest/list_on_test.go index 677f4cf9e26..18285c85f5b 100644 --- a/itest/list_on_test.go +++ b/itest/list_on_test.go @@ -574,6 +574,10 @@ var allTestCases = []*lntest.TestCase{ Name: "relayer blinded error", TestFunc: testRelayingBlindedError, }, + { + Name: "introduction blinded error", + TestFunc: testIntroductionNodeError, + }, { Name: "removetx", TestFunc: testRemoveTx, diff --git a/itest/lnd_route_blinding_test.go b/itest/lnd_route_blinding_test.go index b2e0cf9d3db..7cedbf3f039 100644 --- a/itest/lnd_route_blinding_test.go +++ b/itest/lnd_route_blinding_test.go @@ -841,7 +841,7 @@ func testReceiverBlindedError(ht *lntest.HarnessTest) { defer testCase.cleanup() route := testCase.setup(ctx) - sendAndResumeBlindedPayment(ctx, ht, testCase, route) + sendAndResumeBlindedPayment(ctx, ht, testCase, route, true) } // testRelayingBlindedError tests handling of errors from relaying nodes in a @@ -861,7 +861,7 @@ func testRelayingBlindedError(ht *lntest.HarnessTest) { // a lack of liquidity. This check only happens _after_ the interceptor // has given the instruction to resume so we can use test // infrastructure that will go ahead and intercept the payment. - sendAndResumeBlindedPayment(ctx, ht, testCase, route) + sendAndResumeBlindedPayment(ctx, ht, testCase, route, true) } // sendAndResumeBlindedPayment sends a blinded payment through the test @@ -870,14 +870,18 @@ func testRelayingBlindedError(ht *lntest.HarnessTest) { // reach Carol and asserts that all errors appear to originate from the // introduction node. func sendAndResumeBlindedPayment(ctx context.Context, ht *lntest.HarnessTest, - testCase *blindedForwardTest, route *routing.BlindedPayment) { + testCase *blindedForwardTest, route *routing.BlindedPayment, + interceptAtCarol bool) { blindedRoute := testCase.createRouteToBlinded(10_000_000, route) // Before we dispatch the payment, spin up a goroutine that will // intercept the HTLC on Carol's forward. This allows us to ensure // that the HTLC actually reaches the location we expect it to. - resolveHTLC := testCase.interceptFinalHop() + var resolveHTLC func(routerrpc.ResolveHoldForwardAction) + if interceptAtCarol { + resolveHTLC = testCase.interceptFinalHop() + } // First, test sending the payment all the way through to Dave. We // expect this payment to fail, because he does not know how to @@ -886,7 +890,9 @@ func sendAndResumeBlindedPayment(ctx context.Context, ht *lntest.HarnessTest, // When Carol intercepts the HTLC, instruct her to resume the payment // so that it'll reach Dave and fail. - resolveHTLC(routerrpc.ResolveHoldForwardAction_RESUME) + if interceptAtCarol { + resolveHTLC(routerrpc.ResolveHoldForwardAction_RESUME) + } // Wait for the HTLC to reflect as failed for Alice. preimage, err := lntypes.MakePreimage(testCase.preimage[:]) @@ -901,3 +907,21 @@ func sendAndResumeBlindedPayment(ctx context.Context, ht *lntest.HarnessTest, pmt.Htlcs[0].Failure.Code, ) } + +// testIntroductionNodeError tests handling of errors in a blinded route when +// the introduction node is the source of the error. This test sends a payment +// over Alice -- Bob -- Carol -- Dave, where Bob is the introduction node and +// has insufficient outgoing liquidity to forward on to carol. +func testIntroductionNodeError(ht *lntest.HarnessTest) { + ctx, testCase := newBlindedForwardTest(ht) + defer testCase.cleanup() + route := testCase.setup(ctx) + + // Before we send our payment, drain all of Carol's incoming liquidity + // so that she can't receive the forward from Bob, causing a failure + // at the introduction node. + testCase.drainCarolLiquidity(true) + + // Send the payment, but do not expect it to reach Carol at all. + sendAndResumeBlindedPayment(ctx, ht, testCase, route, false) +} From d57c6fab47d186f0a687f352364ce761d1feb81d Mon Sep 17 00:00:00 2001 From: Carla Kirk-Cohen Date: Fri, 15 Mar 2024 11:53:52 -0400 Subject: [PATCH 12/17] itest: add coverage for disabling blinded forwards --- itest/list_on_test.go | 4 ++++ itest/lnd_route_blinding_test.go | 20 ++++++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/itest/list_on_test.go b/itest/list_on_test.go index 18285c85f5b..796a968050e 100644 --- a/itest/list_on_test.go +++ b/itest/list_on_test.go @@ -578,6 +578,10 @@ var allTestCases = []*lntest.TestCase{ Name: "introduction blinded error", TestFunc: testIntroductionNodeError, }, + { + Name: "disable introduction node", + TestFunc: testDisableIntroductionNode, + }, { Name: "removetx", TestFunc: testRemoveTx, diff --git a/itest/lnd_route_blinding_test.go b/itest/lnd_route_blinding_test.go index 7cedbf3f039..4cbd75ff67f 100644 --- a/itest/lnd_route_blinding_test.go +++ b/itest/lnd_route_blinding_test.go @@ -925,3 +925,23 @@ func testIntroductionNodeError(ht *lntest.HarnessTest) { // Send the payment, but do not expect it to reach Carol at all. sendAndResumeBlindedPayment(ctx, ht, testCase, route, false) } + +// testDisableIntroductionNode tests disabling of blinded forwards for the +// introduction node. +func testDisableIntroductionNode(ht *lntest.HarnessTest) { + // Disable route blinding for Bob, then re-connect to Alice. + ht.RestartNodeWithExtraArgs(ht.Bob, []string{ + "--protocol.no-route-blinding", + }) + ht.EnsureConnected(ht.Alice, ht.Bob) + + ctx, testCase := newBlindedForwardTest(ht) + defer testCase.cleanup() + route := testCase.setup(ctx) + // We always expect failures to look like they originated at Bob + // because blinded errors are converted. However, our tests intercepts + // all of Carol's forwards and we're not providing it any interceptor + // instructions. This means that the test will hang/timeout at Carol + // if Bob _doesn't_ fail the HTLC back as expected. + sendAndResumeBlindedPayment(ctx, ht, testCase, route, false) +} From 2140f1940f55ae7a78b27f7305f7a977d003aed7 Mon Sep 17 00:00:00 2001 From: Carla Kirk-Cohen Date: Thu, 25 Apr 2024 13:38:00 -0400 Subject: [PATCH 13/17] itest: manually set timeout on cancel payment and provide cancel For tests where our payments require an on-chain resolution, provide longer timeout and return cancel functions. --- itest/lnd_route_blinding_test.go | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/itest/lnd_route_blinding_test.go b/itest/lnd_route_blinding_test.go index 4cbd75ff67f..8a796c2ddf2 100644 --- a/itest/lnd_route_blinding_test.go +++ b/itest/lnd_route_blinding_test.go @@ -448,9 +448,11 @@ func (b *blindedForwardTest) createRouteToBlinded(paymentAmt int64, return resp.Routes[0] } -// sendBlindedPayment dispatches a payment to the route provided. +// sendBlindedPayment dispatches a payment to the route provided, returning a +// cancel function for the payment. Timeout is set for very long to allow +// time for on-chain resolution. func (b *blindedForwardTest) sendBlindedPayment(ctx context.Context, - route *lnrpc.Route) { + route *lnrpc.Route) func() { hash := sha256.Sum256(b.preimage[:]) sendReq := &routerrpc.SendToRouteRequest{ @@ -458,11 +460,13 @@ func (b *blindedForwardTest) sendBlindedPayment(ctx context.Context, Route: route, } - // Dispatch in a goroutine because this call is blocking - we assume - // that we'll have assertions that this payment is sent by the caller. + ctx, cancel := context.WithTimeout(ctx, time.Hour) go func() { - b.ht.Alice.RPC.SendToRouteV2(sendReq) + _, err := b.ht.Alice.RPC.Router.SendToRouteV2(ctx, sendReq) + require.NoError(b.ht, err) }() + + return cancel } // interceptFinalHop launches a goroutine to intercept Carol's htlcs and @@ -805,7 +809,8 @@ func testForwardBlindedRoute(ht *lntest.HarnessTest) { resolveHTLC := testCase.interceptFinalHop() // Once our interceptor is set up, we can send the blinded payment. - testCase.sendBlindedPayment(ctx, blindedRoute) + cancelPmt := testCase.sendBlindedPayment(ctx, blindedRoute) + defer cancelPmt() // Wait for the HTLC to be active on Alice's channel. hash := sha256.Sum256(testCase.preimage[:]) @@ -886,7 +891,8 @@ func sendAndResumeBlindedPayment(ctx context.Context, ht *lntest.HarnessTest, // First, test sending the payment all the way through to Dave. We // expect this payment to fail, because he does not know how to // process payments to a blinded route (not yet supported). - testCase.sendBlindedPayment(ctx, blindedRoute) + cancelPmt := testCase.sendBlindedPayment(ctx, blindedRoute) + defer cancelPmt() // When Carol intercepts the HTLC, instruct her to resume the payment // so that it'll reach Dave and fail. From 75d4a4c295e1307ad8de065c1b1e8e46249696d2 Mon Sep 17 00:00:00 2001 From: Carla Kirk-Cohen Date: Mon, 8 Apr 2024 11:46:43 -0400 Subject: [PATCH 14/17] itest: add coverage for blinded error resolution from on-chain failure This itest adds a test that we still propagate blinded errors back properly after a restart with an on-chain resolution. The test also updates our sendpayment timeout to longer so that there's time to resolve the on chain claim. --- itest/list_on_test.go | 4 ++ itest/lnd_route_blinding_test.go | 109 ++++++++++++++++++++++++++++++- 2 files changed, 111 insertions(+), 2 deletions(-) diff --git a/itest/list_on_test.go b/itest/list_on_test.go index 796a968050e..2434ee92d05 100644 --- a/itest/list_on_test.go +++ b/itest/list_on_test.go @@ -582,6 +582,10 @@ var allTestCases = []*lntest.TestCase{ Name: "disable introduction node", TestFunc: testDisableIntroductionNode, }, + { + Name: "on chain to blinded", + TestFunc: testErrorHandlingOnChainFailure, + }, { Name: "removetx", TestFunc: testRemoveTx, diff --git a/itest/lnd_route_blinding_test.go b/itest/lnd_route_blinding_test.go index 8a796c2ddf2..1cae46a0cb3 100644 --- a/itest/lnd_route_blinding_test.go +++ b/itest/lnd_route_blinding_test.go @@ -355,14 +355,16 @@ func (b *blindedForwardTest) setup( ctx context.Context) *routing.BlindedPayment { b.carol = b.ht.NewNode("Carol", []string{ - "requireinterceptor", + "--requireinterceptor", "--bitcoin.timelockdelta=18", }) var err error b.carolInterceptor, err = b.carol.RPC.Router.HtlcInterceptor(ctx) require.NoError(b.ht, err, "interceptor") - b.dave = b.ht.NewNode("Dave", nil) + b.dave = b.ht.NewNode("Dave", []string{ + "--bitcoin.timelockdelta=18", + }) b.channels = setupFourHopNetwork(b.ht, b.carol, b.dave) @@ -951,3 +953,106 @@ func testDisableIntroductionNode(ht *lntest.HarnessTest) { // if Bob _doesn't_ fail the HTLC back as expected. sendAndResumeBlindedPayment(ctx, ht, testCase, route, false) } + +// testErrorHandlingOnChainFailure tests handling of blinded errors when we're +// resolving from an on-chain resolution. This test also tests that we're able +// to resolve blinded HTLCs on chain between restarts, as we've got all the +// infrastructure in place already for error testing. +func testErrorHandlingOnChainFailure(ht *lntest.HarnessTest) { + // Setup a test case, note that we don't use its built in clean up + // because we're going to close a channel so we'll close out the + // rest manually. + ctx, testCase := newBlindedForwardTest(ht) + + // Note that we send a larger amount here do it'll be worthwhile for + // the sweeper to claim. + route := testCase.setup(ctx) + blindedRoute := testCase.createRouteToBlinded(50_000_000, route) + + // Once our interceptor is set up, we can send the blinded payment. + cancelPmt := testCase.sendBlindedPayment(ctx, blindedRoute) + defer cancelPmt() + + // Wait for the HTLC to be active on Alice and Bob's channels. + hash := sha256.Sum256(testCase.preimage[:]) + ht.AssertOutgoingHTLCActive(ht.Alice, testCase.channels[0], hash[:]) + ht.AssertOutgoingHTLCActive(ht.Bob, testCase.channels[1], hash[:]) + + // Intercept the forward on Carol's link, but do not take any action + // so that we have the chance to force close with this HTLC in flight. + carolHTLC, err := testCase.carolInterceptor.Recv() + require.NoError(ht, err) + + // Force close Bob <-> Carol. + closeStream, _ := ht.CloseChannelAssertPending( + ht.Bob, testCase.channels[1], true, + ) + + ht.AssertStreamChannelForceClosed( + ht.Bob, testCase.channels[1], false, closeStream, + ) + + // SuspendCarol so that she can't interfere with the resolution of the + // HTLC from now on. + restartCarol := ht.SuspendNode(testCase.carol) + + // Mine blocks so that Bob will claim his CSV delayed local commitment, + // we've already mined 1 block so we need one less than our CSV. + ht.MineBlocks(node.DefaultCSV - 1) + ht.AssertNumPendingSweeps(ht.Bob, 1) + ht.MineEmptyBlocks(1) + ht.Miner.MineBlocksAndAssertNumTxes(1, 1) + + // Restart bob so that we can test that he's able to recover everything + // he needs to claim a blinded HTLC. + ht.RestartNode(ht.Bob) + + // Mine enough blocks for Bob to trigger timeout of his outgoing HTLC. + // Carol's incoming expiry height is Bob's outgoing so we can use this + // value. + info := ht.Bob.RPC.GetInfo() + target := carolHTLC.IncomingExpiry - info.BlockHeight + ht.MineBlocks(target) + + // Wait for Bob's timeout transaction in the mempool, since we've + // suspended Carol we don't need to account for her commitment output + // claim. + ht.Miner.MineBlocksAndAssertNumTxes(1, 1) + + // Assert that the HTLC has cleared. + ht.AssertHTLCNotActive(ht.Alice, testCase.channels[0], hash[:]) + ht.AssertHTLCNotActive(ht.Bob, testCase.channels[0], hash[:]) + + // Wait for the HTLC to reflect as failed for Alice. + paymentStream := ht.Alice.RPC.TrackPaymentV2(hash[:]) + htlcs := ht.ReceiveTrackPayment(paymentStream).Htlcs + require.Len(ht, htlcs, 1) + require.NotNil(ht, htlcs[0].Failure) + require.Equal( + ht, htlcs[0].Failure.Code, + lnrpc.Failure_INVALID_ONION_BLINDING, + ) + + // Clean up the rest of our force close: mine blocks so that Bob's CSV + // expires plus one block to trigger his sweep and then mine it. + ht.MineBlocks(node.DefaultCSV + 1) + ht.Miner.MineBlocksAndAssertNumTxes(1, 1) + + // Bring carol back up so that we can close out the rest of our + // channels cooperatively. She requires an interceptor to start up + // so we just re-register our interceptor. + require.NoError(ht, restartCarol()) + _, err = testCase.carol.RPC.Router.HtlcInterceptor(ctx) + require.NoError(ht, err, "interceptor") + + // Assert that Carol has started up and reconnected to dave so that + // we can close out channels cooperatively. + ht.EnsureConnected(testCase.carol, testCase.dave) + + // Manually close out the rest of our channels and cancel (don't use + // built in cleanup which will try close the already-force-closed + // channel). + ht.CloseChannel(ht.Alice, testCase.channels[0]) + ht.CloseChannel(testCase.carol, testCase.channels[2]) + testCase.cancel() +} From eaa85920ea1dca750eaa31b6f23c161135df24b0 Mon Sep 17 00:00:00 2001 From: Carla Kirk-Cohen Date: Sat, 17 Feb 2024 17:50:38 -0500 Subject: [PATCH 15/17] multi: enable optional route blinding feature --- feature/default_sets.go | 5 + feature/deps.go | 6 + feature/manager.go | 10 +- lnrpc/lightning.pb.go | 674 ++++++++++++++-------------- lnrpc/lightning.proto | 2 + lnrpc/lightning.swagger.json | 4 + lnrpc/peersrpc/peers.swagger.json | 2 + lnrpc/routerrpc/router.swagger.json | 2 + lnwire/features.go | 10 + server.go | 1 + 10 files changed, 383 insertions(+), 333 deletions(-) diff --git a/feature/default_sets.go b/feature/default_sets.go index 3b7e0f7ea3b..cc802fe8593 100644 --- a/feature/default_sets.go +++ b/feature/default_sets.go @@ -79,6 +79,11 @@ var defaultSetDesc = setDesc{ SetInit: {}, // I SetNodeAnn: {}, // N }, + lnwire.RouteBlindingOptional: { + SetInit: {}, // I + SetNodeAnn: {}, // N + SetInvoice: {}, // 9 + }, lnwire.ShutdownAnySegwitOptional: { SetInit: {}, // I SetNodeAnn: {}, // N diff --git a/feature/deps.go b/feature/deps.go index b9fec2da08e..350d49acf31 100644 --- a/feature/deps.go +++ b/feature/deps.go @@ -79,6 +79,12 @@ var deps = depDesc{ lnwire.AnchorsZeroFeeHtlcTxOptional: {}, lnwire.ExplicitChannelTypeOptional: {}, }, + lnwire.RouteBlindingOptional: { + lnwire.TLVOnionPayloadOptional: {}, + }, + lnwire.RouteBlindingRequired: { + lnwire.TLVOnionPayloadRequired: {}, + }, } // ValidateDeps asserts that a feature vector sets all features and their diff --git a/feature/manager.go b/feature/manager.go index 98317eb8f15..c7029e89382 100644 --- a/feature/manager.go +++ b/feature/manager.go @@ -60,6 +60,9 @@ type Config struct { // segwit witness versions for co-op closes. NoAnySegwit bool + // NoRouteBlinding unsets route blinding feature bits. + NoRouteBlinding bool + // CustomFeatures is a set of custom features to advertise in each // set. CustomFeatures map[Set][]lnwire.FeatureBit @@ -123,6 +126,8 @@ func newManager(cfg Config, desc setDesc) (*Manager, error) { raw.Unset(lnwire.PaymentAddrRequired) raw.Unset(lnwire.MPPOptional) raw.Unset(lnwire.MPPRequired) + raw.Unset(lnwire.RouteBlindingOptional) + raw.Unset(lnwire.RouteBlindingRequired) raw.Unset(lnwire.AMPOptional) raw.Unset(lnwire.AMPRequired) raw.Unset(lnwire.KeysendOptional) @@ -179,7 +184,10 @@ func newManager(cfg Config, desc setDesc) (*Manager, error) { raw.Unset(lnwire.SimpleTaprootChannelsOptionalStaging) raw.Unset(lnwire.SimpleTaprootChannelsRequiredStaging) } - + if cfg.NoRouteBlinding { + raw.Unset(lnwire.RouteBlindingOptional) + raw.Unset(lnwire.RouteBlindingRequired) + } for _, custom := range cfg.CustomFeatures[set] { if custom > set.Maximum() { return nil, fmt.Errorf("feature bit: %v "+ diff --git a/lnrpc/lightning.pb.go b/lnrpc/lightning.pb.go index b2bc8034c70..161fd2f11f4 100644 --- a/lnrpc/lightning.pb.go +++ b/lnrpc/lightning.pb.go @@ -651,6 +651,8 @@ const ( FeatureBit_ANCHORS_OPT FeatureBit = 21 FeatureBit_ANCHORS_ZERO_FEE_HTLC_REQ FeatureBit = 22 FeatureBit_ANCHORS_ZERO_FEE_HTLC_OPT FeatureBit = 23 + FeatureBit_ROUTE_BLINDING_REQUIRED FeatureBit = 24 + FeatureBit_ROUTE_BLINDING_OPTIONAL FeatureBit = 25 FeatureBit_AMP_REQ FeatureBit = 30 FeatureBit_AMP_OPT FeatureBit = 31 ) @@ -681,6 +683,8 @@ var ( 21: "ANCHORS_OPT", 22: "ANCHORS_ZERO_FEE_HTLC_REQ", 23: "ANCHORS_ZERO_FEE_HTLC_OPT", + 24: "ROUTE_BLINDING_REQUIRED", + 25: "ROUTE_BLINDING_OPTIONAL", 30: "AMP_REQ", 31: "AMP_OPT", } @@ -708,6 +712,8 @@ var ( "ANCHORS_OPT": 21, "ANCHORS_ZERO_FEE_HTLC_REQ": 22, "ANCHORS_ZERO_FEE_HTLC_OPT": 23, + "ROUTE_BLINDING_REQUIRED": 24, + "ROUTE_BLINDING_OPTIONAL": 25, "AMP_REQ": 30, "AMP_OPT": 31, } @@ -20669,7 +20675,7 @@ var file_lightning_proto_rawDesc = []byte{ 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, 0x2a, 0xcf, 0x04, 0x0a, 0x0a, 0x46, 0x65, + 0x42, 0x41, 0x4c, 0x41, 0x4e, 0x43, 0x45, 0x10, 0x05, 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, @@ -20705,338 +20711,342 @@ var file_lightning_proto_rawDesc = []byte{ 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, 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, 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, + 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, 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, - 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, + 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 162dc35f9a3..2ad22e08934 100644 --- a/lnrpc/lightning.proto +++ b/lnrpc/lightning.proto @@ -4311,6 +4311,8 @@ enum FeatureBit { ANCHORS_OPT = 21; ANCHORS_ZERO_FEE_HTLC_REQ = 22; ANCHORS_ZERO_FEE_HTLC_OPT = 23; + ROUTE_BLINDING_REQUIRED = 24; + ROUTE_BLINDING_OPTIONAL = 25; AMP_REQ = 30; AMP_OPT = 31; } diff --git a/lnrpc/lightning.swagger.json b/lnrpc/lightning.swagger.json index c0ea92d8219..3a7132fc0b5 100644 --- a/lnrpc/lightning.swagger.json +++ b/lnrpc/lightning.swagger.json @@ -1470,6 +1470,8 @@ "ANCHORS_OPT", "ANCHORS_ZERO_FEE_HTLC_REQ", "ANCHORS_ZERO_FEE_HTLC_OPT", + "ROUTE_BLINDING_REQUIRED", + "ROUTE_BLINDING_OPTIONAL", "AMP_REQ", "AMP_OPT" ] @@ -4694,6 +4696,8 @@ "ANCHORS_OPT", "ANCHORS_ZERO_FEE_HTLC_REQ", "ANCHORS_ZERO_FEE_HTLC_OPT", + "ROUTE_BLINDING_REQUIRED", + "ROUTE_BLINDING_OPTIONAL", "AMP_REQ", "AMP_OPT" ], diff --git a/lnrpc/peersrpc/peers.swagger.json b/lnrpc/peersrpc/peers.swagger.json index c7b9970cf6d..b667994a4f1 100644 --- a/lnrpc/peersrpc/peers.swagger.json +++ b/lnrpc/peersrpc/peers.swagger.json @@ -77,6 +77,8 @@ "ANCHORS_OPT", "ANCHORS_ZERO_FEE_HTLC_REQ", "ANCHORS_ZERO_FEE_HTLC_OPT", + "ROUTE_BLINDING_REQUIRED", + "ROUTE_BLINDING_OPTIONAL", "AMP_REQ", "AMP_OPT" ], diff --git a/lnrpc/routerrpc/router.swagger.json b/lnrpc/routerrpc/router.swagger.json index 8e1c646c131..1d948d3f89e 100644 --- a/lnrpc/routerrpc/router.swagger.json +++ b/lnrpc/routerrpc/router.swagger.json @@ -772,6 +772,8 @@ "ANCHORS_OPT", "ANCHORS_ZERO_FEE_HTLC_REQ", "ANCHORS_ZERO_FEE_HTLC_OPT", + "ROUTE_BLINDING_REQUIRED", + "ROUTE_BLINDING_OPTIONAL", "AMP_REQ", "AMP_OPT" ], diff --git a/lnwire/features.go b/lnwire/features.go index ab6facc75fd..e4dd7f4f81c 100644 --- a/lnwire/features.go +++ b/lnwire/features.go @@ -141,6 +141,14 @@ const ( // transactions, which also imply anchor commitments. AnchorsZeroFeeHtlcTxOptional FeatureBit = 23 + // RouteBlindingRequired is a required feature bit that signals that + // the node supports blinded payments. + RouteBlindingRequired FeatureBit = 24 + + // RouteBlindingOptional is an optional feature bit that signals that + // the node supports blinded payments. + RouteBlindingOptional FeatureBit = 25 + // ShutdownAnySegwitRequired is an required feature bit that signals // that the sender is able to properly handle/parse segwit witness // programs up to version 16. This enables utilization of Taproot @@ -315,6 +323,8 @@ var Features = map[FeatureBit]string{ ScidAliasOptional: "scid-alias", ZeroConfRequired: "zero-conf", ZeroConfOptional: "zero-conf", + RouteBlindingRequired: "route-blinding", + RouteBlindingOptional: "route-blinding", ShutdownAnySegwitRequired: "shutdown-any-segwit", ShutdownAnySegwitOptional: "shutdown-any-segwit", SimpleTaprootChannelsRequiredFinal: "simple-taproot-chans", diff --git a/server.go b/server.go index 2979880a291..43390ca574e 100644 --- a/server.go +++ b/server.go @@ -549,6 +549,7 @@ func newServer(cfg *Config, listenAddrs []net.Addr, NoAnySegwit: cfg.ProtocolOptions.NoAnySegwit(), CustomFeatures: cfg.ProtocolOptions.ExperimentalProtocol.CustomFeatures(), NoTaprootChans: !cfg.ProtocolOptions.TaprootChans, + NoRouteBlinding: cfg.ProtocolOptions.NoRouteBlinding(), }) if err != nil { return nil, err From 7867cb3a70bd44c651c594ed51142fff40981762 Mon Sep 17 00:00:00 2001 From: Carla Kirk-Cohen Date: Fri, 22 Mar 2024 13:47:00 -0400 Subject: [PATCH 16/17] multi: turn on route blinding by default --- config.go | 5 ++--- lncfg/protocol.go | 8 -------- lncfg/protocol_integration.go | 7 ------- 3 files changed, 2 insertions(+), 18 deletions(-) diff --git a/config.go b/config.go index 7084500b4bb..6c8df62dda5 100644 --- a/config.go +++ b/config.go @@ -627,9 +627,8 @@ func DefaultConfig() Config { RejectCacheSize: channeldb.DefaultRejectCacheSize, ChannelCacheSize: channeldb.DefaultChannelCacheSize, }, - Prometheus: lncfg.DefaultPrometheus(), - Watchtower: lncfg.DefaultWatchtowerCfg(defaultTowerDir), - ProtocolOptions: lncfg.DefaultProtocol(), + Prometheus: lncfg.DefaultPrometheus(), + Watchtower: lncfg.DefaultWatchtowerCfg(defaultTowerDir), HealthChecks: &lncfg.HealthCheckConfig{ ChainCheck: &lncfg.CheckConfig{ Interval: defaultChainInterval, diff --git a/lncfg/protocol.go b/lncfg/protocol.go index 59027a09b75..e98b4dcf885 100644 --- a/lncfg/protocol.go +++ b/lncfg/protocol.go @@ -59,14 +59,6 @@ type ProtocolOptions struct { NoRouteBlindingOption bool `long:"no-route-blinding" description:"do not forward payments that are a part of a blinded route"` } -// DefaultProtocol returns a protocol config with route blinding turned off, -// temporarily in place until full handling of blinded route errors is merged. -func DefaultProtocol() *ProtocolOptions { - return &ProtocolOptions{ - NoRouteBlindingOption: true, - } -} - // Wumbo returns true if lnd should permit the creation and acceptance of wumbo // channels. func (l *ProtocolOptions) Wumbo() bool { diff --git a/lncfg/protocol_integration.go b/lncfg/protocol_integration.go index f44aa124693..841f8e9eb67 100644 --- a/lncfg/protocol_integration.go +++ b/lncfg/protocol_integration.go @@ -62,13 +62,6 @@ type ProtocolOptions struct { NoRouteBlindingOption bool `long:"no-route-blinding" description:"do not forward payments that are a part of a blinded route"` } -// DefaultProtocol returns a protocol config with route blinding turned on, -// so that itests can run against route blinding features even while we've -// got it turned off for the daemon (pending completion of error handling). -func DefaultProtocol() *ProtocolOptions { - return &ProtocolOptions{} -} - // Wumbo returns true if lnd should permit the creation and acceptance of wumbo // channels. func (l *ProtocolOptions) Wumbo() bool { From 6572f5cb74a589bc73eb64a7a71c91ddaae2ebf8 Mon Sep 17 00:00:00 2001 From: Carla Kirk-Cohen Date: Tue, 20 Feb 2024 19:43:56 -0500 Subject: [PATCH 17/17] docs: add error handling link --- docs/release-notes/release-notes-0.18.0.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/docs/release-notes/release-notes-0.18.0.md b/docs/release-notes/release-notes-0.18.0.md index ff4a947d868..3135764ff7e 100644 --- a/docs/release-notes/release-notes-0.18.0.md +++ b/docs/release-notes/release-notes-0.18.0.md @@ -211,8 +211,11 @@ bitcoin peers' feefilter values into account](https://github.com/lightningnetwor * [Preparatory work](https://github.com/lightningnetwork/lnd/pull/8159) for forwarding of blinded routes was added, along with [support](https://github.com/lightningnetwork/lnd/pull/8160) - for forwarding blinded payments. Forwarding of blinded payments is disabled - by default, and the feature is not yet advertised to the network. + for forwarding blinded payments and [error handling](https://github.com/lightningnetwork/lnd/pull/8485). + With this change, LND is now eligible to be selected as part of a blinded + route and can forward payments on behalf of nodes that have support for + receiving to blinded paths. This upgrade provides a meaningful improvement + to the anonymity set and usability of blinded paths in the Lightning Network. * Introduced [fee bumper](https://github.com/lightningnetwork/lnd/pull/8424) to handle bumping the fees of sweeping transactions properly. A