From 569f82114292b61170854570833986445c0bc73a Mon Sep 17 00:00:00 2001 From: Joost Jager Date: Thu, 31 Jan 2019 16:07:30 +0100 Subject: [PATCH 1/7] routing/test: allow arbitrary graph source nodes --- routing/pathfind_test.go | 35 ++++++++++++++++++++++++----------- routing/router_test.go | 22 ++++++++++++++++------ 2 files changed, 40 insertions(+), 17 deletions(-) diff --git a/routing/pathfind_test.go b/routing/pathfind_test.go index 8a5cf0687..88adade42 100644 --- a/routing/pathfind_test.go +++ b/routing/pathfind_test.go @@ -386,7 +386,9 @@ type testGraphInstance struct { // a deterministical way and added to the channel graph. A list of nodes is // not required and derived from the channel data. The goal is to keep // instantiating a test channel graph as light weight as possible. -func createTestGraphFromChannels(testChannels []*testChannel) (*testGraphInstance, error) { +func createTestGraphFromChannels(testChannels []*testChannel, source string) ( + *testGraphInstance, error) { + // We'll use this fake address for the IP address of all the nodes in // our tests. This value isn't needed for path finding so it doesn't // need to be unique. @@ -444,13 +446,13 @@ func createTestGraphFromChannels(testChannels []*testChannel) (*testGraphInstanc return dbNode, nil } - var source *channeldb.LightningNode - if source, err = addNodeWithAlias("roasbeef"); err != nil { + // Add the source node. + dbNode, err := addNodeWithAlias(source) + if err != nil { return nil, err } - // Set the source node - if err := graph.SetSourceNode(source); err != nil { + if err = graph.SetSourceNode(dbNode); err != nil { return nil, err } @@ -464,7 +466,10 @@ func createTestGraphFromChannels(testChannels []*testChannel) (*testGraphInstanc _, exists := aliasMap[alias] if !exists { - addNodeWithAlias(alias) + _, err := addNodeWithAlias(alias) + if err != nil { + return nil, err + } } } @@ -619,7 +624,9 @@ func TestFindLowestFeePath(t *testing.T) { }), } - testGraphInstance, err := createTestGraphFromChannels(testChannels) + testGraphInstance, err := createTestGraphFromChannels( + testChannels, "roasbeef", + ) if err != nil { t.Fatalf("unable to create graph: %v", err) } @@ -1367,7 +1374,7 @@ func TestRouteFailMaxHTLC(t *testing.T) { }), } - graph, err := createTestGraphFromChannels(testChannels) + graph, err := createTestGraphFromChannels(testChannels, "roasbeef") if err != nil { t.Fatalf("unable to create graph: %v", err) } @@ -1862,7 +1869,9 @@ func TestRestrictOutgoingChannel(t *testing.T) { }), } - testGraphInstance, err := createTestGraphFromChannels(testChannels) + testGraphInstance, err := createTestGraphFromChannels( + testChannels, "roasbeef", + ) if err != nil { t.Fatalf("unable to create graph: %v", err) } @@ -1957,7 +1966,9 @@ func testCltvLimit(t *testing.T, limit uint32, expectedChannel uint64) { }), } - testGraphInstance, err := createTestGraphFromChannels(testChannels) + testGraphInstance, err := createTestGraphFromChannels( + testChannels, "roasbeef", + ) if err != nil { t.Fatalf("unable to create graph: %v", err) } @@ -2121,7 +2132,9 @@ func testProbabilityRouting(t *testing.T, p10, p11, p20, minProbability float64, }, 20), } - testGraphInstance, err := createTestGraphFromChannels(testChannels) + testGraphInstance, err := createTestGraphFromChannels( + testChannels, "roasbeef", + ) if err != nil { t.Fatalf("unable to create graph: %v", err) } diff --git a/routing/router_test.go b/routing/router_test.go index 4a9d885e4..7f2c51888 100644 --- a/routing/router_test.go +++ b/routing/router_test.go @@ -342,7 +342,7 @@ func TestChannelUpdateValidation(t *testing.T) { }, 2), } - testGraph, err := createTestGraphFromChannels(testChannels) + testGraph, err := createTestGraphFromChannels(testChannels, "a") defer testGraph.cleanUp() if err != nil { t.Fatalf("unable to create graph: %v", err) @@ -1039,7 +1039,9 @@ func TestIgnoreChannelEdgePolicyForUnknownChannel(t *testing.T) { // Setup an initially empty network. testChannels := []*testChannel{} - testGraph, err := createTestGraphFromChannels(testChannels) + testGraph, err := createTestGraphFromChannels( + testChannels, "roasbeef", + ) if err != nil { t.Fatalf("unable to create graph: %v", err) } @@ -2027,7 +2029,7 @@ func TestPruneChannelGraphStaleEdges(t *testing.T) { // We'll create our test graph and router backed with these test // channels we've created. - testGraph, err := createTestGraphFromChannels(testChannels) + testGraph, err := createTestGraphFromChannels(testChannels, "a") if err != nil { t.Fatalf("unable to create test graph: %v", err) } @@ -2064,6 +2066,14 @@ func TestPruneChannelGraphDoubleDisabled(t *testing.T) { // according to that heuristic. timestamp := time.Now() testChannels := []*testChannel{ + // Channel from self shouldn't be pruned. + symmetricTestChannel( + "self", "a", 100000, &testChannelPolicy{ + LastUpdate: timestamp, + Disabled: true, + }, 99, + ), + // No edges. { Node1: &testChannelEnd{Alias: "a"}, @@ -2135,7 +2145,7 @@ func TestPruneChannelGraphDoubleDisabled(t *testing.T) { // We'll create our test graph and router backed with these test // channels we've created. - testGraph, err := createTestGraphFromChannels(testChannels) + testGraph, err := createTestGraphFromChannels(testChannels, "self") if err != nil { t.Fatalf("unable to create test graph: %v", err) } @@ -2543,7 +2553,7 @@ func TestRouterPaymentStateMachine(t *testing.T) { }, 2), } - testGraph, err := createTestGraphFromChannels(testChannels) + testGraph, err := createTestGraphFromChannels(testChannels, "a") if err != nil { t.Fatalf("unable to create graph: %v", err) } @@ -3164,7 +3174,7 @@ func TestSendToRouteStructuredError(t *testing.T) { }, 2), } - testGraph, err := createTestGraphFromChannels(testChannels) + testGraph, err := createTestGraphFromChannels(testChannels, "a") if err != nil { t.Fatalf("unable to create graph: %v", err) } From 55bef96cf3f883a7f35253b9bff6d10da4f7d408 Mon Sep 17 00:00:00 2001 From: Joost Jager Date: Mon, 6 May 2019 15:05:14 +0200 Subject: [PATCH 2/7] htlcswitch/test: stricter error check in TestSwitchSendPayment --- htlcswitch/switch_test.go | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/htlcswitch/switch_test.go b/htlcswitch/switch_test.go index 6a0a24e9d..2571342a4 100644 --- a/htlcswitch/switch_test.go +++ b/htlcswitch/switch_test.go @@ -6,7 +6,6 @@ import ( "fmt" "io" "io/ioutil" - "strings" "testing" "time" @@ -1825,9 +1824,13 @@ func TestSwitchSendPayment(t *testing.T) { select { case err := <-errChan: - if !strings.Contains(err.Error(), lnwire.CodeUnknownPaymentHash.String()) { - t.Fatalf("expected %v got %v", err, - lnwire.CodeUnknownPaymentHash) + fErr, ok := err.(*ForwardingError) + if !ok { + t.Fatal("expected ForwardingError") + } + + if _, ok := fErr.FailureMessage.(*lnwire.FailUnknownPaymentHash); !ok { + t.Fatalf("expected UnknownPaymentHash got %v", fErr) } case <-time.After(time.Second): t.Fatal("err wasn't received") From 2b47632b26bfd99285528309b849ff5e278bdfce Mon Sep 17 00:00:00 2001 From: Joost Jager Date: Tue, 11 Jun 2019 10:24:19 +0200 Subject: [PATCH 3/7] htlcswitch+routing+routerrpc: return error source as index --- go.mod | 2 +- go.sum | 4 +- htlcswitch/failure.go | 19 +-- htlcswitch/mock.go | 10 +- htlcswitch/switch.go | 44 +++--- lnrpc/routerrpc/router.pb.go | 231 ++++++++++++++++--------------- lnrpc/routerrpc/router.proto | 12 +- lnrpc/routerrpc/router_server.go | 2 +- routing/router.go | 130 +++++++++-------- routing/router_test.go | 98 ++----------- server.go | 3 +- 11 files changed, 245 insertions(+), 310 deletions(-) diff --git a/go.mod b/go.mod index 5f39f0b9e..ef704a0f8 100644 --- a/go.mod +++ b/go.mod @@ -30,7 +30,7 @@ require ( github.com/juju/version v0.0.0-20180108022336-b64dbd566305 // indirect github.com/kkdai/bstream v0.0.0-20181106074824-b3251f7901ec github.com/lightninglabs/neutrino v0.0.0-20190629001446-52dd89dd1aaa - github.com/lightningnetwork/lightning-onion v0.0.0-20190430041606-751fb4dd8b72 + github.com/lightningnetwork/lightning-onion v0.0.0-20190703000913-ecc936dc56c9 github.com/lightningnetwork/lnd/queue v1.0.1 github.com/lightningnetwork/lnd/ticker v1.0.0 github.com/ltcsuite/ltcd v0.0.0-20190101042124-f37f8bf35796 diff --git a/go.sum b/go.sum index ef56979b0..1c69b0767 100644 --- a/go.sum +++ b/go.sum @@ -137,8 +137,8 @@ github.com/lightninglabs/neutrino v0.0.0-20190313035638-e1ad4c33fb18 h1:lxD7RgKY github.com/lightninglabs/neutrino v0.0.0-20190313035638-e1ad4c33fb18/go.mod h1:v6tz6jbuAubTrRpX8ke2KH9sJxml8KlPQTKgo9mAp1Q= github.com/lightninglabs/neutrino v0.0.0-20190629001446-52dd89dd1aaa h1:vLsdHrqHKNDShoSRkrFEM0FsNt5waIKS87IgP1YLz+g= github.com/lightninglabs/neutrino v0.0.0-20190629001446-52dd89dd1aaa/go.mod h1:vzLU75ll8qbRJIzW5dvK/UXtR9c2FecJ6VNOM8chyVM= -github.com/lightningnetwork/lightning-onion v0.0.0-20190430041606-751fb4dd8b72 h1:KgmypyQfJnEf2vhwboKCtTp4mHxIcLeXPBPWDbPuzFQ= -github.com/lightningnetwork/lightning-onion v0.0.0-20190430041606-751fb4dd8b72/go.mod h1:Sooe/CoCqa85JxqHV+IBR2HW+6t2Cv+36awSmoccswM= +github.com/lightningnetwork/lightning-onion v0.0.0-20190703000913-ecc936dc56c9 h1:u6dbtgPtilk/HWg9GwA8GniHqzCW/7an3ZSpZARfHx4= +github.com/lightningnetwork/lightning-onion v0.0.0-20190703000913-ecc936dc56c9/go.mod h1:Sooe/CoCqa85JxqHV+IBR2HW+6t2Cv+36awSmoccswM= github.com/ltcsuite/ltcd v0.0.0-20190101042124-f37f8bf35796 h1:sjOGyegMIhvgfq5oaue6Td+hxZuf3tDC8lAPrFldqFw= github.com/ltcsuite/ltcd v0.0.0-20190101042124-f37f8bf35796/go.mod h1:3p7ZTf9V1sNPI5H8P3NkTFF4LuwMdPl2DodF60qAKqY= github.com/ltcsuite/ltcutil v0.0.0-20181217130922-17f3b04680b6/go.mod h1:8Vg/LTOO0KYa/vlHWJ6XZAevPQThGH5sufO0Hrou/lA= diff --git a/htlcswitch/failure.go b/htlcswitch/failure.go index a17517bb7..ffd3b18dd 100644 --- a/htlcswitch/failure.go +++ b/htlcswitch/failure.go @@ -6,17 +6,18 @@ import ( "io" "github.com/btcsuite/btcd/btcec" - "github.com/lightningnetwork/lightning-onion" + sphinx "github.com/lightningnetwork/lightning-onion" "github.com/lightningnetwork/lnd/lnwire" ) // ForwardingError wraps an lnwire.FailureMessage in a struct that also // includes the source of the error. type ForwardingError struct { - // ErrorSource is the public key of the node that sent the error. With - // this information, the dispatcher of a payment can modify their set - // of candidate routes in response to the type of error extracted. - ErrorSource *btcec.PublicKey + // FailureSourceIdx is the index of the node that sent the failure. With + // this information, the dispatcher of a payment can modify their set of + // candidate routes in response to the type of failure extracted. Index + // zero is the self node. + FailureSourceIdx int // ExtraMsg is an additional error message that callers can provide in // order to provide context specific error details. @@ -256,20 +257,20 @@ type SphinxErrorDecrypter struct { // NOTE: Part of the ErrorDecrypter interface. func (s *SphinxErrorDecrypter) DecryptError(reason lnwire.OpaqueReason) (*ForwardingError, error) { - source, failureData, err := s.OnionErrorDecrypter.DecryptError(reason) + failure, err := s.OnionErrorDecrypter.DecryptError(reason) if err != nil { return nil, err } - r := bytes.NewReader(failureData) + r := bytes.NewReader(failure.Message) failureMsg, err := lnwire.DecodeFailure(r, 0) if err != nil { return nil, err } return &ForwardingError{ - ErrorSource: source, - FailureMessage: failureMsg, + FailureSourceIdx: failure.SenderIdx, + FailureMessage: failureMsg, }, nil } diff --git a/htlcswitch/mock.go b/htlcswitch/mock.go index 1f681264a..89c053960 100644 --- a/htlcswitch/mock.go +++ b/htlcswitch/mock.go @@ -163,10 +163,7 @@ func initSwitchWithDB(startingHeight uint32, db *channeldb.DB) (*Switch, error) } } - priv, _ := btcec.NewPrivateKey(btcec.S256()) - pubkey := priv.PubKey() cfg := Config{ - SelfKey: pubkey, DB: db, SwitchPackager: channeldb.NewSwitchPackager(), FwdingLog: &mockForwardingLog{ @@ -393,12 +390,9 @@ func (o *mockDeobfuscator) DecryptError(reason lnwire.OpaqueReason) (*Forwarding return nil, err } - priv, _ := btcec.NewPrivateKey(btcec.S256()) - pubkey := priv.PubKey() - return &ForwardingError{ - ErrorSource: pubkey, - FailureMessage: failure, + FailureSourceIdx: 1, + FailureMessage: failure, }, nil } diff --git a/htlcswitch/switch.go b/htlcswitch/switch.go index ee2c090a2..a86767452 100644 --- a/htlcswitch/switch.go +++ b/htlcswitch/switch.go @@ -9,7 +9,6 @@ import ( "sync/atomic" "time" - "github.com/btcsuite/btcd/btcec" "github.com/btcsuite/btcd/wire" "github.com/btcsuite/btcutil" "github.com/coreos/bbolt" @@ -112,11 +111,6 @@ type ChanClose struct { // Config defines the configuration for the service. ALL elements within the // configuration MUST be non-nil for the service to carry out its duties. type Config struct { - // SelfKey is the key of the backing Lightning node. This key is used - // to properly craft failure messages, such that the Layer 3 router can - // properly route around link./vertex failures. - SelfKey *btcec.PublicKey - // FwdingLog is an interface that will be used by the switch to log // forwarding events. A forwarding event happens each time a payment // circuit is successfully completed. So when we forward an HTLC, and a @@ -776,8 +770,8 @@ func (s *Switch) handleLocalDispatch(pkt *htlcPacket) error { if err != nil { log.Errorf("Link %v not found", pkt.outgoingChanID) return &ForwardingError{ - ErrorSource: s.cfg.SelfKey, - FailureMessage: &lnwire.FailUnknownNextPeer{}, + FailureSourceIdx: 0, + FailureMessage: &lnwire.FailUnknownNextPeer{}, } } @@ -790,9 +784,9 @@ func (s *Switch) handleLocalDispatch(pkt *htlcPacket) error { // will be returned back to the router. htlcErr := lnwire.NewTemporaryChannelFailure(nil) return &ForwardingError{ - ErrorSource: s.cfg.SelfKey, - ExtraMsg: err.Error(), - FailureMessage: htlcErr, + FailureSourceIdx: 0, + ExtraMsg: err.Error(), + FailureMessage: htlcErr, } } @@ -808,8 +802,8 @@ func (s *Switch) handleLocalDispatch(pkt *htlcPacket) error { "satisfied", pkt.outgoingChanID) return &ForwardingError{ - ErrorSource: s.cfg.SelfKey, - FailureMessage: htlcErr, + FailureSourceIdx: 0, + FailureMessage: htlcErr, } } @@ -823,9 +817,9 @@ func (s *Switch) handleLocalDispatch(pkt *htlcPacket) error { // will be returned back to the router. htlcErr := lnwire.NewTemporaryChannelFailure(nil) return &ForwardingError{ - ErrorSource: s.cfg.SelfKey, - ExtraMsg: err.Error(), - FailureMessage: htlcErr, + FailureSourceIdx: 0, + ExtraMsg: err.Error(), + FailureMessage: htlcErr, } } @@ -967,9 +961,9 @@ func (s *Switch) parseFailedPayment(deobfuscator ErrorDecrypter, failureMsg = lnwire.NewTemporaryChannelFailure(nil) } failure = &ForwardingError{ - ErrorSource: s.cfg.SelfKey, - ExtraMsg: userErr, - FailureMessage: failureMsg, + FailureSourceIdx: 0, + ExtraMsg: userErr, + FailureMessage: failureMsg, } // A payment had to be timed out on chain before it got past @@ -981,9 +975,9 @@ func (s *Switch) parseFailedPayment(deobfuscator ErrorDecrypter, "on-chain, then cancelled back (hash=%v, pid=%d)", paymentHash, paymentID) failure = &ForwardingError{ - ErrorSource: s.cfg.SelfKey, - ExtraMsg: userErr, - FailureMessage: lnwire.FailPermanentChannelFailure{}, + FailureSourceIdx: 0, + ExtraMsg: userErr, + FailureMessage: lnwire.FailPermanentChannelFailure{}, } // A regular multi-hop payment error that we'll need to @@ -999,9 +993,9 @@ func (s *Switch) parseFailedPayment(deobfuscator ErrorDecrypter, paymentHash, paymentID, err) log.Error(userErr) failure = &ForwardingError{ - ErrorSource: s.cfg.SelfKey, - ExtraMsg: userErr, - FailureMessage: lnwire.NewTemporaryChannelFailure(nil), + FailureSourceIdx: 0, + ExtraMsg: userErr, + FailureMessage: lnwire.NewTemporaryChannelFailure(nil), } } } diff --git a/lnrpc/routerrpc/router.pb.go b/lnrpc/routerrpc/router.pb.go index 6249d28bc..57217669d 100644 --- a/lnrpc/routerrpc/router.pb.go +++ b/lnrpc/routerrpc/router.pb.go @@ -610,10 +610,6 @@ func (m *SendToRouteResponse) GetFailure() *Failure { type Failure struct { /// Failure code as defined in the Lightning spec Code Failure_FailureCode `protobuf:"varint,1,opt,name=code,proto3,enum=routerrpc.Failure_FailureCode" json:"code,omitempty"` - //* - //The node pubkey of the intermediate or final node that generated the failure - //message. - FailureSourcePubkey []byte `protobuf:"bytes,2,opt,name=failure_source_pubkey,json=failureSourcePubkey,proto3" json:"failure_source_pubkey,omitempty"` /// An optional channel update message. ChannelUpdate *ChannelUpdate `protobuf:"bytes,3,opt,name=channel_update,json=channelUpdate,proto3" json:"channel_update,omitempty"` /// A failure type-dependent htlc value. @@ -623,7 +619,11 @@ type Failure struct { /// A failure type-dependent cltv expiry value. CltvExpiry uint32 `protobuf:"varint,6,opt,name=cltv_expiry,json=cltvExpiry,proto3" json:"cltv_expiry,omitempty"` /// A failure type-dependent flags value. - Flags uint32 `protobuf:"varint,7,opt,name=flags,proto3" json:"flags,omitempty"` + Flags uint32 `protobuf:"varint,7,opt,name=flags,proto3" json:"flags,omitempty"` + //* + //The position in the path of the intermediate or final node that generated + //the failure message. Position zero is the sender node. + FailureSourceIndex uint32 `protobuf:"varint,8,opt,name=failure_source_index,json=failureSourceIndex,proto3" json:"failure_source_index,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -661,13 +661,6 @@ func (m *Failure) GetCode() Failure_FailureCode { return Failure_RESERVED } -func (m *Failure) GetFailureSourcePubkey() []byte { - if m != nil { - return m.FailureSourcePubkey - } - return nil -} - func (m *Failure) GetChannelUpdate() *ChannelUpdate { if m != nil { return m.ChannelUpdate @@ -703,6 +696,13 @@ func (m *Failure) GetFlags() uint32 { return 0 } +func (m *Failure) GetFailureSourceIndex() uint32 { + if m != nil { + return m.FailureSourceIndex + } + return 0 +} + type ChannelUpdate struct { //* //The signature that validates the announced data and proves the ownership @@ -1167,109 +1167,110 @@ func init() { func init() { proto.RegisterFile("routerrpc/router.proto", fileDescriptor_7a0613f69d37b0a5) } var fileDescriptor_7a0613f69d37b0a5 = []byte{ - // 1623 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x57, 0xdd, 0x72, 0x1a, 0xc9, - 0x15, 0x5e, 0x04, 0x08, 0x71, 0xf8, 0x1b, 0xb5, 0x64, 0x79, 0x8c, 0x2c, 0xaf, 0x76, 0x92, 0x78, - 0x55, 0xae, 0x8d, 0x94, 0x90, 0xf2, 0xd6, 0x5e, 0x25, 0x85, 0xa1, 0x59, 0x26, 0x86, 0x19, 0x6d, - 0x03, 0xde, 0x75, 0x72, 0xd1, 0xd5, 0x82, 0x16, 0x4c, 0x09, 0x66, 0xd8, 0x99, 0x66, 0x63, 0xe5, - 0x22, 0x77, 0xc9, 0x5d, 0x9e, 0x25, 0x79, 0x82, 0xbc, 0x47, 0x5e, 0x25, 0x57, 0xa9, 0xee, 0x1e, - 0x60, 0x40, 0xc8, 0xeb, 0x2b, 0x31, 0xdf, 0xf9, 0xfa, 0x9c, 0xd3, 0xe7, 0xaf, 0x8f, 0xe0, 0x24, - 0x0c, 0x16, 0x82, 0x87, 0xe1, 0x7c, 0x78, 0xa5, 0x7f, 0x5d, 0xce, 0xc3, 0x40, 0x04, 0x28, 0xbf, - 0xc2, 0xab, 0xf9, 0x70, 0x3e, 0xd4, 0xa8, 0xf5, 0xbf, 0x3d, 0x40, 0x3d, 0xee, 0x8f, 0xae, 0xd9, - 0xfd, 0x8c, 0xfb, 0x82, 0xf0, 0x1f, 0x17, 0x3c, 0x12, 0x08, 0x41, 0x66, 0xc4, 0x23, 0x61, 0xa6, - 0xce, 0x53, 0x17, 0x45, 0xa2, 0x7e, 0x23, 0x03, 0xd2, 0x6c, 0x26, 0xcc, 0xbd, 0xf3, 0xd4, 0x45, - 0x9a, 0xc8, 0x9f, 0xe8, 0x0b, 0x28, 0xce, 0xf5, 0x39, 0x3a, 0x61, 0xd1, 0xc4, 0x4c, 0x2b, 0x76, - 0x21, 0xc6, 0xda, 0x2c, 0x9a, 0xa0, 0x0b, 0x30, 0x6e, 0x3d, 0x9f, 0x4d, 0xe9, 0x70, 0x2a, 0x7e, - 0xa2, 0x23, 0x3e, 0x15, 0xcc, 0xcc, 0x9c, 0xa7, 0x2e, 0xb2, 0xa4, 0xac, 0xf0, 0xc6, 0x54, 0xfc, - 0xd4, 0x94, 0x28, 0xfa, 0x12, 0x2a, 0x4b, 0x65, 0xa1, 0xf6, 0xc2, 0xcc, 0x9e, 0xa7, 0x2e, 0xf2, - 0xa4, 0x3c, 0xdf, 0xf4, 0xed, 0x4b, 0xa8, 0x08, 0x6f, 0xc6, 0x83, 0x85, 0xa0, 0x11, 0x1f, 0x06, - 0xfe, 0x28, 0x32, 0xf7, 0xb5, 0xc6, 0x18, 0xee, 0x69, 0x14, 0x59, 0x50, 0xba, 0xe5, 0x9c, 0x4e, - 0xbd, 0x99, 0x27, 0x68, 0xc4, 0x84, 0x99, 0x53, 0xae, 0x17, 0x6e, 0x39, 0xef, 0x48, 0xac, 0xc7, - 0x84, 0xf4, 0x2f, 0x58, 0x88, 0x71, 0xe0, 0xf9, 0x63, 0x3a, 0x9c, 0x30, 0x9f, 0x7a, 0x23, 0xf3, - 0xe0, 0x3c, 0x75, 0x91, 0x21, 0xe5, 0x25, 0xde, 0x98, 0x30, 0xdf, 0x1e, 0xa1, 0x33, 0x00, 0x75, - 0x07, 0xa5, 0xce, 0xcc, 0x2b, 0x8b, 0x79, 0x89, 0x28, 0x5d, 0xa8, 0x06, 0x05, 0x15, 0x60, 0x3a, - 0xf1, 0x7c, 0x11, 0x99, 0x70, 0x9e, 0xbe, 0x28, 0xd4, 0x8c, 0xcb, 0xa9, 0x2f, 0x63, 0x4d, 0xa4, - 0xa4, 0xed, 0xf9, 0x82, 0x24, 0x49, 0xd6, 0x37, 0x70, 0xd4, 0x0f, 0xd9, 0xf0, 0x6e, 0x2b, 0xf8, - 0xdb, 0x61, 0x4d, 0x3d, 0x08, 0xab, 0xf5, 0x37, 0x28, 0xc5, 0x87, 0x7a, 0x82, 0x89, 0x45, 0x84, - 0x7e, 0x0d, 0xd9, 0x48, 0x30, 0xc1, 0x15, 0xb9, 0x5c, 0x7b, 0x7a, 0xb9, 0xca, 0xf6, 0x65, 0x82, - 0xc8, 0x89, 0x66, 0xa1, 0x2a, 0x1c, 0xcc, 0x43, 0xee, 0xcd, 0xd8, 0x98, 0xab, 0x84, 0x16, 0xc9, - 0xea, 0x1b, 0x59, 0x90, 0x55, 0x87, 0x55, 0x3a, 0x0b, 0xb5, 0x62, 0xf2, 0x0e, 0x44, 0x8b, 0xac, - 0xdf, 0x43, 0x45, 0x7d, 0xb7, 0x38, 0xff, 0x58, 0xc9, 0x3c, 0x85, 0x1c, 0x9b, 0xe9, 0xd8, 0xeb, - 0xb2, 0xd9, 0x67, 0x33, 0x19, 0x76, 0x6b, 0x04, 0xc6, 0xfa, 0x7c, 0x34, 0x0f, 0xfc, 0x88, 0xcb, - 0x54, 0x48, 0xe5, 0x32, 0x13, 0x32, 0x6d, 0x33, 0x79, 0x2a, 0xa5, 0x4e, 0x95, 0x63, 0xbc, 0xc5, - 0x79, 0x37, 0x62, 0x02, 0xbd, 0xd4, 0x15, 0x40, 0xa7, 0xc1, 0xf0, 0x4e, 0xd6, 0x14, 0xbb, 0x8f, - 0xd5, 0x97, 0x24, 0xdc, 0x09, 0x86, 0x77, 0x4d, 0x09, 0x5a, 0x7f, 0xd6, 0xb5, 0xdd, 0x0f, 0xb4, - 0xef, 0x9f, 0x1c, 0xde, 0x75, 0x08, 0xf6, 0x1e, 0x0f, 0x01, 0x85, 0xa3, 0x0d, 0xe5, 0xf1, 0x2d, - 0x92, 0x91, 0x4d, 0x6d, 0x45, 0xf6, 0x2b, 0xc8, 0xdd, 0x32, 0x6f, 0xba, 0x08, 0x97, 0x8a, 0x51, - 0x22, 0x4d, 0x2d, 0x2d, 0x21, 0x4b, 0x8a, 0xf5, 0x8f, 0x1c, 0xe4, 0x62, 0x10, 0xd5, 0x20, 0x33, - 0x0c, 0x46, 0xcb, 0xec, 0xbe, 0x78, 0x78, 0x6c, 0xf9, 0xb7, 0x11, 0x8c, 0x38, 0x51, 0x5c, 0x54, - 0x83, 0x27, 0xb1, 0x2a, 0x1a, 0x05, 0x8b, 0x70, 0xc8, 0xe9, 0x7c, 0x71, 0x73, 0xc7, 0xef, 0xe3, - 0x84, 0x1f, 0xc5, 0xc2, 0x9e, 0x92, 0x5d, 0x2b, 0x11, 0xfa, 0x03, 0x94, 0x65, 0x17, 0xf8, 0x7c, - 0x4a, 0x17, 0xf3, 0x11, 0x5b, 0x15, 0x81, 0x99, 0xb0, 0xd8, 0xd0, 0x84, 0x81, 0x92, 0x93, 0xd2, - 0x30, 0xf9, 0x89, 0x4e, 0x21, 0x3f, 0x11, 0xd3, 0xa1, 0xce, 0x5e, 0x46, 0x35, 0xd2, 0x81, 0x04, - 0x54, 0xde, 0x2c, 0x28, 0x05, 0xbe, 0x17, 0xf8, 0x34, 0x9a, 0x30, 0x5a, 0x7b, 0xfd, 0xb5, 0x6a, - 0xf0, 0x22, 0x29, 0x28, 0xb0, 0x37, 0x61, 0xb5, 0xd7, 0x5f, 0xa3, 0xcf, 0xa1, 0xa0, 0xda, 0x8c, - 0x7f, 0x98, 0x7b, 0xe1, 0xbd, 0xea, 0xec, 0x12, 0x51, 0x9d, 0x87, 0x15, 0x82, 0x8e, 0x21, 0x7b, - 0x3b, 0x65, 0xe3, 0x48, 0x75, 0x73, 0x89, 0xe8, 0x0f, 0xeb, 0xbf, 0x19, 0x28, 0x24, 0x42, 0x80, - 0x8a, 0x70, 0x40, 0x70, 0x0f, 0x93, 0x77, 0xb8, 0x69, 0x7c, 0x86, 0x4c, 0x38, 0x1e, 0x38, 0x6f, - 0x1d, 0xf7, 0x7b, 0x87, 0x5e, 0xd7, 0xdf, 0x77, 0xb1, 0xd3, 0xa7, 0xed, 0x7a, 0xaf, 0x6d, 0xa4, - 0xd0, 0x73, 0x30, 0x6d, 0xa7, 0xe1, 0x12, 0x82, 0x1b, 0xfd, 0x95, 0xac, 0xde, 0x75, 0x07, 0x4e, - 0xdf, 0xd8, 0x43, 0x9f, 0xc3, 0x69, 0xcb, 0x76, 0xea, 0x1d, 0xba, 0xe6, 0x34, 0x3a, 0xfd, 0x77, - 0x14, 0xff, 0x70, 0x6d, 0x93, 0xf7, 0x46, 0x7a, 0x17, 0xa1, 0xdd, 0xef, 0x34, 0x96, 0x1a, 0x32, - 0xe8, 0x19, 0x3c, 0xd1, 0x04, 0x7d, 0x84, 0xf6, 0x5d, 0x97, 0xf6, 0x5c, 0xd7, 0x31, 0xb2, 0xe8, - 0x10, 0x4a, 0xb6, 0xf3, 0xae, 0xde, 0xb1, 0x9b, 0x94, 0xe0, 0x7a, 0xa7, 0x6b, 0xec, 0xa3, 0x23, - 0xa8, 0x6c, 0xf3, 0x72, 0x52, 0xc5, 0x92, 0xe7, 0x3a, 0xb6, 0xeb, 0xd0, 0x77, 0x98, 0xf4, 0x6c, - 0xd7, 0x31, 0x0e, 0xd0, 0x09, 0xa0, 0x4d, 0x51, 0xbb, 0x5b, 0x6f, 0x18, 0x79, 0xf4, 0x04, 0x0e, - 0x37, 0xf1, 0xb7, 0xf8, 0xbd, 0x01, 0x32, 0x0c, 0xda, 0x31, 0xfa, 0x06, 0x77, 0xdc, 0xef, 0x69, - 0xd7, 0x76, 0xec, 0xee, 0xa0, 0x6b, 0x14, 0xd0, 0x31, 0x18, 0x2d, 0x8c, 0xa9, 0xed, 0xf4, 0x06, - 0xad, 0x96, 0xdd, 0xb0, 0xb1, 0xd3, 0x37, 0x8a, 0xda, 0xf2, 0xae, 0x8b, 0x97, 0xe4, 0x81, 0x46, - 0xbb, 0xee, 0x38, 0xb8, 0x43, 0x9b, 0x76, 0xaf, 0xfe, 0xa6, 0x83, 0x9b, 0x46, 0x19, 0x9d, 0xc1, - 0xb3, 0x3e, 0xee, 0x5e, 0xbb, 0xa4, 0x4e, 0xde, 0xd3, 0xa5, 0xbc, 0x55, 0xb7, 0x3b, 0x03, 0x82, - 0x8d, 0x0a, 0xfa, 0x02, 0xce, 0x08, 0xfe, 0x6e, 0x60, 0x13, 0xdc, 0xa4, 0x8e, 0xdb, 0xc4, 0xb4, - 0x85, 0xeb, 0xfd, 0x01, 0xc1, 0xb4, 0x6b, 0xf7, 0x7a, 0xb6, 0xf3, 0xad, 0x61, 0xa0, 0x5f, 0xc2, - 0xf9, 0x8a, 0xb2, 0x52, 0xb0, 0xc5, 0x3a, 0x94, 0xf7, 0x5b, 0xe6, 0xd3, 0xc1, 0x3f, 0xf4, 0xe9, - 0x35, 0xc6, 0xc4, 0x40, 0xa8, 0x0a, 0x27, 0x6b, 0xf3, 0xda, 0x40, 0x6c, 0xfb, 0x48, 0xca, 0xae, - 0x31, 0xe9, 0xd6, 0x1d, 0x99, 0xe0, 0x0d, 0xd9, 0xb1, 0x74, 0x7b, 0x2d, 0xdb, 0x76, 0xfb, 0x89, - 0xf5, 0xaf, 0x34, 0x94, 0x36, 0x8a, 0x1e, 0x3d, 0x87, 0x7c, 0xe4, 0x8d, 0x7d, 0x26, 0x64, 0x2b, - 0xeb, 0x2e, 0x5f, 0x03, 0xea, 0xa5, 0x98, 0x30, 0xcf, 0xd7, 0xe3, 0x45, 0x77, 0x5b, 0x5e, 0x21, - 0x6a, 0xb8, 0x3c, 0x85, 0xdc, 0xf2, 0xa5, 0x49, 0xab, 0x06, 0xd9, 0x1f, 0xea, 0x17, 0xe6, 0x39, - 0xe4, 0xe5, 0xfc, 0x8a, 0x04, 0x9b, 0xcd, 0x55, 0xef, 0x94, 0xc8, 0x1a, 0x40, 0xbf, 0x80, 0xd2, - 0x8c, 0x47, 0x11, 0x1b, 0x73, 0xaa, 0xeb, 0x1f, 0x14, 0xa3, 0x18, 0x83, 0x2d, 0x89, 0x49, 0xd2, - 0xb2, 0x7f, 0x35, 0x29, 0xab, 0x49, 0x31, 0xa8, 0x49, 0xdb, 0xe3, 0x53, 0xb0, 0xb8, 0xcd, 0x92, - 0xe3, 0x53, 0x30, 0xf4, 0x0a, 0x0e, 0x75, 0x2f, 0x7b, 0xbe, 0x37, 0x5b, 0xcc, 0x74, 0x4f, 0xe7, - 0x94, 0xcb, 0x15, 0xd5, 0xd3, 0x1a, 0x57, 0xad, 0xfd, 0x0c, 0x0e, 0x6e, 0x58, 0xc4, 0xe5, 0xe4, - 0x56, 0xef, 0x67, 0x89, 0xe4, 0xe4, 0x77, 0x8b, 0x73, 0x29, 0x92, 0xf3, 0x3c, 0x94, 0xd3, 0x24, - 0xaf, 0x45, 0xb7, 0x9c, 0x13, 0x19, 0xc7, 0x95, 0x05, 0xf6, 0x61, 0x6d, 0xa1, 0x90, 0xb0, 0xa0, - 0x71, 0x65, 0xe1, 0x15, 0x1c, 0xf2, 0x0f, 0x22, 0x64, 0x34, 0x98, 0xb3, 0x1f, 0x17, 0x9c, 0x8e, - 0x98, 0x60, 0x66, 0x51, 0x05, 0xb7, 0xa2, 0x04, 0xae, 0xc2, 0x9b, 0x4c, 0x30, 0xeb, 0x39, 0x54, - 0x09, 0x8f, 0xb8, 0xe8, 0x7a, 0x51, 0xe4, 0x05, 0x7e, 0x23, 0xf0, 0x45, 0x18, 0x4c, 0xe3, 0x07, - 0xc0, 0x3a, 0x83, 0xd3, 0x9d, 0x52, 0x3d, 0xc1, 0xe5, 0xe1, 0xef, 0x16, 0x3c, 0xbc, 0xdf, 0x7d, - 0xf8, 0x2d, 0x9c, 0xee, 0x94, 0xc6, 0xe3, 0xff, 0x2b, 0xc8, 0xfa, 0xc1, 0x88, 0x47, 0x66, 0x4a, - 0x2d, 0x00, 0x27, 0x89, 0xb9, 0xe9, 0x04, 0x23, 0xde, 0xf6, 0x22, 0x11, 0x84, 0xf7, 0x44, 0x93, - 0xac, 0xff, 0xa4, 0xa0, 0x90, 0x80, 0xd1, 0x09, 0xec, 0xc7, 0x33, 0x5a, 0x17, 0x55, 0xfc, 0x85, - 0x5e, 0x42, 0x79, 0xca, 0x22, 0x41, 0xe5, 0xc8, 0xa6, 0x32, 0x49, 0xf1, 0x7b, 0xb7, 0x85, 0xa2, - 0x6f, 0xe0, 0x69, 0x20, 0x26, 0x3c, 0xd4, 0xab, 0x4c, 0xb4, 0x18, 0x0e, 0x79, 0x14, 0xd1, 0x79, - 0x18, 0xdc, 0xa8, 0x52, 0xdb, 0x23, 0x8f, 0x89, 0xd1, 0x6b, 0x38, 0x88, 0x6b, 0x24, 0x32, 0x33, - 0xca, 0xf5, 0x67, 0x0f, 0x47, 0xfe, 0xd2, 0xfb, 0x15, 0xd5, 0xfa, 0x77, 0x0a, 0xca, 0x9b, 0x42, - 0xf4, 0x42, 0x55, 0xbf, 0x2a, 0x41, 0x6f, 0xa4, 0xee, 0x91, 0x21, 0x09, 0xe4, 0x93, 0xef, 0x52, - 0x83, 0xe3, 0x99, 0xe7, 0xd3, 0x39, 0xf7, 0xd9, 0xd4, 0xfb, 0x2b, 0xa7, 0xcb, 0x45, 0x22, 0xad, - 0xd8, 0x3b, 0x65, 0xc8, 0x82, 0xe2, 0xc6, 0xa5, 0x33, 0xea, 0xd2, 0x1b, 0xd8, 0xab, 0x7f, 0xa6, - 0xa0, 0x98, 0x5c, 0x89, 0x50, 0x09, 0xf2, 0xb6, 0x43, 0x5b, 0x1d, 0xfb, 0xdb, 0x76, 0xdf, 0xf8, - 0x4c, 0x7e, 0xf6, 0x06, 0x8d, 0x06, 0xc6, 0x4d, 0xdc, 0x34, 0x52, 0x08, 0x41, 0x59, 0x4e, 0x02, - 0xdc, 0xa4, 0x7d, 0xbb, 0x8b, 0xdd, 0x81, 0x7c, 0x16, 0x8e, 0xa0, 0x12, 0x63, 0x8e, 0x4b, 0x89, - 0x3b, 0xe8, 0x63, 0x23, 0x8d, 0x0c, 0x28, 0xc6, 0x20, 0x26, 0xc4, 0x25, 0x46, 0x46, 0xce, 0xb2, - 0x18, 0x79, 0xf8, 0xc4, 0x34, 0x71, 0xbf, 0x6e, 0x77, 0x7a, 0x46, 0xb6, 0xf6, 0xf7, 0x0c, 0xec, - 0xab, 0x15, 0x22, 0x44, 0x6d, 0x28, 0x24, 0x76, 0x71, 0x74, 0x96, 0xc8, 0xc0, 0xc3, 0x1d, 0xbd, - 0x6a, 0xee, 0xde, 0xf1, 0x16, 0xd1, 0x6f, 0x52, 0xe8, 0x8f, 0x50, 0x4c, 0x6e, 0x96, 0x28, 0xb9, - 0x31, 0xec, 0x58, 0x39, 0x3f, 0xaa, 0xeb, 0x2d, 0x18, 0x38, 0x12, 0xde, 0x4c, 0xbe, 0xf6, 0xf1, - 0xce, 0x86, 0xaa, 0x09, 0xfe, 0xd6, 0x22, 0x58, 0x3d, 0xdd, 0x29, 0x8b, 0xfb, 0xa3, 0xa3, 0xaf, - 0x18, 0x6f, 0x4d, 0x0f, 0xae, 0xb8, 0xb9, 0xaa, 0x55, 0x5f, 0x3c, 0x26, 0x8e, 0xb5, 0x8d, 0xe0, - 0x68, 0x47, 0x27, 0xa3, 0x5f, 0x25, 0x3d, 0x78, 0x74, 0x0e, 0x54, 0x5f, 0xfe, 0x1c, 0x6d, 0x6d, - 0x65, 0x47, 0xcb, 0x6f, 0x58, 0x79, 0x7c, 0x60, 0x6c, 0x58, 0xf9, 0xc8, 0xe4, 0x78, 0xf3, 0xdb, - 0x3f, 0x5d, 0x8d, 0x3d, 0x31, 0x59, 0xdc, 0x5c, 0x0e, 0x83, 0xd9, 0xd5, 0xd4, 0x1b, 0x4f, 0x84, - 0xef, 0xf9, 0x63, 0x9f, 0x8b, 0xbf, 0x04, 0xe1, 0xdd, 0xd5, 0xd4, 0x1f, 0x5d, 0xa9, 0x2d, 0xf4, - 0x6a, 0xa5, 0xee, 0x66, 0x5f, 0xfd, 0x0f, 0xf7, 0xbb, 0xff, 0x07, 0x00, 0x00, 0xff, 0xff, 0xb7, - 0x0c, 0x44, 0x60, 0xf3, 0x0d, 0x00, 0x00, + // 1635 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x57, 0x5f, 0x73, 0x22, 0xc7, + 0x11, 0x37, 0x02, 0x09, 0xd1, 0xfc, 0xd1, 0x6a, 0xa4, 0xd3, 0xed, 0xa1, 0xd3, 0x59, 0x5e, 0x27, + 0x67, 0xd5, 0x95, 0x23, 0x39, 0xa4, 0xce, 0xe5, 0xa7, 0xa4, 0x38, 0x18, 0xcc, 0xfa, 0x60, 0x57, + 0x1e, 0xe0, 0xec, 0x4b, 0x1e, 0xa6, 0x46, 0xec, 0x08, 0xb6, 0x04, 0xbb, 0x78, 0x77, 0x70, 0x4e, + 0x79, 0xc8, 0x5b, 0x5e, 0x52, 0x95, 0xcf, 0x92, 0x7c, 0x82, 0x7c, 0x8f, 0x7c, 0x95, 0x3c, 0xa5, + 0x66, 0x66, 0x81, 0x05, 0xa1, 0x8b, 0x9f, 0xc4, 0xfe, 0xfa, 0xd7, 0x7f, 0xa6, 0x7b, 0xba, 0xa7, + 0x05, 0x27, 0x51, 0x38, 0x17, 0x3c, 0x8a, 0x66, 0xc3, 0x2b, 0xfd, 0xeb, 0x72, 0x16, 0x85, 0x22, + 0x44, 0x85, 0x25, 0x5e, 0x2d, 0x44, 0xb3, 0xa1, 0x46, 0xad, 0xff, 0xee, 0x00, 0xea, 0xf1, 0xc0, + 0xbb, 0x66, 0xf7, 0x53, 0x1e, 0x08, 0xc2, 0x7f, 0x9a, 0xf3, 0x58, 0x20, 0x04, 0x39, 0x8f, 0xc7, + 0xc2, 0xcc, 0x9c, 0x67, 0x2e, 0x4a, 0x44, 0xfd, 0x46, 0x06, 0x64, 0xd9, 0x54, 0x98, 0x3b, 0xe7, + 0x99, 0x8b, 0x2c, 0x91, 0x3f, 0xd1, 0x67, 0x50, 0x9a, 0x69, 0x3d, 0x3a, 0x66, 0xf1, 0xd8, 0xcc, + 0x2a, 0x76, 0x31, 0xc1, 0xda, 0x2c, 0x1e, 0xa3, 0x0b, 0x30, 0x6e, 0xfd, 0x80, 0x4d, 0xe8, 0x70, + 0x22, 0x7e, 0xa6, 0x1e, 0x9f, 0x08, 0x66, 0xe6, 0xce, 0x33, 0x17, 0xbb, 0xa4, 0xa2, 0xf0, 0xc6, + 0x44, 0xfc, 0xdc, 0x94, 0x28, 0xfa, 0x02, 0x0e, 0x16, 0xc6, 0x22, 0x1d, 0x85, 0xb9, 0x7b, 0x9e, + 0xb9, 0x28, 0x90, 0xca, 0x6c, 0x3d, 0xb6, 0x2f, 0xe0, 0x40, 0xf8, 0x53, 0x1e, 0xce, 0x05, 0x8d, + 0xf9, 0x30, 0x0c, 0xbc, 0xd8, 0xdc, 0xd3, 0x16, 0x13, 0xb8, 0xa7, 0x51, 0x64, 0x41, 0xf9, 0x96, + 0x73, 0x3a, 0xf1, 0xa7, 0xbe, 0xa0, 0x31, 0x13, 0x66, 0x5e, 0x85, 0x5e, 0xbc, 0xe5, 0xbc, 0x23, + 0xb1, 0x1e, 0x13, 0x32, 0xbe, 0x70, 0x2e, 0x46, 0xa1, 0x1f, 0x8c, 0xe8, 0x70, 0xcc, 0x02, 0xea, + 0x7b, 0xe6, 0xfe, 0x79, 0xe6, 0x22, 0x47, 0x2a, 0x0b, 0xbc, 0x31, 0x66, 0x81, 0xed, 0xa1, 0x33, + 0x00, 0x75, 0x06, 0x65, 0xce, 0x2c, 0x28, 0x8f, 0x05, 0x89, 0x28, 0x5b, 0xa8, 0x06, 0x45, 0x95, + 0x60, 0x3a, 0xf6, 0x03, 0x11, 0x9b, 0x70, 0x9e, 0xbd, 0x28, 0xd6, 0x8c, 0xcb, 0x49, 0x20, 0x73, + 0x4d, 0xa4, 0xa4, 0xed, 0x07, 0x82, 0xa4, 0x49, 0xd6, 0x37, 0x70, 0xd4, 0x8f, 0xd8, 0xf0, 0x6e, + 0x23, 0xf9, 0x9b, 0x69, 0xcd, 0x3c, 0x48, 0xab, 0xf5, 0x57, 0x28, 0x27, 0x4a, 0x3d, 0xc1, 0xc4, + 0x3c, 0x46, 0xbf, 0x81, 0xdd, 0x58, 0x30, 0xc1, 0x15, 0xb9, 0x52, 0x7b, 0x7a, 0xb9, 0xac, 0xf6, + 0x65, 0x8a, 0xc8, 0x89, 0x66, 0xa1, 0x2a, 0xec, 0xcf, 0x22, 0xee, 0x4f, 0xd9, 0x88, 0xab, 0x82, + 0x96, 0xc8, 0xf2, 0x1b, 0x59, 0xb0, 0xab, 0x94, 0x55, 0x39, 0x8b, 0xb5, 0x52, 0xfa, 0x0c, 0x44, + 0x8b, 0xac, 0xdf, 0xc3, 0x81, 0xfa, 0x6e, 0x71, 0xfe, 0xb1, 0x2b, 0xf3, 0x14, 0xf2, 0x6c, 0xaa, + 0x73, 0xaf, 0xaf, 0xcd, 0x1e, 0x9b, 0xca, 0xb4, 0x5b, 0x1e, 0x18, 0x2b, 0xfd, 0x78, 0x16, 0x06, + 0x31, 0x97, 0xa5, 0x90, 0xc6, 0x65, 0x25, 0x64, 0xd9, 0xa6, 0x52, 0x2b, 0xa3, 0xb4, 0x2a, 0x09, + 0xde, 0xe2, 0xbc, 0x1b, 0x33, 0x81, 0x5e, 0xea, 0x1b, 0x40, 0x27, 0xe1, 0xf0, 0x4e, 0xde, 0x29, + 0x76, 0x9f, 0x98, 0x2f, 0x4b, 0xb8, 0x13, 0x0e, 0xef, 0x9a, 0x12, 0xb4, 0xfe, 0xa4, 0xef, 0x76, + 0x3f, 0xd4, 0xb1, 0xff, 0xe2, 0xf4, 0xae, 0x52, 0xb0, 0xf3, 0x78, 0x0a, 0x28, 0x1c, 0xad, 0x19, + 0x4f, 0x4e, 0x91, 0xce, 0x6c, 0x66, 0x23, 0xb3, 0x5f, 0x42, 0xfe, 0x96, 0xf9, 0x93, 0x79, 0xb4, + 0x30, 0x8c, 0x52, 0x65, 0x6a, 0x69, 0x09, 0x59, 0x50, 0xac, 0xbf, 0xe7, 0x21, 0x9f, 0x80, 0xa8, + 0x06, 0xb9, 0x61, 0xe8, 0x2d, 0xaa, 0xfb, 0xe2, 0xa1, 0xda, 0xe2, 0x6f, 0x23, 0xf4, 0x38, 0x51, + 0x5c, 0xf4, 0x07, 0xa8, 0xc8, 0x1b, 0x1d, 0xf0, 0x09, 0x9d, 0xcf, 0x3c, 0xb6, 0x2c, 0xa8, 0x99, + 0xd2, 0x6e, 0x68, 0xc2, 0x40, 0xc9, 0x49, 0x79, 0x98, 0xfe, 0x44, 0xa7, 0x50, 0x18, 0x8b, 0xc9, + 0x50, 0x57, 0x22, 0xa7, 0x9a, 0x62, 0x5f, 0x02, 0xaa, 0x06, 0x16, 0x94, 0xc3, 0xc0, 0x0f, 0x03, + 0x1a, 0x8f, 0x19, 0xad, 0xbd, 0xfe, 0x5a, 0x35, 0x6b, 0x89, 0x14, 0x15, 0xd8, 0x1b, 0xb3, 0xda, + 0xeb, 0xaf, 0xd1, 0xa7, 0x50, 0x54, 0x2d, 0xc3, 0x3f, 0xcc, 0xfc, 0xe8, 0x5e, 0x75, 0x69, 0x99, + 0xa8, 0x2e, 0xc2, 0x0a, 0x41, 0xc7, 0xb0, 0x7b, 0x3b, 0x61, 0xa3, 0x58, 0x75, 0x66, 0x99, 0xe8, + 0x0f, 0xf4, 0x15, 0x1c, 0x27, 0x39, 0xa0, 0x71, 0x38, 0x8f, 0x86, 0x9c, 0xfa, 0x81, 0xc7, 0x3f, + 0xa8, 0xbe, 0x2c, 0x13, 0x94, 0xc8, 0x7a, 0x4a, 0x64, 0x4b, 0x89, 0xf5, 0x9f, 0x1c, 0x14, 0x53, + 0x09, 0x40, 0x25, 0xd8, 0x27, 0xb8, 0x87, 0xc9, 0x3b, 0xdc, 0x34, 0x3e, 0x41, 0x26, 0x1c, 0x0f, + 0x9c, 0xb7, 0x8e, 0xfb, 0x83, 0x43, 0xaf, 0xeb, 0xef, 0xbb, 0xd8, 0xe9, 0xd3, 0x76, 0xbd, 0xd7, + 0x36, 0x32, 0xe8, 0x39, 0x98, 0xb6, 0xd3, 0x70, 0x09, 0xc1, 0x8d, 0xfe, 0x52, 0x56, 0xef, 0xba, + 0x03, 0xa7, 0x6f, 0xec, 0xa0, 0x4f, 0xe1, 0xb4, 0x65, 0x3b, 0xf5, 0x0e, 0x5d, 0x71, 0x1a, 0x9d, + 0xfe, 0x3b, 0x8a, 0x7f, 0xbc, 0xb6, 0xc9, 0x7b, 0x23, 0xbb, 0x8d, 0xd0, 0xee, 0x77, 0x1a, 0x0b, + 0x0b, 0x39, 0xf4, 0x0c, 0x9e, 0x68, 0x82, 0x56, 0xa1, 0x7d, 0xd7, 0xa5, 0x3d, 0xd7, 0x75, 0x8c, + 0x5d, 0x74, 0x08, 0x65, 0xdb, 0x79, 0x57, 0xef, 0xd8, 0x4d, 0x4a, 0x70, 0xbd, 0xd3, 0x35, 0xf6, + 0xd0, 0x11, 0x1c, 0x6c, 0xf2, 0xf2, 0xd2, 0xc4, 0x82, 0xe7, 0x3a, 0xb6, 0xeb, 0xd0, 0x77, 0x98, + 0xf4, 0x6c, 0xd7, 0x31, 0xf6, 0xd1, 0x09, 0xa0, 0x75, 0x51, 0xbb, 0x5b, 0x6f, 0x18, 0x05, 0xf4, + 0x04, 0x0e, 0xd7, 0xf1, 0xb7, 0xf8, 0xbd, 0x01, 0x32, 0x0d, 0x3a, 0x30, 0xfa, 0x06, 0x77, 0xdc, + 0x1f, 0x68, 0xd7, 0x76, 0xec, 0xee, 0xa0, 0x6b, 0x14, 0xd1, 0x31, 0x18, 0x2d, 0x8c, 0xa9, 0xed, + 0xf4, 0x06, 0xad, 0x96, 0xdd, 0xb0, 0xb1, 0xd3, 0x37, 0x4a, 0xda, 0xf3, 0xb6, 0x83, 0x97, 0xa5, + 0x42, 0xa3, 0x5d, 0x77, 0x1c, 0xdc, 0xa1, 0x4d, 0xbb, 0x57, 0x7f, 0xd3, 0xc1, 0x4d, 0xa3, 0x82, + 0xce, 0xe0, 0x59, 0x1f, 0x77, 0xaf, 0x5d, 0x52, 0x27, 0xef, 0xe9, 0x42, 0xde, 0xaa, 0xdb, 0x9d, + 0x01, 0xc1, 0xc6, 0x01, 0xfa, 0x0c, 0xce, 0x08, 0xfe, 0x7e, 0x60, 0x13, 0xdc, 0xa4, 0x8e, 0xdb, + 0xc4, 0xb4, 0x85, 0xeb, 0xfd, 0x01, 0xc1, 0xb4, 0x6b, 0xf7, 0x7a, 0xb6, 0xf3, 0xad, 0x61, 0xa0, + 0x5f, 0xc1, 0xf9, 0x92, 0xb2, 0x34, 0xb0, 0xc1, 0x3a, 0x94, 0xe7, 0x5b, 0xd4, 0xd3, 0xc1, 0x3f, + 0xf6, 0xe9, 0x35, 0xc6, 0xc4, 0x40, 0xa8, 0x0a, 0x27, 0x2b, 0xf7, 0xda, 0x41, 0xe2, 0xfb, 0x48, + 0xca, 0xae, 0x31, 0xe9, 0xd6, 0x1d, 0x59, 0xe0, 0x35, 0xd9, 0xb1, 0x0c, 0x7b, 0x25, 0xdb, 0x0c, + 0xfb, 0xc9, 0x77, 0xb9, 0xfd, 0x1d, 0x23, 0x6b, 0xfd, 0x33, 0x0b, 0xe5, 0xb5, 0x66, 0x41, 0xcf, + 0xa1, 0x10, 0xfb, 0xa3, 0x80, 0x09, 0xd9, 0xce, 0xba, 0xd3, 0x57, 0x80, 0x7a, 0x2d, 0xc6, 0xcc, + 0x0f, 0xf4, 0x88, 0xd1, 0x23, 0xb6, 0xa0, 0x10, 0x35, 0x60, 0x9e, 0x42, 0x7e, 0xf1, 0xda, 0x64, + 0x55, 0x63, 0xed, 0x0d, 0xf5, 0x2b, 0xf3, 0x1c, 0x0a, 0x72, 0x86, 0xc5, 0x82, 0x4d, 0x67, 0xaa, + 0xe7, 0xca, 0x64, 0x05, 0xa0, 0xcf, 0xa1, 0x3c, 0xe5, 0x71, 0xcc, 0x46, 0x9c, 0xea, 0xbe, 0x01, + 0xc5, 0x28, 0x25, 0x60, 0x4b, 0xb5, 0xcf, 0xe7, 0xb0, 0xe8, 0xe3, 0x84, 0xb4, 0xab, 0x49, 0x09, + 0xa8, 0x49, 0x9b, 0x23, 0x54, 0xb0, 0xa4, 0x3d, 0xd3, 0x23, 0x54, 0x30, 0xf4, 0x0a, 0x0e, 0xf5, + 0x0c, 0xf0, 0x03, 0x7f, 0x3a, 0x9f, 0xea, 0x59, 0x90, 0x57, 0x21, 0x1f, 0xa8, 0x59, 0xa0, 0x71, + 0x35, 0x12, 0x9e, 0xc1, 0xfe, 0x0d, 0x8b, 0xb9, 0x9c, 0xde, 0x49, 0xaf, 0xe6, 0xe5, 0x77, 0x8b, + 0x73, 0x29, 0x92, 0x33, 0x3d, 0x92, 0x53, 0xa8, 0xa0, 0x45, 0xb7, 0x9c, 0x13, 0x99, 0xc7, 0xa5, + 0x07, 0xf6, 0x61, 0xe5, 0xa1, 0x98, 0xf2, 0xa0, 0x71, 0xe5, 0xe1, 0x15, 0x1c, 0xf2, 0x0f, 0x22, + 0x62, 0x34, 0x9c, 0xb1, 0x9f, 0xe6, 0x9c, 0x7a, 0x4c, 0x30, 0xb3, 0xa4, 0x92, 0x7b, 0xa0, 0x04, + 0xae, 0xc2, 0x9b, 0x4c, 0x30, 0xeb, 0x39, 0x54, 0x09, 0x8f, 0xb9, 0xe8, 0xfa, 0x71, 0xec, 0x87, + 0x41, 0x23, 0x0c, 0x44, 0x14, 0x4e, 0x92, 0x47, 0xc0, 0x3a, 0x83, 0xd3, 0xad, 0x52, 0x3d, 0xc5, + 0xa5, 0xf2, 0xf7, 0x73, 0x1e, 0xdd, 0x6f, 0x57, 0x7e, 0x0b, 0xa7, 0x5b, 0xa5, 0xc9, 0x13, 0xf0, + 0x25, 0xec, 0x06, 0xa1, 0xc7, 0x63, 0x33, 0xa3, 0x96, 0x80, 0x93, 0xd4, 0xbc, 0x75, 0x42, 0x8f, + 0xb7, 0xfd, 0x58, 0x84, 0xd1, 0x3d, 0xd1, 0x24, 0xeb, 0xdf, 0x19, 0x28, 0xa6, 0x60, 0x74, 0x02, + 0x7b, 0xb3, 0xf9, 0xcd, 0x1d, 0xbf, 0x4f, 0x2e, 0x55, 0xf2, 0x85, 0x5e, 0x42, 0x65, 0xc2, 0x62, + 0x41, 0xe5, 0xf8, 0xa3, 0xb2, 0x48, 0xc9, 0x9b, 0xb7, 0x81, 0xa2, 0x6f, 0xe0, 0x69, 0x28, 0xc6, + 0x3c, 0xd2, 0xeb, 0x4c, 0x3c, 0x1f, 0x0e, 0x79, 0x1c, 0xd3, 0x59, 0x14, 0xde, 0xa8, 0xab, 0xb6, + 0x43, 0x1e, 0x13, 0xa3, 0xd7, 0xb0, 0x9f, 0xdc, 0x91, 0xd8, 0xcc, 0xa9, 0xd0, 0x9f, 0x3d, 0x7c, + 0x2a, 0x16, 0xd1, 0x2f, 0xa9, 0xd6, 0xbf, 0x32, 0x50, 0x59, 0x17, 0xa2, 0x17, 0xea, 0xf6, 0xab, + 0x2b, 0xe8, 0x7b, 0xea, 0x1c, 0x39, 0x92, 0x42, 0x7e, 0xf1, 0x59, 0x6a, 0x70, 0x3c, 0xf5, 0x03, + 0x3a, 0xe3, 0x01, 0x9b, 0xf8, 0x7f, 0xe1, 0x74, 0xb1, 0x4c, 0x64, 0x15, 0x7b, 0xab, 0x0c, 0x59, + 0x50, 0x5a, 0x3b, 0x74, 0x4e, 0x1d, 0x7a, 0x0d, 0x7b, 0xf5, 0x8f, 0x0c, 0x94, 0xd2, 0x6b, 0x11, + 0x2a, 0x43, 0xc1, 0x76, 0x68, 0xab, 0x63, 0x7f, 0xdb, 0xee, 0x1b, 0x9f, 0xc8, 0xcf, 0xde, 0xa0, + 0xd1, 0xc0, 0xb8, 0x89, 0x9b, 0x46, 0x06, 0x21, 0xa8, 0xc8, 0x79, 0x80, 0x9b, 0xb4, 0x6f, 0x77, + 0xb1, 0x3b, 0x90, 0x8f, 0xc3, 0x11, 0x1c, 0x24, 0x98, 0xe3, 0x52, 0xe2, 0x0e, 0xfa, 0xd8, 0xc8, + 0x22, 0x03, 0x4a, 0x09, 0x88, 0x09, 0x71, 0x89, 0x91, 0x93, 0x13, 0x2d, 0x41, 0x1e, 0x3e, 0x34, + 0x4d, 0xdc, 0xaf, 0xdb, 0x9d, 0x9e, 0xb1, 0x5b, 0xfb, 0x5b, 0x0e, 0xf6, 0xd4, 0x1a, 0x11, 0xa1, + 0x36, 0x14, 0x53, 0xfb, 0x38, 0x3a, 0x4b, 0x55, 0xe0, 0xe1, 0x9e, 0x5e, 0x35, 0xb7, 0xef, 0x79, + 0xf3, 0xf8, 0xab, 0x0c, 0xfa, 0x0e, 0x4a, 0xe9, 0xed, 0x12, 0xa5, 0xb7, 0x86, 0x2d, 0x6b, 0xe7, + 0x47, 0x6d, 0xbd, 0x05, 0x03, 0xc7, 0xc2, 0x9f, 0xca, 0x2d, 0x21, 0xd9, 0xdb, 0x50, 0x35, 0xc5, + 0xdf, 0x58, 0x06, 0xab, 0xa7, 0x5b, 0x65, 0x49, 0x7f, 0x74, 0xf4, 0x11, 0x93, 0xcd, 0xe9, 0xc1, + 0x11, 0xd7, 0xd7, 0xb5, 0xea, 0x8b, 0xc7, 0xc4, 0x89, 0x35, 0x0f, 0x8e, 0xb6, 0x74, 0x32, 0xfa, + 0x75, 0x3a, 0x82, 0x47, 0xe7, 0x40, 0xf5, 0xe5, 0xff, 0xa3, 0xad, 0xbc, 0x6c, 0x69, 0xf9, 0x35, + 0x2f, 0x8f, 0x0f, 0x8c, 0x35, 0x2f, 0x1f, 0x99, 0x1c, 0x6f, 0x7e, 0xfb, 0xc7, 0xab, 0x91, 0x2f, + 0xc6, 0xf3, 0x9b, 0xcb, 0x61, 0x38, 0xbd, 0x9a, 0xf8, 0xa3, 0xb1, 0x08, 0xfc, 0x60, 0x14, 0x70, + 0xf1, 0xe7, 0x30, 0xba, 0xbb, 0x9a, 0x04, 0xde, 0x95, 0xda, 0x44, 0xaf, 0x96, 0xe6, 0x6e, 0xf6, + 0xd4, 0xff, 0x71, 0xbf, 0xfb, 0x5f, 0x00, 0x00, 0x00, 0xff, 0xff, 0x2f, 0xaa, 0x9f, 0x4e, 0xf7, + 0x0d, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. diff --git a/lnrpc/routerrpc/router.proto b/lnrpc/routerrpc/router.proto index b2e513176..2581ce595 100644 --- a/lnrpc/routerrpc/router.proto +++ b/lnrpc/routerrpc/router.proto @@ -199,11 +199,7 @@ message Failure { /// Failure code as defined in the Lightning spec FailureCode code = 1; - /** - The node pubkey of the intermediate or final node that generated the failure - message. - **/ - bytes failure_source_pubkey = 2; + reserved 2; /// An optional channel update message. ChannelUpdate channel_update = 3; @@ -219,6 +215,12 @@ message Failure { /// A failure type-dependent flags value. uint32 flags = 7; + + /** + The position in the path of the intermediate or final node that generated + the failure message. Position zero is the sender node. + **/ + uint32 failure_source_index = 8; } diff --git a/lnrpc/routerrpc/router_server.go b/lnrpc/routerrpc/router_server.go index 6291a5479..21bc233e9 100644 --- a/lnrpc/routerrpc/router_server.go +++ b/lnrpc/routerrpc/router_server.go @@ -399,7 +399,7 @@ func marshallError(sendError error) (*Failure, error) { return nil, errors.New("unknown wire error") } - response.FailureSourcePubkey = fErr.ErrorSource.SerializeCompressed() + response.FailureSourceIndex = uint32(fErr.FailureSourceIdx) return response, nil } diff --git a/routing/router.go b/routing/router.go index 00e4d94b0..63f77a319 100644 --- a/routing/router.go +++ b/routing/router.go @@ -1815,19 +1815,39 @@ func (r *ChannelRouter) processSendError(paySession PaymentSession, rt *route.Route, fErr *htlcswitch.ForwardingError) ( bool, channeldb.FailureReason) { - errSource := fErr.ErrorSource - errVertex := route.NewVertex(errSource) + var ( + failureSourceIdx = fErr.FailureSourceIdx - log.Tracef("node=%x reported failure when sending htlc", errVertex) + failureVertex route.Vertex + failureSource *btcec.PublicKey + err error + ) + + // For any non-self failure, look up the source pub key in the hops + // slice. Otherwise return the self node pubkey. + if failureSourceIdx > 0 { + failureVertex = rt.Hops[failureSourceIdx-1].PubKeyBytes + failureSource, err = btcec.ParsePubKey(failureVertex[:], btcec.S256()) + if err != nil { + log.Errorf("Cannot parse pubkey %v: %v", + failureVertex, err) + + return true, channeldb.FailureReasonError + } + } else { + failureVertex = r.selfNode.PubKeyBytes + failureSource, err = r.selfNode.PubKey() + if err != nil { + log.Errorf("Cannot parse self pubkey: %v", err) + return true, channeldb.FailureReasonError + } + } + log.Tracef("Node %x (index %v) reported failure when sending htlc", + failureVertex, failureSourceIdx) // Always determine chan id ourselves, because a channel // update with id may not be available. - failedEdge, failedAmt, err := getFailedEdge( - rt, route.Vertex(errVertex), - ) - if err != nil { - return true, channeldb.FailureReasonError - } + failedEdge, failedAmt := getFailedEdge(rt, failureSourceIdx) // processChannelUpdateAndRetry is a closure that // handles a failure message containing a channel @@ -1919,8 +1939,8 @@ func (r *ChannelRouter) processSendError(paySession PaymentSession, // that sent us this error, as it doesn't now what the // correct block height is. case *lnwire.FailExpiryTooSoon: - r.applyChannelUpdate(&onionErr.Update, errSource) - paySession.ReportVertexFailure(errVertex) + r.applyChannelUpdate(&onionErr.Update, failureSource) + paySession.ReportVertexFailure(failureVertex) return false, 0 // If we hit an instance of onion payload corruption or an invalid @@ -1941,7 +1961,7 @@ func (r *ChannelRouter) processSendError(paySession PaymentSession, // routing. case *lnwire.FailAmountBelowMinimum: processChannelUpdateAndRetry( - &onionErr.Update, errSource, + &onionErr.Update, failureSource, ) return false, 0 @@ -1950,7 +1970,7 @@ func (r *ChannelRouter) processSendError(paySession PaymentSession, // newly updated fees. case *lnwire.FailFeeInsufficient: processChannelUpdateAndRetry( - &onionErr.Update, errSource, + &onionErr.Update, failureSource, ) return false, 0 @@ -1959,7 +1979,7 @@ func (r *ChannelRouter) processSendError(paySession PaymentSession, // apply the new delta value and try it once more. case *lnwire.FailIncorrectCltvExpiry: processChannelUpdateAndRetry( - &onionErr.Update, errSource, + &onionErr.Update, failureSource, ) return false, 0 @@ -1967,7 +1987,7 @@ func (r *ChannelRouter) processSendError(paySession PaymentSession, // forward one is currently disabled, so we'll apply // the update and continue. case *lnwire.FailChannelDisabled: - r.applyChannelUpdate(&onionErr.Update, errSource) + r.applyChannelUpdate(&onionErr.Update, failureSource) paySession.ReportEdgeFailure(failedEdge, 0) return false, 0 @@ -1975,7 +1995,7 @@ func (r *ChannelRouter) processSendError(paySession PaymentSession, // sufficient capacity, so we'll prune this edge for // now, and continue onwards with our path finding. case *lnwire.FailTemporaryChannelFailure: - r.applyChannelUpdate(onionErr.Update, errSource) + r.applyChannelUpdate(onionErr.Update, failureSource) paySession.ReportEdgeFailure(failedEdge, failedAmt) return false, 0 @@ -1983,14 +2003,14 @@ func (r *ChannelRouter) processSendError(paySession PaymentSession, // required features, then we'll note this error and // continue. case *lnwire.FailRequiredNodeFeatureMissing: - paySession.ReportVertexFailure(errVertex) + paySession.ReportVertexFailure(failureVertex) return false, 0 // If the send fail due to a node not having the // required features, then we'll note this error and // continue. case *lnwire.FailRequiredChannelFeatureMissing: - paySession.ReportVertexFailure(errVertex) + paySession.ReportVertexFailure(failureVertex) return false, 0 // If the next hop in the route wasn't known or @@ -2008,11 +2028,11 @@ func (r *ChannelRouter) processSendError(paySession PaymentSession, // reason, then we'll note this and continue with the // routes. case *lnwire.FailTemporaryNodeFailure: - paySession.ReportVertexFailure(errVertex) + paySession.ReportVertexFailure(failureVertex) return false, 0 case *lnwire.FailPermanentNodeFailure: - paySession.ReportVertexFailure(errVertex) + paySession.ReportVertexFailure(failureVertex) return false, 0 // If we crafted a route that contains a too long time @@ -2025,7 +2045,7 @@ func (r *ChannelRouter) processSendError(paySession PaymentSession, // that as a hint during future path finding through // that node. case *lnwire.FailExpiryTooFar: - paySession.ReportVertexFailure(errVertex) + paySession.ReportVertexFailure(failureVertex) return false, 0 // If we get a permanent channel or node failure, then @@ -2046,45 +2066,43 @@ func (r *ChannelRouter) processSendError(paySession PaymentSession, } // getFailedEdge tries to locate the failing channel given a route and the -// pubkey of the node that sent the error. It will assume that the error is -// associated with the outgoing channel of the error node. As a second result, +// pubkey of the node that sent the failure. It will assume that the failure is +// associated with the outgoing channel of the failing node. As a second result, // it returns the amount sent over the edge. -func getFailedEdge(route *route.Route, errSource route.Vertex) (edge, - lnwire.MilliSatoshi, error) { +func getFailedEdge(route *route.Route, failureSource int) (edge, + lnwire.MilliSatoshi) { - hopCount := len(route.Hops) - fromNode := route.SourcePubKey - amt := route.TotalAmount - for i, hop := range route.Hops { - toNode := hop.PubKeyBytes - - // Determine if we have a failure from the final hop. - // - // TODO(joostjager): In this case, certain types of errors are - // not expected. For example FailUnknownNextPeer. This could be - // a reason to prune the node? - finalHopFailing := i == hopCount-1 && errSource == toNode - - // As this error indicates that the target channel was unable to - // carry this HTLC (for w/e reason), we'll return the _outgoing_ - // channel that the source of the error was meant to pass the - // HTLC along to. - // - // If the errSource is the final hop, we assume that the failing - // channel is the incoming channel. - if errSource == fromNode || finalHopFailing { - return edge{ - from: fromNode, - to: toNode, - channel: hop.ChannelID, - }, amt, nil - } - - fromNode = toNode - amt = hop.AmtToForward + // Determine if we have a failure from the final hop. If it is, we + // assume that the failing channel is the incoming channel. In this + // function the outgoing channel of the hop indicated by failureSource + // is returned, where index zero is the self node. By decrementing + // failureSource by one, the outgoing channel of the penultimate hop is + // returned, which is the same as the incoming channel of the final + // node. + // + // TODO(joostjager): In this case, certain types of failures are not + // expected. For example FailUnknownNextPeer. This could be a reason to + // prune the node? + if failureSource == len(route.Hops) { + failureSource-- } - return edge{}, 0, fmt.Errorf("cannot find error source node in route") + // As this failure indicates that the target channel was unable to carry + // this HTLC (for w/e reason), we'll return the _outgoing_ channel that + // the source of the failure was meant to pass the HTLC along to. + if failureSource == 0 { + return edge{ + from: route.SourcePubKey, + to: route.Hops[0].PubKeyBytes, + channel: route.Hops[0].ChannelID, + }, route.TotalAmount + } + + return edge{ + from: route.Hops[failureSource-1].PubKeyBytes, + to: route.Hops[failureSource].PubKeyBytes, + channel: route.Hops[failureSource].ChannelID, + }, route.Hops[failureSource-1].AmtToForward } // applyChannelUpdate validates a channel update and if valid, applies it to the diff --git a/routing/router_test.go b/routing/router_test.go index 7f2c51888..aa01b2ff4 100644 --- a/routing/router_test.go +++ b/routing/router_test.go @@ -265,8 +265,6 @@ func TestSendPaymentRouteFailureFallback(t *testing.T) { var preImage [32]byte copy(preImage[:], bytes.Repeat([]byte{9}, 32)) - sourceNode := ctx.router.selfNode - // We'll modify the SendToSwitch method that's been set within the // router's configuration to ignore the path that has luo ji as the // first hop. This should force the router to instead take the @@ -276,12 +274,8 @@ func TestSendPaymentRouteFailureFallback(t *testing.T) { roasbeefLuoji := lnwire.NewShortChanIDFromInt(689530843) if firstHop == roasbeefLuoji { - pub, err := sourceNode.PubKey() - if err != nil { - return preImage, err - } return [32]byte{}, &htlcswitch.ForwardingError{ - ErrorSource: pub, + FailureSourceIdx: 0, // TODO(roasbeef): temp node failure should be? FailureMessage: &lnwire.FailTemporaryChannelFailure{}, } @@ -410,17 +404,8 @@ func TestChannelUpdateValidation(t *testing.T) { // The unsigned channel update is attached to the failure message. ctx.router.cfg.Payer.(*mockPaymentAttemptDispatcher).setPaymentResult( func(firstHop lnwire.ShortChannelID) ([32]byte, error) { - - v := ctx.aliases["b"] - source, err := btcec.ParsePubKey( - v[:], btcec.S256(), - ) - if err != nil { - t.Fatal(err) - } - return [32]byte{}, &htlcswitch.ForwardingError{ - ErrorSource: source, + FailureSourceIdx: 1, FailureMessage: &lnwire.FailFeeInsufficient{ Update: errChanUpdate, }, @@ -533,9 +518,6 @@ func TestSendPaymentErrorRepeatedFeeInsufficient(t *testing.T) { FeeRate: uint32(edgeUpdateToFail.FeeProportionalMillionths), } - // The error will be returned by Son Goku. - sourceNode := ctx.aliases["songoku"] - // We'll now modify the SendToSwitch method to return an error for the // outgoing channel to Son goku. This will be a fee related error, so // it should only cause the edge to be pruned after the second attempt. @@ -544,15 +526,8 @@ func TestSendPaymentErrorRepeatedFeeInsufficient(t *testing.T) { roasbeefSongoku := lnwire.NewShortChanIDFromInt(chanID) if firstHop == roasbeefSongoku { - sourceKey, err := btcec.ParsePubKey( - sourceNode[:], btcec.S256(), - ) - if err != nil { - t.Fatal(err) - } - return [32]byte{}, &htlcswitch.ForwardingError{ - ErrorSource: sourceKey, + FailureSourceIdx: 1, // Within our error, we'll add a channel update // which is meant to reflect he new fee @@ -647,9 +622,6 @@ func TestSendPaymentErrorNonFinalTimeLockErrors(t *testing.T) { FeeRate: uint32(edgeUpdateToFail.FeeProportionalMillionths), } - // The error will be returned by Son Goku. - sourceNode := ctx.aliases["songoku"] - // We'll now modify the SendToSwitch method to return an error for the // outgoing channel to son goku. Since this is a time lock related // error, we should fail the payment flow all together, as Goku is the @@ -658,15 +630,8 @@ func TestSendPaymentErrorNonFinalTimeLockErrors(t *testing.T) { func(firstHop lnwire.ShortChannelID) ([32]byte, error) { if firstHop == roasbeefSongoku { - sourceKey, err := btcec.ParsePubKey( - sourceNode[:], btcec.S256(), - ) - if err != nil { - t.Fatal(err) - } - return [32]byte{}, &htlcswitch.ForwardingError{ - ErrorSource: sourceKey, + FailureSourceIdx: 1, FailureMessage: &lnwire.FailExpiryTooSoon{ Update: errChanUpdate, }, @@ -719,15 +684,8 @@ func TestSendPaymentErrorNonFinalTimeLockErrors(t *testing.T) { func(firstHop lnwire.ShortChannelID) ([32]byte, error) { if firstHop == roasbeefSongoku { - sourceKey, err := btcec.ParsePubKey( - sourceNode[:], btcec.S256(), - ) - if err != nil { - t.Fatal(err) - } - return [32]byte{}, &htlcswitch.ForwardingError{ - ErrorSource: sourceKey, + FailureSourceIdx: 1, FailureMessage: &lnwire.FailIncorrectCltvExpiry{ Update: errChanUpdate, }, @@ -776,16 +734,6 @@ func TestSendPaymentErrorPathPruning(t *testing.T) { var preImage [32]byte copy(preImage[:], bytes.Repeat([]byte{9}, 32)) - sourceNode, err := ctx.graph.SourceNode() - if err != nil { - t.Fatalf("unable to fetch source node: %v", err) - } - - sourcePub, err := sourceNode.PubKey() - if err != nil { - t.Fatalf("unable to fetch source node pub: %v", err) - } - roasbeefLuoji := lnwire.NewShortChanIDFromInt(689530843) // First, we'll modify the SendToSwitch method to return an error @@ -802,7 +750,7 @@ func TestSendPaymentErrorPathPruning(t *testing.T) { // outgoing link to simulate the channel from luo ji to // roasbeef not having enough capacity. return [32]byte{}, &htlcswitch.ForwardingError{ - ErrorSource: sourcePub, + FailureSourceIdx: 0, FailureMessage: &lnwire.FailTemporaryChannelFailure{}, } } @@ -812,16 +760,8 @@ func TestSendPaymentErrorPathPruning(t *testing.T) { // prune out the rest of the routes. roasbeefSatoshi := lnwire.NewShortChanIDFromInt(2340213491) if firstHop == roasbeefSatoshi { - vertex := ctx.aliases["satoshi"] - key, err := btcec.ParsePubKey( - vertex[:], btcec.S256(), - ) - if err != nil { - t.Fatal(err) - } - return [32]byte{}, &htlcswitch.ForwardingError{ - ErrorSource: key, + FailureSourceIdx: 1, FailureMessage: &lnwire.FailUnknownNextPeer{}, } } @@ -854,7 +794,7 @@ func TestSendPaymentErrorPathPruning(t *testing.T) { if firstHop == roasbeefLuoji { return [32]byte{}, &htlcswitch.ForwardingError{ - ErrorSource: sourcePub, + FailureSourceIdx: 0, FailureMessage: &lnwire.FailUnknownNextPeer{}, } } @@ -900,7 +840,7 @@ func TestSendPaymentErrorPathPruning(t *testing.T) { // outgoing link to simulate the channel from luo ji to // roasbeef not having enough capacity. return [32]byte{}, &htlcswitch.ForwardingError{ - ErrorSource: sourcePub, + FailureSourceIdx: 0, FailureMessage: &lnwire.FailTemporaryChannelFailure{}, } } @@ -2948,11 +2888,6 @@ func TestRouterPaymentStateMachine(t *testing.T) { PaymentHash: payHash, } - errSource, err := btcec.ParsePubKey(hop1[:], btcec.S256()) - if err != nil { - t.Fatalf("unable to fetch source node pub: %v", err) - } - copy(preImage[:], bytes.Repeat([]byte{9}, 32)) router.cfg.MissionControl = &mockPaymentSessionSource{ @@ -3030,7 +2965,7 @@ func TestRouterPaymentStateMachine(t *testing.T) { case sendToSwitchResultFailure: select { case sendResult <- &htlcswitch.ForwardingError{ - ErrorSource: errSource, + FailureSourceIdx: 1, FailureMessage: &lnwire.FailTemporaryChannelFailure{}, }: case <-time.After(1 * time.Second): @@ -3056,7 +2991,7 @@ func TestRouterPaymentStateMachine(t *testing.T) { select { case getPaymentResult <- &htlcswitch.PaymentResult{ Error: &htlcswitch.ForwardingError{ - ErrorSource: errSource, + FailureSourceIdx: 1, FailureMessage: &lnwire.FailTemporaryChannelFailure{}, }, }: @@ -3224,17 +3159,8 @@ func TestSendToRouteStructuredError(t *testing.T) { // The unsigned channel update is attached to the failure message. ctx.router.cfg.Payer.(*mockPaymentAttemptDispatcher).setPaymentResult( func(firstHop lnwire.ShortChannelID) ([32]byte, error) { - - v := ctx.aliases["b"] - source, err := btcec.ParsePubKey( - v[:], btcec.S256(), - ) - if err != nil { - t.Fatal(err) - } - return [32]byte{}, &htlcswitch.ForwardingError{ - ErrorSource: source, + FailureSourceIdx: 1, FailureMessage: &lnwire.FailFeeInsufficient{ Update: lnwire.ChannelUpdate{}, }, diff --git a/server.go b/server.go index ed1681885..88204d812 100644 --- a/server.go +++ b/server.go @@ -409,8 +409,7 @@ func newServer(listenAddrs []net.Addr, chanDB *channeldb.DB, } s.htlcSwitch, err = htlcswitch.New(htlcswitch.Config{ - DB: chanDB, - SelfKey: s.identityPriv.PubKey(), + DB: chanDB, LocalChannelClose: func(pubKey []byte, request *htlcswitch.ChanClose) { From 0cdae56d28c00cfa9bbbcb5769b1d19ff17a3c39 Mon Sep 17 00:00:00 2001 From: Joost Jager Date: Wed, 19 Jun 2019 15:04:17 +0200 Subject: [PATCH 4/7] htlcswitch+routing: make error decryption mockable --- htlcswitch/failure.go | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/htlcswitch/failure.go b/htlcswitch/failure.go index ffd3b18dd..29b72021c 100644 --- a/htlcswitch/failure.go +++ b/htlcswitch/failure.go @@ -244,10 +244,21 @@ func (s *SphinxErrorEncrypter) Reextract( // ErrorEncrypter interface. var _ ErrorEncrypter = (*SphinxErrorEncrypter)(nil) +// OnionErrorDecrypter is the interface that provides onion level error +// decryption. +type OnionErrorDecrypter interface { + // DecryptError attempts to decrypt the passed encrypted error response. + // The onion failure is encrypted in backward manner, starting from the + // node where error have occurred. As a result, in order to decrypt the + // error we need get all shared secret and apply decryption in the + // reverse order. + DecryptError(encryptedData []byte) (*sphinx.DecryptedError, error) +} + // SphinxErrorDecrypter wraps the sphinx data SphinxErrorDecrypter and maps the // returned errors to concrete lnwire.FailureMessage instances. type SphinxErrorDecrypter struct { - *sphinx.OnionErrorDecrypter + OnionErrorDecrypter } // DecryptError peels off each layer of onion encryption from the first hop, to @@ -255,7 +266,8 @@ type SphinxErrorDecrypter struct { // along with the source of the error. // // NOTE: Part of the ErrorDecrypter interface. -func (s *SphinxErrorDecrypter) DecryptError(reason lnwire.OpaqueReason) (*ForwardingError, error) { +func (s *SphinxErrorDecrypter) DecryptError(reason lnwire.OpaqueReason) ( + *ForwardingError, error) { failure, err := s.OnionErrorDecrypter.DecryptError(reason) if err != nil { From 418fe364de7eb1c6971dbcb369e3e34d63c4cf5d Mon Sep 17 00:00:00 2001 From: Joost Jager Date: Wed, 19 Jun 2019 11:12:10 +0200 Subject: [PATCH 5/7] htlcswitch: use distinct error for unreadable failures --- htlcswitch/mock.go | 15 ++ htlcswitch/switch.go | 35 ++--- htlcswitch/switch_test.go | 104 +++++++++++++ lnrpc/routerrpc/router.pb.go | 258 ++++++++++++++++--------------- lnrpc/routerrpc/router.proto | 6 + lnrpc/routerrpc/router_server.go | 5 + routing/payment_lifecycle.go | 29 ++-- routing/router.go | 20 ++- routing/router_test.go | 89 +++++++++++ 9 files changed, 396 insertions(+), 165 deletions(-) diff --git a/htlcswitch/mock.go b/htlcswitch/mock.go index 89c053960..b5ebfcf8e 100644 --- a/htlcswitch/mock.go +++ b/htlcswitch/mock.go @@ -965,3 +965,18 @@ func (m *mockCircuitMap) NumPending() int { func (m *mockCircuitMap) NumOpen() int { return 0 } + +type mockOnionErrorDecryptor struct { + sourceIdx int + message []byte + err error +} + +func (m *mockOnionErrorDecryptor) DecryptError(encryptedData []byte) ( + *sphinx.DecryptedError, error) { + + return &sphinx.DecryptedError{ + SenderIdx: m.sourceIdx, + Message: m.message, + }, m.err +} diff --git a/htlcswitch/switch.go b/htlcswitch/switch.go index a86767452..8f6f99ece 100644 --- a/htlcswitch/switch.go +++ b/htlcswitch/switch.go @@ -58,6 +58,10 @@ var ( // active links in the switch for a specific destination. ErrNoLinksFound = errors.New("no channel links found") + // ErrUnreadableFailureMessage is returned when the failure message + // cannot be decrypted. + ErrUnreadableFailureMessage = errors.New("unreadable failure message") + // zeroPreimage is the empty preimage which is returned when we have // some errors. zeroPreimage [sha256.Size]byte @@ -936,9 +940,7 @@ func (s *Switch) extractResult(deobfuscator ErrorDecrypter, n *networkResult, // the payment deobfuscator. func (s *Switch) parseFailedPayment(deobfuscator ErrorDecrypter, paymentID uint64, paymentHash lntypes.Hash, unencrypted, - isResolution bool, htlc *lnwire.UpdateFailHTLC) *ForwardingError { - - var failure *ForwardingError + isResolution bool, htlc *lnwire.UpdateFailHTLC) error { switch { @@ -960,7 +962,8 @@ func (s *Switch) parseFailedPayment(deobfuscator ErrorDecrypter, // router. failureMsg = lnwire.NewTemporaryChannelFailure(nil) } - failure = &ForwardingError{ + + return &ForwardingError{ FailureSourceIdx: 0, ExtraMsg: userErr, FailureMessage: failureMsg, @@ -974,7 +977,8 @@ func (s *Switch) parseFailedPayment(deobfuscator ErrorDecrypter, userErr := fmt.Sprintf("payment was resolved "+ "on-chain, then cancelled back (hash=%v, pid=%d)", paymentHash, paymentID) - failure = &ForwardingError{ + + return &ForwardingError{ FailureSourceIdx: 0, ExtraMsg: userErr, FailureMessage: lnwire.FailPermanentChannelFailure{}, @@ -983,24 +987,19 @@ func (s *Switch) parseFailedPayment(deobfuscator ErrorDecrypter, // A regular multi-hop payment error that we'll need to // decrypt. default: - var err error // We'll attempt to fully decrypt the onion encrypted // error. If we're unable to then we'll bail early. - failure, err = deobfuscator.DecryptError(htlc.Reason) + failure, err := deobfuscator.DecryptError(htlc.Reason) if err != nil { - userErr := fmt.Sprintf("unable to de-obfuscate "+ - "onion failure (hash=%v, pid=%d): %v", + log.Errorf("unable to de-obfuscate onion failure "+ + "(hash=%v, pid=%d): %v", paymentHash, paymentID, err) - log.Error(userErr) - failure = &ForwardingError{ - FailureSourceIdx: 0, - ExtraMsg: userErr, - FailureMessage: lnwire.NewTemporaryChannelFailure(nil), - } - } - } - return failure + return ErrUnreadableFailureMessage + } + + return failure + } } // handlePacketForward is used in cases when we need forward the htlc update diff --git a/htlcswitch/switch_test.go b/htlcswitch/switch_test.go index 2571342a4..1e27abe9d 100644 --- a/htlcswitch/switch_test.go +++ b/htlcswitch/switch_test.go @@ -2241,3 +2241,107 @@ func TestSwitchGetPaymentResult(t *testing.T) { t.Fatalf("result not received") } } + +// TestInvalidFailure tests that the switch returns an unreadable failure error +// if the failure cannot be decrypted. +func TestInvalidFailure(t *testing.T) { + t.Parallel() + + alicePeer, err := newMockServer(t, "alice", testStartingHeight, nil, 6) + if err != nil { + t.Fatalf("unable to create alice server: %v", err) + } + + s, err := initSwitchWithDB(testStartingHeight, nil) + if err != nil { + t.Fatalf("unable to init switch: %v", err) + } + if err := s.Start(); err != nil { + t.Fatalf("unable to start switch: %v", err) + } + defer s.Stop() + + chanID1, _, aliceChanID, _ := genIDs() + + // Set up a mock channel link. + aliceChannelLink := newMockChannelLink( + s, chanID1, aliceChanID, alicePeer, true, + ) + if err := s.AddLink(aliceChannelLink); err != nil { + t.Fatalf("unable to add link: %v", err) + } + + // Create a request which should be forwarded to the mock channel link. + preimage, err := genPreimage() + if err != nil { + t.Fatalf("unable to generate preimage: %v", err) + } + rhash := fastsha256.Sum256(preimage[:]) + update := &lnwire.UpdateAddHTLC{ + PaymentHash: rhash, + Amount: 1, + } + + paymentID := uint64(123) + + // Send the request. + err = s.SendHTLC( + aliceChannelLink.ShortChanID(), paymentID, update, + ) + if err != nil { + t.Fatalf("unable to send payment: %v", err) + } + + // Catch the packet and complete the circuit so that the switch is ready + // for a response. + select { + case packet := <-aliceChannelLink.packets: + if err := aliceChannelLink.completeCircuit(packet); err != nil { + t.Fatalf("unable to complete payment circuit: %v", err) + } + + case <-time.After(time.Second): + t.Fatal("request was not propagated to destination") + } + + // Send response packet with an unreadable failure message to the + // switch. The reason failed is not relevant, because we mock the + // decryption. + packet := &htlcPacket{ + outgoingChanID: aliceChannelLink.ShortChanID(), + outgoingHTLCID: 0, + amount: 1, + htlc: &lnwire.UpdateFailHTLC{ + Reason: []byte{1, 2, 3}, + }, + } + + if err := s.forward(packet); err != nil { + t.Fatalf("can't forward htlc packet: %v", err) + } + + // Get payment result from switch. We expect an unreadable failure + // message error. + deobfuscator := SphinxErrorDecrypter{ + OnionErrorDecrypter: &mockOnionErrorDecryptor{ + err: ErrUnreadableFailureMessage, + }, + } + + resultChan, err := s.GetPaymentResult( + paymentID, rhash, &deobfuscator, + ) + if err != nil { + t.Fatal(err) + } + + select { + case result := <-resultChan: + if result.Error != ErrUnreadableFailureMessage { + t.Fatal("expected unreadable failure message") + } + + case <-time.After(time.Second): + t.Fatal("err wasn't received") + } +} diff --git a/lnrpc/routerrpc/router.pb.go b/lnrpc/routerrpc/router.pb.go index 57217669d..d303c711f 100644 --- a/lnrpc/routerrpc/router.pb.go +++ b/lnrpc/routerrpc/router.pb.go @@ -103,31 +103,36 @@ const ( Failure_TEMPORARY_NODE_FAILURE Failure_FailureCode = 19 Failure_PERMANENT_NODE_FAILURE Failure_FailureCode = 20 Failure_PERMANENT_CHANNEL_FAILURE Failure_FailureCode = 21 + //* + //An unreadable failure result is returned if the received failure message + //cannot be decrypted. In that case the error source is unknown. + Failure_UNREADABLE_FAILURE Failure_FailureCode = 999 ) var Failure_FailureCode_name = map[int32]string{ - 0: "RESERVED", - 1: "UNKNOWN_PAYMENT_HASH", - 2: "INCORRECT_PAYMENT_AMOUNT", - 3: "FINAL_INCORRECT_CLTV_EXPIRY", - 4: "FINAL_INCORRECT_HTLC_AMOUNT", - 5: "FINAL_EXPIRY_TOO_SOON", - 6: "INVALID_REALM", - 7: "EXPIRY_TOO_SOON", - 8: "INVALID_ONION_VERSION", - 9: "INVALID_ONION_HMAC", - 10: "INVALID_ONION_KEY", - 11: "AMOUNT_BELOW_MINIMUM", - 12: "FEE_INSUFFICIENT", - 13: "INCORRECT_CLTV_EXPIRY", - 14: "CHANNEL_DISABLED", - 15: "TEMPORARY_CHANNEL_FAILURE", - 16: "REQUIRED_NODE_FEATURE_MISSING", - 17: "REQUIRED_CHANNEL_FEATURE_MISSING", - 18: "UNKNOWN_NEXT_PEER", - 19: "TEMPORARY_NODE_FAILURE", - 20: "PERMANENT_NODE_FAILURE", - 21: "PERMANENT_CHANNEL_FAILURE", + 0: "RESERVED", + 1: "UNKNOWN_PAYMENT_HASH", + 2: "INCORRECT_PAYMENT_AMOUNT", + 3: "FINAL_INCORRECT_CLTV_EXPIRY", + 4: "FINAL_INCORRECT_HTLC_AMOUNT", + 5: "FINAL_EXPIRY_TOO_SOON", + 6: "INVALID_REALM", + 7: "EXPIRY_TOO_SOON", + 8: "INVALID_ONION_VERSION", + 9: "INVALID_ONION_HMAC", + 10: "INVALID_ONION_KEY", + 11: "AMOUNT_BELOW_MINIMUM", + 12: "FEE_INSUFFICIENT", + 13: "INCORRECT_CLTV_EXPIRY", + 14: "CHANNEL_DISABLED", + 15: "TEMPORARY_CHANNEL_FAILURE", + 16: "REQUIRED_NODE_FEATURE_MISSING", + 17: "REQUIRED_CHANNEL_FEATURE_MISSING", + 18: "UNKNOWN_NEXT_PEER", + 19: "TEMPORARY_NODE_FAILURE", + 20: "PERMANENT_NODE_FAILURE", + 21: "PERMANENT_CHANNEL_FAILURE", + 999: "UNREADABLE_FAILURE", } var Failure_FailureCode_value = map[string]int32{ @@ -153,6 +158,7 @@ var Failure_FailureCode_value = map[string]int32{ "TEMPORARY_NODE_FAILURE": 19, "PERMANENT_NODE_FAILURE": 20, "PERMANENT_CHANNEL_FAILURE": 21, + "UNREADABLE_FAILURE": 999, } func (x Failure_FailureCode) String() string { @@ -1167,110 +1173,110 @@ func init() { func init() { proto.RegisterFile("routerrpc/router.proto", fileDescriptor_7a0613f69d37b0a5) } var fileDescriptor_7a0613f69d37b0a5 = []byte{ - // 1635 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x57, 0x5f, 0x73, 0x22, 0xc7, - 0x11, 0x37, 0x02, 0x09, 0xd1, 0xfc, 0xd1, 0x6a, 0xa4, 0xd3, 0xed, 0xa1, 0xd3, 0x59, 0x5e, 0x27, - 0x67, 0xd5, 0x95, 0x23, 0x39, 0xa4, 0xce, 0xe5, 0xa7, 0xa4, 0x38, 0x18, 0xcc, 0xfa, 0x60, 0x57, - 0x1e, 0xe0, 0xec, 0x4b, 0x1e, 0xa6, 0x46, 0xec, 0x08, 0xb6, 0x04, 0xbb, 0x78, 0x77, 0x70, 0x4e, - 0x79, 0xc8, 0x5b, 0x5e, 0x52, 0x95, 0xcf, 0x92, 0x7c, 0x82, 0x7c, 0x8f, 0x7c, 0x95, 0x3c, 0xa5, - 0x66, 0x66, 0x81, 0x05, 0xa1, 0x8b, 0x9f, 0xc4, 0xfe, 0xfa, 0xd7, 0x7f, 0xa6, 0x7b, 0xba, 0xa7, - 0x05, 0x27, 0x51, 0x38, 0x17, 0x3c, 0x8a, 0x66, 0xc3, 0x2b, 0xfd, 0xeb, 0x72, 0x16, 0x85, 0x22, - 0x44, 0x85, 0x25, 0x5e, 0x2d, 0x44, 0xb3, 0xa1, 0x46, 0xad, 0xff, 0xee, 0x00, 0xea, 0xf1, 0xc0, - 0xbb, 0x66, 0xf7, 0x53, 0x1e, 0x08, 0xc2, 0x7f, 0x9a, 0xf3, 0x58, 0x20, 0x04, 0x39, 0x8f, 0xc7, - 0xc2, 0xcc, 0x9c, 0x67, 0x2e, 0x4a, 0x44, 0xfd, 0x46, 0x06, 0x64, 0xd9, 0x54, 0x98, 0x3b, 0xe7, - 0x99, 0x8b, 0x2c, 0x91, 0x3f, 0xd1, 0x67, 0x50, 0x9a, 0x69, 0x3d, 0x3a, 0x66, 0xf1, 0xd8, 0xcc, - 0x2a, 0x76, 0x31, 0xc1, 0xda, 0x2c, 0x1e, 0xa3, 0x0b, 0x30, 0x6e, 0xfd, 0x80, 0x4d, 0xe8, 0x70, - 0x22, 0x7e, 0xa6, 0x1e, 0x9f, 0x08, 0x66, 0xe6, 0xce, 0x33, 0x17, 0xbb, 0xa4, 0xa2, 0xf0, 0xc6, - 0x44, 0xfc, 0xdc, 0x94, 0x28, 0xfa, 0x02, 0x0e, 0x16, 0xc6, 0x22, 0x1d, 0x85, 0xb9, 0x7b, 0x9e, - 0xb9, 0x28, 0x90, 0xca, 0x6c, 0x3d, 0xb6, 0x2f, 0xe0, 0x40, 0xf8, 0x53, 0x1e, 0xce, 0x05, 0x8d, - 0xf9, 0x30, 0x0c, 0xbc, 0xd8, 0xdc, 0xd3, 0x16, 0x13, 0xb8, 0xa7, 0x51, 0x64, 0x41, 0xf9, 0x96, - 0x73, 0x3a, 0xf1, 0xa7, 0xbe, 0xa0, 0x31, 0x13, 0x66, 0x5e, 0x85, 0x5e, 0xbc, 0xe5, 0xbc, 0x23, - 0xb1, 0x1e, 0x13, 0x32, 0xbe, 0x70, 0x2e, 0x46, 0xa1, 0x1f, 0x8c, 0xe8, 0x70, 0xcc, 0x02, 0xea, - 0x7b, 0xe6, 0xfe, 0x79, 0xe6, 0x22, 0x47, 0x2a, 0x0b, 0xbc, 0x31, 0x66, 0x81, 0xed, 0xa1, 0x33, - 0x00, 0x75, 0x06, 0x65, 0xce, 0x2c, 0x28, 0x8f, 0x05, 0x89, 0x28, 0x5b, 0xa8, 0x06, 0x45, 0x95, - 0x60, 0x3a, 0xf6, 0x03, 0x11, 0x9b, 0x70, 0x9e, 0xbd, 0x28, 0xd6, 0x8c, 0xcb, 0x49, 0x20, 0x73, - 0x4d, 0xa4, 0xa4, 0xed, 0x07, 0x82, 0xa4, 0x49, 0xd6, 0x37, 0x70, 0xd4, 0x8f, 0xd8, 0xf0, 0x6e, - 0x23, 0xf9, 0x9b, 0x69, 0xcd, 0x3c, 0x48, 0xab, 0xf5, 0x57, 0x28, 0x27, 0x4a, 0x3d, 0xc1, 0xc4, - 0x3c, 0x46, 0xbf, 0x81, 0xdd, 0x58, 0x30, 0xc1, 0x15, 0xb9, 0x52, 0x7b, 0x7a, 0xb9, 0xac, 0xf6, - 0x65, 0x8a, 0xc8, 0x89, 0x66, 0xa1, 0x2a, 0xec, 0xcf, 0x22, 0xee, 0x4f, 0xd9, 0x88, 0xab, 0x82, - 0x96, 0xc8, 0xf2, 0x1b, 0x59, 0xb0, 0xab, 0x94, 0x55, 0x39, 0x8b, 0xb5, 0x52, 0xfa, 0x0c, 0x44, - 0x8b, 0xac, 0xdf, 0xc3, 0x81, 0xfa, 0x6e, 0x71, 0xfe, 0xb1, 0x2b, 0xf3, 0x14, 0xf2, 0x6c, 0xaa, - 0x73, 0xaf, 0xaf, 0xcd, 0x1e, 0x9b, 0xca, 0xb4, 0x5b, 0x1e, 0x18, 0x2b, 0xfd, 0x78, 0x16, 0x06, - 0x31, 0x97, 0xa5, 0x90, 0xc6, 0x65, 0x25, 0x64, 0xd9, 0xa6, 0x52, 0x2b, 0xa3, 0xb4, 0x2a, 0x09, - 0xde, 0xe2, 0xbc, 0x1b, 0x33, 0x81, 0x5e, 0xea, 0x1b, 0x40, 0x27, 0xe1, 0xf0, 0x4e, 0xde, 0x29, - 0x76, 0x9f, 0x98, 0x2f, 0x4b, 0xb8, 0x13, 0x0e, 0xef, 0x9a, 0x12, 0xb4, 0xfe, 0xa4, 0xef, 0x76, - 0x3f, 0xd4, 0xb1, 0xff, 0xe2, 0xf4, 0xae, 0x52, 0xb0, 0xf3, 0x78, 0x0a, 0x28, 0x1c, 0xad, 0x19, - 0x4f, 0x4e, 0x91, 0xce, 0x6c, 0x66, 0x23, 0xb3, 0x5f, 0x42, 0xfe, 0x96, 0xf9, 0x93, 0x79, 0xb4, - 0x30, 0x8c, 0x52, 0x65, 0x6a, 0x69, 0x09, 0x59, 0x50, 0xac, 0xbf, 0xe7, 0x21, 0x9f, 0x80, 0xa8, - 0x06, 0xb9, 0x61, 0xe8, 0x2d, 0xaa, 0xfb, 0xe2, 0xa1, 0xda, 0xe2, 0x6f, 0x23, 0xf4, 0x38, 0x51, - 0x5c, 0xf4, 0x07, 0xa8, 0xc8, 0x1b, 0x1d, 0xf0, 0x09, 0x9d, 0xcf, 0x3c, 0xb6, 0x2c, 0xa8, 0x99, - 0xd2, 0x6e, 0x68, 0xc2, 0x40, 0xc9, 0x49, 0x79, 0x98, 0xfe, 0x44, 0xa7, 0x50, 0x18, 0x8b, 0xc9, - 0x50, 0x57, 0x22, 0xa7, 0x9a, 0x62, 0x5f, 0x02, 0xaa, 0x06, 0x16, 0x94, 0xc3, 0xc0, 0x0f, 0x03, - 0x1a, 0x8f, 0x19, 0xad, 0xbd, 0xfe, 0x5a, 0x35, 0x6b, 0x89, 0x14, 0x15, 0xd8, 0x1b, 0xb3, 0xda, - 0xeb, 0xaf, 0xd1, 0xa7, 0x50, 0x54, 0x2d, 0xc3, 0x3f, 0xcc, 0xfc, 0xe8, 0x5e, 0x75, 0x69, 0x99, - 0xa8, 0x2e, 0xc2, 0x0a, 0x41, 0xc7, 0xb0, 0x7b, 0x3b, 0x61, 0xa3, 0x58, 0x75, 0x66, 0x99, 0xe8, - 0x0f, 0xf4, 0x15, 0x1c, 0x27, 0x39, 0xa0, 0x71, 0x38, 0x8f, 0x86, 0x9c, 0xfa, 0x81, 0xc7, 0x3f, - 0xa8, 0xbe, 0x2c, 0x13, 0x94, 0xc8, 0x7a, 0x4a, 0x64, 0x4b, 0x89, 0xf5, 0x9f, 0x1c, 0x14, 0x53, - 0x09, 0x40, 0x25, 0xd8, 0x27, 0xb8, 0x87, 0xc9, 0x3b, 0xdc, 0x34, 0x3e, 0x41, 0x26, 0x1c, 0x0f, - 0x9c, 0xb7, 0x8e, 0xfb, 0x83, 0x43, 0xaf, 0xeb, 0xef, 0xbb, 0xd8, 0xe9, 0xd3, 0x76, 0xbd, 0xd7, - 0x36, 0x32, 0xe8, 0x39, 0x98, 0xb6, 0xd3, 0x70, 0x09, 0xc1, 0x8d, 0xfe, 0x52, 0x56, 0xef, 0xba, - 0x03, 0xa7, 0x6f, 0xec, 0xa0, 0x4f, 0xe1, 0xb4, 0x65, 0x3b, 0xf5, 0x0e, 0x5d, 0x71, 0x1a, 0x9d, - 0xfe, 0x3b, 0x8a, 0x7f, 0xbc, 0xb6, 0xc9, 0x7b, 0x23, 0xbb, 0x8d, 0xd0, 0xee, 0x77, 0x1a, 0x0b, - 0x0b, 0x39, 0xf4, 0x0c, 0x9e, 0x68, 0x82, 0x56, 0xa1, 0x7d, 0xd7, 0xa5, 0x3d, 0xd7, 0x75, 0x8c, - 0x5d, 0x74, 0x08, 0x65, 0xdb, 0x79, 0x57, 0xef, 0xd8, 0x4d, 0x4a, 0x70, 0xbd, 0xd3, 0x35, 0xf6, - 0xd0, 0x11, 0x1c, 0x6c, 0xf2, 0xf2, 0xd2, 0xc4, 0x82, 0xe7, 0x3a, 0xb6, 0xeb, 0xd0, 0x77, 0x98, - 0xf4, 0x6c, 0xd7, 0x31, 0xf6, 0xd1, 0x09, 0xa0, 0x75, 0x51, 0xbb, 0x5b, 0x6f, 0x18, 0x05, 0xf4, - 0x04, 0x0e, 0xd7, 0xf1, 0xb7, 0xf8, 0xbd, 0x01, 0x32, 0x0d, 0x3a, 0x30, 0xfa, 0x06, 0x77, 0xdc, - 0x1f, 0x68, 0xd7, 0x76, 0xec, 0xee, 0xa0, 0x6b, 0x14, 0xd1, 0x31, 0x18, 0x2d, 0x8c, 0xa9, 0xed, - 0xf4, 0x06, 0xad, 0x96, 0xdd, 0xb0, 0xb1, 0xd3, 0x37, 0x4a, 0xda, 0xf3, 0xb6, 0x83, 0x97, 0xa5, - 0x42, 0xa3, 0x5d, 0x77, 0x1c, 0xdc, 0xa1, 0x4d, 0xbb, 0x57, 0x7f, 0xd3, 0xc1, 0x4d, 0xa3, 0x82, - 0xce, 0xe0, 0x59, 0x1f, 0x77, 0xaf, 0x5d, 0x52, 0x27, 0xef, 0xe9, 0x42, 0xde, 0xaa, 0xdb, 0x9d, - 0x01, 0xc1, 0xc6, 0x01, 0xfa, 0x0c, 0xce, 0x08, 0xfe, 0x7e, 0x60, 0x13, 0xdc, 0xa4, 0x8e, 0xdb, - 0xc4, 0xb4, 0x85, 0xeb, 0xfd, 0x01, 0xc1, 0xb4, 0x6b, 0xf7, 0x7a, 0xb6, 0xf3, 0xad, 0x61, 0xa0, - 0x5f, 0xc1, 0xf9, 0x92, 0xb2, 0x34, 0xb0, 0xc1, 0x3a, 0x94, 0xe7, 0x5b, 0xd4, 0xd3, 0xc1, 0x3f, - 0xf6, 0xe9, 0x35, 0xc6, 0xc4, 0x40, 0xa8, 0x0a, 0x27, 0x2b, 0xf7, 0xda, 0x41, 0xe2, 0xfb, 0x48, - 0xca, 0xae, 0x31, 0xe9, 0xd6, 0x1d, 0x59, 0xe0, 0x35, 0xd9, 0xb1, 0x0c, 0x7b, 0x25, 0xdb, 0x0c, - 0xfb, 0xc9, 0x77, 0xb9, 0xfd, 0x1d, 0x23, 0x6b, 0xfd, 0x33, 0x0b, 0xe5, 0xb5, 0x66, 0x41, 0xcf, - 0xa1, 0x10, 0xfb, 0xa3, 0x80, 0x09, 0xd9, 0xce, 0xba, 0xd3, 0x57, 0x80, 0x7a, 0x2d, 0xc6, 0xcc, - 0x0f, 0xf4, 0x88, 0xd1, 0x23, 0xb6, 0xa0, 0x10, 0x35, 0x60, 0x9e, 0x42, 0x7e, 0xf1, 0xda, 0x64, - 0x55, 0x63, 0xed, 0x0d, 0xf5, 0x2b, 0xf3, 0x1c, 0x0a, 0x72, 0x86, 0xc5, 0x82, 0x4d, 0x67, 0xaa, - 0xe7, 0xca, 0x64, 0x05, 0xa0, 0xcf, 0xa1, 0x3c, 0xe5, 0x71, 0xcc, 0x46, 0x9c, 0xea, 0xbe, 0x01, - 0xc5, 0x28, 0x25, 0x60, 0x4b, 0xb5, 0xcf, 0xe7, 0xb0, 0xe8, 0xe3, 0x84, 0xb4, 0xab, 0x49, 0x09, - 0xa8, 0x49, 0x9b, 0x23, 0x54, 0xb0, 0xa4, 0x3d, 0xd3, 0x23, 0x54, 0x30, 0xf4, 0x0a, 0x0e, 0xf5, - 0x0c, 0xf0, 0x03, 0x7f, 0x3a, 0x9f, 0xea, 0x59, 0x90, 0x57, 0x21, 0x1f, 0xa8, 0x59, 0xa0, 0x71, - 0x35, 0x12, 0x9e, 0xc1, 0xfe, 0x0d, 0x8b, 0xb9, 0x9c, 0xde, 0x49, 0xaf, 0xe6, 0xe5, 0x77, 0x8b, - 0x73, 0x29, 0x92, 0x33, 0x3d, 0x92, 0x53, 0xa8, 0xa0, 0x45, 0xb7, 0x9c, 0x13, 0x99, 0xc7, 0xa5, - 0x07, 0xf6, 0x61, 0xe5, 0xa1, 0x98, 0xf2, 0xa0, 0x71, 0xe5, 0xe1, 0x15, 0x1c, 0xf2, 0x0f, 0x22, - 0x62, 0x34, 0x9c, 0xb1, 0x9f, 0xe6, 0x9c, 0x7a, 0x4c, 0x30, 0xb3, 0xa4, 0x92, 0x7b, 0xa0, 0x04, - 0xae, 0xc2, 0x9b, 0x4c, 0x30, 0xeb, 0x39, 0x54, 0x09, 0x8f, 0xb9, 0xe8, 0xfa, 0x71, 0xec, 0x87, - 0x41, 0x23, 0x0c, 0x44, 0x14, 0x4e, 0x92, 0x47, 0xc0, 0x3a, 0x83, 0xd3, 0xad, 0x52, 0x3d, 0xc5, - 0xa5, 0xf2, 0xf7, 0x73, 0x1e, 0xdd, 0x6f, 0x57, 0x7e, 0x0b, 0xa7, 0x5b, 0xa5, 0xc9, 0x13, 0xf0, - 0x25, 0xec, 0x06, 0xa1, 0xc7, 0x63, 0x33, 0xa3, 0x96, 0x80, 0x93, 0xd4, 0xbc, 0x75, 0x42, 0x8f, - 0xb7, 0xfd, 0x58, 0x84, 0xd1, 0x3d, 0xd1, 0x24, 0xeb, 0xdf, 0x19, 0x28, 0xa6, 0x60, 0x74, 0x02, - 0x7b, 0xb3, 0xf9, 0xcd, 0x1d, 0xbf, 0x4f, 0x2e, 0x55, 0xf2, 0x85, 0x5e, 0x42, 0x65, 0xc2, 0x62, - 0x41, 0xe5, 0xf8, 0xa3, 0xb2, 0x48, 0xc9, 0x9b, 0xb7, 0x81, 0xa2, 0x6f, 0xe0, 0x69, 0x28, 0xc6, - 0x3c, 0xd2, 0xeb, 0x4c, 0x3c, 0x1f, 0x0e, 0x79, 0x1c, 0xd3, 0x59, 0x14, 0xde, 0xa8, 0xab, 0xb6, - 0x43, 0x1e, 0x13, 0xa3, 0xd7, 0xb0, 0x9f, 0xdc, 0x91, 0xd8, 0xcc, 0xa9, 0xd0, 0x9f, 0x3d, 0x7c, - 0x2a, 0x16, 0xd1, 0x2f, 0xa9, 0xd6, 0xbf, 0x32, 0x50, 0x59, 0x17, 0xa2, 0x17, 0xea, 0xf6, 0xab, - 0x2b, 0xe8, 0x7b, 0xea, 0x1c, 0x39, 0x92, 0x42, 0x7e, 0xf1, 0x59, 0x6a, 0x70, 0x3c, 0xf5, 0x03, - 0x3a, 0xe3, 0x01, 0x9b, 0xf8, 0x7f, 0xe1, 0x74, 0xb1, 0x4c, 0x64, 0x15, 0x7b, 0xab, 0x0c, 0x59, - 0x50, 0x5a, 0x3b, 0x74, 0x4e, 0x1d, 0x7a, 0x0d, 0x7b, 0xf5, 0x8f, 0x0c, 0x94, 0xd2, 0x6b, 0x11, - 0x2a, 0x43, 0xc1, 0x76, 0x68, 0xab, 0x63, 0x7f, 0xdb, 0xee, 0x1b, 0x9f, 0xc8, 0xcf, 0xde, 0xa0, - 0xd1, 0xc0, 0xb8, 0x89, 0x9b, 0x46, 0x06, 0x21, 0xa8, 0xc8, 0x79, 0x80, 0x9b, 0xb4, 0x6f, 0x77, - 0xb1, 0x3b, 0x90, 0x8f, 0xc3, 0x11, 0x1c, 0x24, 0x98, 0xe3, 0x52, 0xe2, 0x0e, 0xfa, 0xd8, 0xc8, - 0x22, 0x03, 0x4a, 0x09, 0x88, 0x09, 0x71, 0x89, 0x91, 0x93, 0x13, 0x2d, 0x41, 0x1e, 0x3e, 0x34, - 0x4d, 0xdc, 0xaf, 0xdb, 0x9d, 0x9e, 0xb1, 0x5b, 0xfb, 0x5b, 0x0e, 0xf6, 0xd4, 0x1a, 0x11, 0xa1, - 0x36, 0x14, 0x53, 0xfb, 0x38, 0x3a, 0x4b, 0x55, 0xe0, 0xe1, 0x9e, 0x5e, 0x35, 0xb7, 0xef, 0x79, - 0xf3, 0xf8, 0xab, 0x0c, 0xfa, 0x0e, 0x4a, 0xe9, 0xed, 0x12, 0xa5, 0xb7, 0x86, 0x2d, 0x6b, 0xe7, - 0x47, 0x6d, 0xbd, 0x05, 0x03, 0xc7, 0xc2, 0x9f, 0xca, 0x2d, 0x21, 0xd9, 0xdb, 0x50, 0x35, 0xc5, - 0xdf, 0x58, 0x06, 0xab, 0xa7, 0x5b, 0x65, 0x49, 0x7f, 0x74, 0xf4, 0x11, 0x93, 0xcd, 0xe9, 0xc1, - 0x11, 0xd7, 0xd7, 0xb5, 0xea, 0x8b, 0xc7, 0xc4, 0x89, 0x35, 0x0f, 0x8e, 0xb6, 0x74, 0x32, 0xfa, - 0x75, 0x3a, 0x82, 0x47, 0xe7, 0x40, 0xf5, 0xe5, 0xff, 0xa3, 0xad, 0xbc, 0x6c, 0x69, 0xf9, 0x35, - 0x2f, 0x8f, 0x0f, 0x8c, 0x35, 0x2f, 0x1f, 0x99, 0x1c, 0x6f, 0x7e, 0xfb, 0xc7, 0xab, 0x91, 0x2f, - 0xc6, 0xf3, 0x9b, 0xcb, 0x61, 0x38, 0xbd, 0x9a, 0xf8, 0xa3, 0xb1, 0x08, 0xfc, 0x60, 0x14, 0x70, - 0xf1, 0xe7, 0x30, 0xba, 0xbb, 0x9a, 0x04, 0xde, 0x95, 0xda, 0x44, 0xaf, 0x96, 0xe6, 0x6e, 0xf6, - 0xd4, 0xff, 0x71, 0xbf, 0xfb, 0x5f, 0x00, 0x00, 0x00, 0xff, 0xff, 0x2f, 0xaa, 0x9f, 0x4e, 0xf7, - 0x0d, 0x00, 0x00, + // 1648 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x57, 0x4f, 0x73, 0x22, 0xc7, + 0x15, 0x37, 0x02, 0x84, 0x78, 0xfc, 0xd1, 0xa8, 0xa5, 0x95, 0x58, 0xb4, 0x5a, 0xcb, 0xe3, 0x64, + 0xad, 0xda, 0x72, 0x24, 0x87, 0xd4, 0xba, 0x7c, 0x4a, 0x8a, 0x85, 0xc6, 0x8c, 0x17, 0x66, 0xe4, + 0x06, 0xd6, 0xde, 0xe4, 0xd0, 0xd5, 0x62, 0x5a, 0x30, 0x25, 0x98, 0xc1, 0x33, 0x8d, 0xb3, 0xca, + 0x21, 0xb7, 0x1c, 0xf3, 0x29, 0xf2, 0x01, 0x92, 0x4f, 0x90, 0xaf, 0x93, 0x7b, 0x8e, 0x39, 0xa5, + 0xba, 0x7b, 0x80, 0x01, 0xa1, 0xf5, 0x9e, 0xc4, 0xfc, 0xde, 0xaf, 0xdf, 0xdf, 0x7e, 0xaf, 0x9f, + 0xe0, 0x38, 0x0c, 0xe6, 0x82, 0x87, 0xe1, 0x6c, 0x78, 0xa5, 0x7f, 0x5d, 0xce, 0xc2, 0x40, 0x04, + 0x28, 0xbf, 0xc4, 0xab, 0xf9, 0x70, 0x36, 0xd4, 0xa8, 0xf9, 0xbf, 0x1d, 0x40, 0x3d, 0xee, 0xbb, + 0xd7, 0xec, 0x7e, 0xca, 0x7d, 0x41, 0xf8, 0x4f, 0x73, 0x1e, 0x09, 0x84, 0x20, 0xe3, 0xf2, 0x48, + 0x54, 0x52, 0xe7, 0xa9, 0x8b, 0x22, 0x51, 0xbf, 0x91, 0x01, 0x69, 0x36, 0x15, 0x95, 0x9d, 0xf3, + 0xd4, 0x45, 0x9a, 0xc8, 0x9f, 0xe8, 0x33, 0x28, 0xce, 0xf4, 0x39, 0x3a, 0x66, 0xd1, 0xb8, 0x92, + 0x56, 0xec, 0x42, 0x8c, 0xb5, 0x59, 0x34, 0x46, 0x17, 0x60, 0xdc, 0x7a, 0x3e, 0x9b, 0xd0, 0xe1, + 0x44, 0xfc, 0x4c, 0x5d, 0x3e, 0x11, 0xac, 0x92, 0x39, 0x4f, 0x5d, 0x64, 0x49, 0x59, 0xe1, 0x8d, + 0x89, 0xf8, 0xb9, 0x29, 0x51, 0xf4, 0x05, 0xec, 0x2f, 0x94, 0x85, 0xda, 0x8b, 0x4a, 0xf6, 0x3c, + 0x75, 0x91, 0x27, 0xe5, 0xd9, 0xba, 0x6f, 0x5f, 0xc0, 0xbe, 0xf0, 0xa6, 0x3c, 0x98, 0x0b, 0x1a, + 0xf1, 0x61, 0xe0, 0xbb, 0x51, 0x65, 0x57, 0x6b, 0x8c, 0xe1, 0x9e, 0x46, 0x91, 0x09, 0xa5, 0x5b, + 0xce, 0xe9, 0xc4, 0x9b, 0x7a, 0x82, 0x46, 0x4c, 0x54, 0x72, 0xca, 0xf5, 0xc2, 0x2d, 0xe7, 0x1d, + 0x89, 0xf5, 0x98, 0x90, 0xfe, 0x05, 0x73, 0x31, 0x0a, 0x3c, 0x7f, 0x44, 0x87, 0x63, 0xe6, 0x53, + 0xcf, 0xad, 0xec, 0x9d, 0xa7, 0x2e, 0x32, 0xa4, 0xbc, 0xc0, 0x1b, 0x63, 0xe6, 0x5b, 0x2e, 0x3a, + 0x03, 0x50, 0x31, 0x28, 0x75, 0x95, 0xbc, 0xb2, 0x98, 0x97, 0x88, 0xd2, 0x85, 0x6a, 0x50, 0x50, + 0x09, 0xa6, 0x63, 0xcf, 0x17, 0x51, 0x05, 0xce, 0xd3, 0x17, 0x85, 0x9a, 0x71, 0x39, 0xf1, 0x65, + 0xae, 0x89, 0x94, 0xb4, 0x3d, 0x5f, 0x90, 0x24, 0xc9, 0xfc, 0x06, 0x0e, 0xfb, 0x21, 0x1b, 0xde, + 0x6d, 0x24, 0x7f, 0x33, 0xad, 0xa9, 0x07, 0x69, 0x35, 0xff, 0x0a, 0xa5, 0xf8, 0x50, 0x4f, 0x30, + 0x31, 0x8f, 0xd0, 0x6f, 0x20, 0x1b, 0x09, 0x26, 0xb8, 0x22, 0x97, 0x6b, 0x27, 0x97, 0xcb, 0x6a, + 0x5f, 0x26, 0x88, 0x9c, 0x68, 0x16, 0xaa, 0xc2, 0xde, 0x2c, 0xe4, 0xde, 0x94, 0x8d, 0xb8, 0x2a, + 0x68, 0x91, 0x2c, 0xbf, 0x91, 0x09, 0x59, 0x75, 0x58, 0x95, 0xb3, 0x50, 0x2b, 0x26, 0x63, 0x20, + 0x5a, 0x64, 0xfe, 0x1e, 0xf6, 0xd5, 0x77, 0x8b, 0xf3, 0x0f, 0x5d, 0x99, 0x13, 0xc8, 0xb1, 0xa9, + 0xce, 0xbd, 0xbe, 0x36, 0xbb, 0x6c, 0x2a, 0xd3, 0x6e, 0xba, 0x60, 0xac, 0xce, 0x47, 0xb3, 0xc0, + 0x8f, 0xb8, 0x2c, 0x85, 0x54, 0x2e, 0x2b, 0x21, 0xcb, 0x36, 0x95, 0xa7, 0x52, 0xea, 0x54, 0x39, + 0xc6, 0x5b, 0x9c, 0x77, 0x23, 0x26, 0xd0, 0x0b, 0x7d, 0x03, 0xe8, 0x24, 0x18, 0xde, 0xc9, 0x3b, + 0xc5, 0xee, 0x63, 0xf5, 0x25, 0x09, 0x77, 0x82, 0xe1, 0x5d, 0x53, 0x82, 0xe6, 0x9f, 0xf4, 0xdd, + 0xee, 0x07, 0xda, 0xf7, 0x8f, 0x4e, 0xef, 0x2a, 0x05, 0x3b, 0x8f, 0xa7, 0x80, 0xc2, 0xe1, 0x9a, + 0xf2, 0x38, 0x8a, 0x64, 0x66, 0x53, 0x1b, 0x99, 0xfd, 0x12, 0x72, 0xb7, 0xcc, 0x9b, 0xcc, 0xc3, + 0x85, 0x62, 0x94, 0x28, 0x53, 0x4b, 0x4b, 0xc8, 0x82, 0x62, 0xfe, 0x23, 0x07, 0xb9, 0x18, 0x44, + 0x35, 0xc8, 0x0c, 0x03, 0x77, 0x51, 0xdd, 0xe7, 0x0f, 0x8f, 0x2d, 0xfe, 0x36, 0x02, 0x97, 0x13, + 0xc5, 0x45, 0x7f, 0x80, 0xb2, 0xbc, 0xd1, 0x3e, 0x9f, 0xd0, 0xf9, 0xcc, 0x65, 0xcb, 0x82, 0x56, + 0x12, 0xa7, 0x1b, 0x9a, 0x30, 0x50, 0x72, 0x52, 0x1a, 0x26, 0x3f, 0xd1, 0x29, 0xe4, 0xc7, 0x62, + 0x32, 0xd4, 0x95, 0xc8, 0xa8, 0xa6, 0xd8, 0x93, 0x80, 0xaa, 0x81, 0x09, 0xa5, 0xc0, 0xf7, 0x02, + 0x9f, 0x46, 0x63, 0x46, 0x6b, 0xaf, 0xbe, 0x56, 0xcd, 0x5a, 0x24, 0x05, 0x05, 0xf6, 0xc6, 0xac, + 0xf6, 0xea, 0x6b, 0xf4, 0x29, 0x14, 0x54, 0xcb, 0xf0, 0xf7, 0x33, 0x2f, 0xbc, 0x57, 0x5d, 0x5a, + 0x22, 0xaa, 0x8b, 0xb0, 0x42, 0xd0, 0x11, 0x64, 0x6f, 0x27, 0x6c, 0x14, 0xa9, 0xce, 0x2c, 0x11, + 0xfd, 0x81, 0xbe, 0x82, 0xa3, 0x38, 0x07, 0x34, 0x0a, 0xe6, 0xe1, 0x90, 0x53, 0xcf, 0x77, 0xf9, + 0x7b, 0xd5, 0x97, 0x25, 0x82, 0x62, 0x59, 0x4f, 0x89, 0x2c, 0x29, 0x31, 0xff, 0x9b, 0x81, 0x42, + 0x22, 0x01, 0xa8, 0x08, 0x7b, 0x04, 0xf7, 0x30, 0x79, 0x8b, 0x9b, 0xc6, 0x27, 0xa8, 0x02, 0x47, + 0x03, 0xfb, 0x8d, 0xed, 0xfc, 0x60, 0xd3, 0xeb, 0xfa, 0xbb, 0x2e, 0xb6, 0xfb, 0xb4, 0x5d, 0xef, + 0xb5, 0x8d, 0x14, 0x7a, 0x06, 0x15, 0xcb, 0x6e, 0x38, 0x84, 0xe0, 0x46, 0x7f, 0x29, 0xab, 0x77, + 0x9d, 0x81, 0xdd, 0x37, 0x76, 0xd0, 0xa7, 0x70, 0xda, 0xb2, 0xec, 0x7a, 0x87, 0xae, 0x38, 0x8d, + 0x4e, 0xff, 0x2d, 0xc5, 0x3f, 0x5e, 0x5b, 0xe4, 0x9d, 0x91, 0xde, 0x46, 0x68, 0xf7, 0x3b, 0x8d, + 0x85, 0x86, 0x0c, 0x7a, 0x0a, 0x4f, 0x34, 0x41, 0x1f, 0xa1, 0x7d, 0xc7, 0xa1, 0x3d, 0xc7, 0xb1, + 0x8d, 0x2c, 0x3a, 0x80, 0x92, 0x65, 0xbf, 0xad, 0x77, 0xac, 0x26, 0x25, 0xb8, 0xde, 0xe9, 0x1a, + 0xbb, 0xe8, 0x10, 0xf6, 0x37, 0x79, 0x39, 0xa9, 0x62, 0xc1, 0x73, 0x6c, 0xcb, 0xb1, 0xe9, 0x5b, + 0x4c, 0x7a, 0x96, 0x63, 0x1b, 0x7b, 0xe8, 0x18, 0xd0, 0xba, 0xa8, 0xdd, 0xad, 0x37, 0x8c, 0x3c, + 0x7a, 0x02, 0x07, 0xeb, 0xf8, 0x1b, 0xfc, 0xce, 0x00, 0x99, 0x06, 0xed, 0x18, 0x7d, 0x8d, 0x3b, + 0xce, 0x0f, 0xb4, 0x6b, 0xd9, 0x56, 0x77, 0xd0, 0x35, 0x0a, 0xe8, 0x08, 0x8c, 0x16, 0xc6, 0xd4, + 0xb2, 0x7b, 0x83, 0x56, 0xcb, 0x6a, 0x58, 0xd8, 0xee, 0x1b, 0x45, 0x6d, 0x79, 0x5b, 0xe0, 0x25, + 0x79, 0xa0, 0xd1, 0xae, 0xdb, 0x36, 0xee, 0xd0, 0xa6, 0xd5, 0xab, 0xbf, 0xee, 0xe0, 0xa6, 0x51, + 0x46, 0x67, 0xf0, 0xb4, 0x8f, 0xbb, 0xd7, 0x0e, 0xa9, 0x93, 0x77, 0x74, 0x21, 0x6f, 0xd5, 0xad, + 0xce, 0x80, 0x60, 0x63, 0x1f, 0x7d, 0x06, 0x67, 0x04, 0x7f, 0x3f, 0xb0, 0x08, 0x6e, 0x52, 0xdb, + 0x69, 0x62, 0xda, 0xc2, 0xf5, 0xfe, 0x80, 0x60, 0xda, 0xb5, 0x7a, 0x3d, 0xcb, 0xfe, 0xd6, 0x30, + 0xd0, 0xaf, 0xe0, 0x7c, 0x49, 0x59, 0x2a, 0xd8, 0x60, 0x1d, 0xc8, 0xf8, 0x16, 0xf5, 0xb4, 0xf1, + 0x8f, 0x7d, 0x7a, 0x8d, 0x31, 0x31, 0x10, 0xaa, 0xc2, 0xf1, 0xca, 0xbc, 0x36, 0x10, 0xdb, 0x3e, + 0x94, 0xb2, 0x6b, 0x4c, 0xba, 0x75, 0x5b, 0x16, 0x78, 0x4d, 0x76, 0x24, 0xdd, 0x5e, 0xc9, 0x36, + 0xdd, 0x7e, 0x82, 0x4e, 0x00, 0x0d, 0x6c, 0x82, 0xeb, 0x4d, 0x19, 0xe6, 0x12, 0xff, 0x4f, 0xee, + 0xbb, 0xcc, 0xde, 0x8e, 0x91, 0x36, 0xff, 0x99, 0x86, 0xd2, 0x5a, 0x17, 0xa1, 0x67, 0x90, 0x8f, + 0xbc, 0x91, 0xcf, 0x84, 0xec, 0x73, 0x3d, 0x02, 0x56, 0x80, 0x7a, 0x46, 0xc6, 0xcc, 0xf3, 0xf5, + 0xec, 0xd1, 0xb3, 0x37, 0xaf, 0x10, 0x35, 0x79, 0x4e, 0x20, 0xb7, 0x78, 0x86, 0xd2, 0xaa, 0xe3, + 0x76, 0x87, 0xfa, 0xf9, 0x79, 0x06, 0x79, 0x39, 0xdc, 0x22, 0xc1, 0xa6, 0x33, 0xd5, 0x8c, 0x25, + 0xb2, 0x02, 0xd0, 0xe7, 0x50, 0x9a, 0xf2, 0x28, 0x62, 0x23, 0x4e, 0x75, 0x43, 0x81, 0x62, 0x14, + 0x63, 0xb0, 0xa5, 0xfa, 0xea, 0x73, 0x58, 0x34, 0x78, 0x4c, 0xca, 0x6a, 0x52, 0x0c, 0x6a, 0xd2, + 0xe6, 0x6c, 0x15, 0x2c, 0xee, 0xdb, 0xe4, 0x6c, 0x15, 0x0c, 0xbd, 0x84, 0x03, 0x3d, 0x1c, 0x3c, + 0xdf, 0x9b, 0xce, 0xa7, 0x7a, 0x48, 0xe4, 0x94, 0xcb, 0xfb, 0x6a, 0x48, 0x68, 0x5c, 0xcd, 0x8a, + 0xa7, 0xb0, 0x77, 0xc3, 0x22, 0x2e, 0xc7, 0x7a, 0xdc, 0xc4, 0x39, 0xf9, 0xdd, 0xe2, 0x5c, 0x8a, + 0xe4, 0xb0, 0x0f, 0xe5, 0x78, 0xca, 0x6b, 0xd1, 0x2d, 0xe7, 0x44, 0xe6, 0x71, 0x69, 0x81, 0xbd, + 0x5f, 0x59, 0x28, 0x24, 0x2c, 0x68, 0x5c, 0x59, 0x78, 0x09, 0x07, 0xfc, 0xbd, 0x08, 0x19, 0x0d, + 0x66, 0xec, 0xa7, 0x39, 0xa7, 0x2e, 0x13, 0xac, 0x52, 0x54, 0xc9, 0xdd, 0x57, 0x02, 0x47, 0xe1, + 0x4d, 0x26, 0x98, 0xf9, 0x0c, 0xaa, 0x84, 0x47, 0x5c, 0x74, 0xbd, 0x28, 0xf2, 0x02, 0xbf, 0x11, + 0xf8, 0x22, 0x0c, 0x26, 0xf1, 0xeb, 0x60, 0x9e, 0xc1, 0xe9, 0x56, 0xa9, 0x1e, 0xef, 0xf2, 0xf0, + 0xf7, 0x73, 0x1e, 0xde, 0x6f, 0x3f, 0xfc, 0x06, 0x4e, 0xb7, 0x4a, 0xe3, 0xb7, 0xe1, 0x4b, 0xc8, + 0xfa, 0x81, 0xcb, 0xa3, 0x4a, 0x4a, 0x6d, 0x07, 0xc7, 0x89, 0x41, 0x6c, 0x07, 0x2e, 0x6f, 0x7b, + 0x91, 0x08, 0xc2, 0x7b, 0xa2, 0x49, 0xe6, 0xbf, 0x53, 0x50, 0x48, 0xc0, 0xe8, 0x18, 0x76, 0x67, + 0xf3, 0x9b, 0x3b, 0x7e, 0x1f, 0x5f, 0xaa, 0xf8, 0x0b, 0xbd, 0x80, 0xf2, 0x84, 0x45, 0x82, 0xca, + 0xb9, 0x48, 0x65, 0x91, 0xe2, 0xc7, 0x70, 0x03, 0x45, 0xdf, 0xc0, 0x49, 0x20, 0xc6, 0x3c, 0xd4, + 0x7b, 0x4e, 0x34, 0x1f, 0x0e, 0x79, 0x14, 0xd1, 0x59, 0x18, 0xdc, 0xa8, 0xab, 0xb6, 0x43, 0x1e, + 0x13, 0xa3, 0x57, 0xb0, 0x17, 0xdf, 0x91, 0xa8, 0x92, 0x51, 0xae, 0x3f, 0x7d, 0xf8, 0x86, 0x2c, + 0xbc, 0x5f, 0x52, 0xcd, 0x7f, 0xa5, 0xa0, 0xbc, 0x2e, 0x44, 0xcf, 0xd5, 0xed, 0x57, 0x57, 0xd0, + 0x73, 0x55, 0x1c, 0x19, 0x92, 0x40, 0x3e, 0x3a, 0x96, 0x1a, 0x1c, 0x4d, 0x3d, 0x9f, 0xce, 0xb8, + 0xcf, 0x26, 0xde, 0x5f, 0x38, 0x5d, 0x6c, 0x19, 0x69, 0xc5, 0xde, 0x2a, 0x43, 0x26, 0x14, 0xd7, + 0x82, 0xce, 0xa8, 0xa0, 0xd7, 0xb0, 0x97, 0x7f, 0x4f, 0x41, 0x31, 0xb9, 0x2f, 0xa1, 0x12, 0xe4, + 0x2d, 0x9b, 0xb6, 0x3a, 0xd6, 0xb7, 0xed, 0xbe, 0xf1, 0x89, 0xfc, 0xec, 0x0d, 0x1a, 0x0d, 0x8c, + 0x9b, 0xb8, 0x69, 0xa4, 0x10, 0x82, 0xb2, 0x1c, 0x08, 0xb8, 0x49, 0xfb, 0x56, 0x17, 0x3b, 0x03, + 0xf9, 0x6a, 0x1c, 0xc2, 0x7e, 0x8c, 0xd9, 0x0e, 0x25, 0xce, 0xa0, 0x8f, 0x8d, 0x34, 0x32, 0xa0, + 0x18, 0x83, 0x98, 0x10, 0x87, 0x18, 0x19, 0x39, 0xea, 0x62, 0xe4, 0xe1, 0x0b, 0xd4, 0xc4, 0xfd, + 0xba, 0xd5, 0xe9, 0x19, 0xd9, 0xda, 0xdf, 0x32, 0xb0, 0xab, 0xf6, 0x8b, 0x10, 0xb5, 0xa1, 0x90, + 0x58, 0xd4, 0xd1, 0x59, 0xa2, 0x02, 0x0f, 0x17, 0xf8, 0x6a, 0x65, 0xfb, 0x02, 0x38, 0x8f, 0xbe, + 0x4a, 0xa1, 0xef, 0xa0, 0x98, 0x5c, 0x3b, 0x51, 0x72, 0x9d, 0xd8, 0xb2, 0x8f, 0x7e, 0x50, 0xd7, + 0x1b, 0x30, 0x70, 0x24, 0xbc, 0xa9, 0x5c, 0x1f, 0xe2, 0x85, 0x0e, 0x55, 0x13, 0xfc, 0x8d, 0x2d, + 0xb1, 0x7a, 0xba, 0x55, 0x16, 0xf7, 0x47, 0x47, 0x87, 0x18, 0xaf, 0x54, 0x0f, 0x42, 0x5c, 0xdf, + 0xe3, 0xaa, 0xcf, 0x1f, 0x13, 0xc7, 0xda, 0x5c, 0x38, 0xdc, 0xd2, 0xc9, 0xe8, 0xd7, 0x49, 0x0f, + 0x1e, 0x9d, 0x03, 0xd5, 0x17, 0xbf, 0x44, 0x5b, 0x59, 0xd9, 0xd2, 0xf2, 0x6b, 0x56, 0x1e, 0x1f, + 0x18, 0x6b, 0x56, 0x3e, 0x30, 0x39, 0x5e, 0xff, 0xf6, 0x8f, 0x57, 0x23, 0x4f, 0x8c, 0xe7, 0x37, + 0x97, 0xc3, 0x60, 0x7a, 0x35, 0xf1, 0x46, 0x63, 0xe1, 0x7b, 0xfe, 0xc8, 0xe7, 0xe2, 0xcf, 0x41, + 0x78, 0x77, 0x35, 0xf1, 0xdd, 0x2b, 0xb5, 0xa2, 0x5e, 0x2d, 0xd5, 0xdd, 0xec, 0xaa, 0x7f, 0xf0, + 0x7e, 0xf7, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x7c, 0x28, 0x1a, 0x3a, 0x10, 0x0e, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. diff --git a/lnrpc/routerrpc/router.proto b/lnrpc/routerrpc/router.proto index 2581ce595..670dd06e6 100644 --- a/lnrpc/routerrpc/router.proto +++ b/lnrpc/routerrpc/router.proto @@ -194,6 +194,12 @@ message Failure { TEMPORARY_NODE_FAILURE = 19; PERMANENT_NODE_FAILURE = 20; PERMANENT_CHANNEL_FAILURE = 21; + + /** + An unreadable failure result is returned if the received failure message + cannot be decrypted. In that case the error source is unknown. + */ + UNREADABLE_FAILURE = 999; } /// Failure code as defined in the Lightning spec diff --git a/lnrpc/routerrpc/router_server.go b/lnrpc/routerrpc/router_server.go index 21bc233e9..21ef6a660 100644 --- a/lnrpc/routerrpc/router_server.go +++ b/lnrpc/routerrpc/router_server.go @@ -310,6 +310,11 @@ func (s *Server) SendToRoute(ctx context.Context, func marshallError(sendError error) (*Failure, error) { response := &Failure{} + if sendError == htlcswitch.ErrUnreadableFailureMessage { + response.Code = Failure_UNREADABLE_FAILURE + return response, nil + } + fErr, ok := sendError.(*htlcswitch.ForwardingError) if !ok { return nil, sendError diff --git a/routing/payment_lifecycle.go b/routing/payment_lifecycle.go index cec91e9dd..32f6883ea 100644 --- a/routing/payment_lifecycle.go +++ b/routing/payment_lifecycle.go @@ -17,7 +17,7 @@ import ( type errNoRoute struct { // lastError is the error encountered during the last payment attempt, // if at least one attempt has been made. - lastError *htlcswitch.ForwardingError + lastError error } // Error returns a string representation of the error. @@ -37,7 +37,7 @@ type paymentLifecycle struct { finalCLTVDelta uint16 attempt *channeldb.PaymentAttemptInfo circuit *sphinx.Circuit - lastError *htlcswitch.ForwardingError + lastError error } // resumePayment resumes the paymentLifecycle from the current state. @@ -341,25 +341,16 @@ func (p *paymentLifecycle) sendPaymentAttempt(firstHop lnwire.ShortChannelID, // handleSendError inspects the given error from the Switch and determines // whether we should make another payment attempt. func (p *paymentLifecycle) handleSendError(sendErr error) error { - var reason channeldb.FailureReason - // If an internal, non-forwarding error occurred, we can stop trying. - fErr, ok := sendErr.(*htlcswitch.ForwardingError) - if !ok { - reason = channeldb.FailureReasonError - } else { - var final bool - final, reason = p.router.processSendError( - p.paySession, &p.attempt.Route, fErr, - ) + final, reason := p.router.processSendError( + p.paySession, &p.attempt.Route, sendErr, + ) + if !final { + // Save the forwarding error so it can be returned if + // this turns out to be the last attempt. + p.lastError = sendErr - if !final { - // Save the forwarding error so it can be returned if - // this turns out to be the last attempt. - p.lastError = fErr - - return nil - } + return nil } log.Debugf("Payment %x failed: final_outcome=%v, raw_err=%v", diff --git a/routing/router.go b/routing/router.go index 63f77a319..f67387a6c 100644 --- a/routing/router.go +++ b/routing/router.go @@ -1812,8 +1812,24 @@ func (r *ChannelRouter) sendPayment( // to continue with an alternative route. This is indicated by the boolean // return value. func (r *ChannelRouter) processSendError(paySession PaymentSession, - rt *route.Route, fErr *htlcswitch.ForwardingError) ( - bool, channeldb.FailureReason) { + rt *route.Route, sendErr error) (bool, channeldb.FailureReason) { + + // If the failure message could not be decrypted, attribute the failure + // to our own outgoing channel. + // + // TODO(joostager): Penalize all channels in the route. + if sendErr == htlcswitch.ErrUnreadableFailureMessage { + sendErr = &htlcswitch.ForwardingError{ + FailureSourceIdx: 0, + FailureMessage: lnwire.NewTemporaryChannelFailure(nil), + } + } + + // If an internal, non-forwarding error occurred, we can stop trying. + fErr, ok := sendErr.(*htlcswitch.ForwardingError) + if !ok { + return true, channeldb.FailureReasonError + } var ( failureSourceIdx = fErr.FailureSourceIdx diff --git a/routing/router_test.go b/routing/router_test.go index aa01b2ff4..f83ef7ba8 100644 --- a/routing/router_test.go +++ b/routing/router_test.go @@ -2425,6 +2425,95 @@ func TestEmptyRoutesGenerateSphinxPacket(t *testing.T) { } } +// TestUnknownErrorSource tests that if the source of an error is unknown, all +// edges along the route will be pruned. +func TestUnknownErrorSource(t *testing.T) { + t.Parallel() + + // Setup a network. It contains two paths to c: a->b->c and an + // alternative a->d->c. + chanCapSat := btcutil.Amount(100000) + testChannels := []*testChannel{ + symmetricTestChannel("a", "b", chanCapSat, &testChannelPolicy{ + Expiry: 144, + FeeRate: 400, + MinHTLC: 1, + MaxHTLC: lnwire.NewMSatFromSatoshis(chanCapSat), + }, 1), + symmetricTestChannel("b", "c", chanCapSat, &testChannelPolicy{ + Expiry: 144, + FeeRate: 400, + MinHTLC: 1, + MaxHTLC: lnwire.NewMSatFromSatoshis(chanCapSat), + }, 3), + symmetricTestChannel("a", "d", chanCapSat, &testChannelPolicy{ + Expiry: 144, + FeeRate: 400, + FeeBaseMsat: 100000, + MinHTLC: 1, + MaxHTLC: lnwire.NewMSatFromSatoshis(chanCapSat), + }, 2), + symmetricTestChannel("d", "c", chanCapSat, &testChannelPolicy{ + Expiry: 144, + FeeRate: 400, + FeeBaseMsat: 100000, + MinHTLC: 1, + MaxHTLC: lnwire.NewMSatFromSatoshis(chanCapSat), + }, 4), + } + + testGraph, err := createTestGraphFromChannels(testChannels, "a") + defer testGraph.cleanUp() + if err != nil { + t.Fatalf("unable to create graph: %v", err) + } + + const startingBlockHeight = 101 + + ctx, cleanUp, err := createTestCtxFromGraphInstance(startingBlockHeight, + testGraph) + + defer cleanUp() + if err != nil { + t.Fatalf("unable to create router: %v", err) + } + + // Create a payment to node c. + payment := LightningPayment{ + Target: ctx.aliases["c"], + Amount: lnwire.NewMSatFromSatoshis(1000), + FeeLimit: noFeeLimit, + PaymentHash: lntypes.Hash{}, + } + + // We'll modify the SendToSwitch method so that it simulates hop b as a + // node that returns an unparsable failure if approached via the a->b + // channel. + ctx.router.cfg.Payer.(*mockPaymentAttemptDispatcher).setPaymentResult( + func(firstHop lnwire.ShortChannelID) ([32]byte, error) { + + // If channel a->b is used, return an error without + // source and message. The sender won't know the origin + // of the error. + if firstHop.ToUint64() == 1 { + return [32]byte{}, + htlcswitch.ErrUnreadableFailureMessage + } + + // Otherwise the payment succeeds. + return lntypes.Preimage{}, nil + }) + + // Send off the payment request to the router. The expectation is that + // the route a->b->c is tried first. An unreadable faiure is returned + // which should pruning the channel a->b. We expect the payment to + // succeed via a->d. + _, _, err = ctx.router.SendPayment(&payment) + if err != nil { + t.Fatalf("expected payment to succeed, but got: %v", err) + } +} + // assertChannelsPruned ensures that only the given channels are pruned from the // graph out of the set of all channels. func assertChannelsPruned(t *testing.T, graph *channeldb.ChannelGraph, From c6f9517e485915cb22d34cb14f87adabcec6d225 Mon Sep 17 00:00:00 2001 From: Joost Jager Date: Wed, 3 Jul 2019 12:51:13 +0200 Subject: [PATCH 6/7] lnwire+htlcswitch: only use references for failure messages Methods on failure message types used to be defined on value receivers. This allowed assignment of a failure message to ForwardingError both as a value and as a pointer. This is error-prone, especially when using a type switch. In this commit the failure message methods are changed so that they target pointer receivers. Two instances where a value was assigned instead of a reference are fixed. --- htlcswitch/link.go | 2 +- htlcswitch/switch.go | 2 +- lnwire/onion_error.go | 64 +++++++++++++++++++------------------- lnwire/onion_error_test.go | 4 +-- 4 files changed, 36 insertions(+), 36 deletions(-) diff --git a/htlcswitch/link.go b/htlcswitch/link.go index 15c65ec4a..b5c66e376 100644 --- a/htlcswitch/link.go +++ b/htlcswitch/link.go @@ -1214,7 +1214,7 @@ func (l *channelLink) processHodlEvent(hodlEvent invoices.HodlEvent, ) case invoices.CancelExpiryTooSoon: - failure = lnwire.FailFinalExpiryTooSoon{} + failure = &lnwire.FailFinalExpiryTooSoon{} default: return fmt.Errorf("unknown cancel reason: %v", diff --git a/htlcswitch/switch.go b/htlcswitch/switch.go index 8f6f99ece..8bcd278ee 100644 --- a/htlcswitch/switch.go +++ b/htlcswitch/switch.go @@ -981,7 +981,7 @@ func (s *Switch) parseFailedPayment(deobfuscator ErrorDecrypter, return &ForwardingError{ FailureSourceIdx: 0, ExtraMsg: userErr, - FailureMessage: lnwire.FailPermanentChannelFailure{}, + FailureMessage: &lnwire.FailPermanentChannelFailure{}, } // A regular multi-hop payment error that we'll need to diff --git a/lnwire/onion_error.go b/lnwire/onion_error.go index 7e04cc95c..6ea01d52c 100644 --- a/lnwire/onion_error.go +++ b/lnwire/onion_error.go @@ -162,14 +162,14 @@ type FailInvalidRealm struct{} // Returns a human readable string describing the target FailureMessage. // // NOTE: Implements the error interface. -func (f FailInvalidRealm) Error() string { +func (f *FailInvalidRealm) Error() string { return f.Code().String() } // Code returns the failure unique code. // // NOTE: Part of the FailureMessage interface. -func (f FailInvalidRealm) Code() FailCode { +func (f *FailInvalidRealm) Code() FailCode { return CodeInvalidRealm } @@ -181,14 +181,14 @@ type FailTemporaryNodeFailure struct{} // Code returns the failure unique code. // NOTE: Part of the FailureMessage interface. -func (f FailTemporaryNodeFailure) Code() FailCode { +func (f *FailTemporaryNodeFailure) Code() FailCode { return CodeTemporaryNodeFailure } // Returns a human readable string describing the target FailureMessage. // // NOTE: Implements the error interface. -func (f FailTemporaryNodeFailure) Error() string { +func (f *FailTemporaryNodeFailure) Error() string { return f.Code().String() } @@ -201,14 +201,14 @@ type FailPermanentNodeFailure struct{} // Code returns the failure unique code. // // NOTE: Part of the FailureMessage interface. -func (f FailPermanentNodeFailure) Code() FailCode { +func (f *FailPermanentNodeFailure) Code() FailCode { return CodePermanentNodeFailure } // Returns a human readable string describing the target FailureMessage. // // NOTE: Implements the error interface. -func (f FailPermanentNodeFailure) Error() string { +func (f *FailPermanentNodeFailure) Error() string { return f.Code().String() } @@ -222,14 +222,14 @@ type FailRequiredNodeFeatureMissing struct{} // Code returns the failure unique code. // // NOTE: Part of the FailureMessage interface. -func (f FailRequiredNodeFeatureMissing) Code() FailCode { +func (f *FailRequiredNodeFeatureMissing) Code() FailCode { return CodeRequiredNodeFeatureMissing } // Returns a human readable string describing the target FailureMessage. // // NOTE: Implements the error interface. -func (f FailRequiredNodeFeatureMissing) Error() string { +func (f *FailRequiredNodeFeatureMissing) Error() string { return f.Code().String() } @@ -242,14 +242,14 @@ type FailPermanentChannelFailure struct{} // Code returns the failure unique code. // // NOTE: Part of the FailureMessage interface. -func (f FailPermanentChannelFailure) Code() FailCode { +func (f *FailPermanentChannelFailure) Code() FailCode { return CodePermanentChannelFailure } // Returns a human readable string describing the target FailureMessage. // // NOTE: Implements the error interface. -func (f FailPermanentChannelFailure) Error() string { +func (f *FailPermanentChannelFailure) Error() string { return f.Code().String() } @@ -263,14 +263,14 @@ type FailRequiredChannelFeatureMissing struct{} // Code returns the failure unique code. // // NOTE: Part of the FailureMessage interface. -func (f FailRequiredChannelFeatureMissing) Code() FailCode { +func (f *FailRequiredChannelFeatureMissing) Code() FailCode { return CodeRequiredChannelFeatureMissing } // Returns a human readable string describing the target FailureMessage. // // NOTE: Implements the error interface. -func (f FailRequiredChannelFeatureMissing) Error() string { +func (f *FailRequiredChannelFeatureMissing) Error() string { return f.Code().String() } @@ -283,14 +283,14 @@ type FailUnknownNextPeer struct{} // Code returns the failure unique code. // // NOTE: Part of the FailureMessage interface. -func (f FailUnknownNextPeer) Code() FailCode { +func (f *FailUnknownNextPeer) Code() FailCode { return CodeUnknownNextPeer } // Returns a human readable string describing the target FailureMessage. // // NOTE: Implements the error interface. -func (f FailUnknownNextPeer) Error() string { +func (f *FailUnknownNextPeer) Error() string { return f.Code().String() } @@ -306,14 +306,14 @@ type FailIncorrectPaymentAmount struct{} // Code returns the failure unique code. // // NOTE: Part of the FailureMessage interface. -func (f FailIncorrectPaymentAmount) Code() FailCode { +func (f *FailIncorrectPaymentAmount) Code() FailCode { return CodeIncorrectPaymentAmount } // Returns a human readable string describing the target FailureMessage. // // NOTE: Implements the error interface. -func (f FailIncorrectPaymentAmount) Error() string { +func (f *FailIncorrectPaymentAmount) Error() string { return f.Code().String() } @@ -344,21 +344,21 @@ func NewFailUnknownPaymentHash(amt MilliSatoshi) *FailUnknownPaymentHash { } // Amount is the value of the extended HTLC. -func (f FailUnknownPaymentHash) Amount() MilliSatoshi { +func (f *FailUnknownPaymentHash) Amount() MilliSatoshi { return f.amount } // Code returns the failure unique code. // // NOTE: Part of the FailureMessage interface. -func (f FailUnknownPaymentHash) Code() FailCode { +func (f *FailUnknownPaymentHash) Code() FailCode { return CodeUnknownPaymentHash } // Returns a human readable string describing the target FailureMessage. // // NOTE: Implements the error interface. -func (f FailUnknownPaymentHash) Error() string { +func (f *FailUnknownPaymentHash) Error() string { return fmt.Sprintf("UnknownPaymentHash(amt=%v)", f.amount) } @@ -398,14 +398,14 @@ type FailFinalExpiryTooSoon struct{} // Code returns the failure unique code. // // NOTE: Part of the FailureMessage interface. -func (f FailFinalExpiryTooSoon) Code() FailCode { +func (f *FailFinalExpiryTooSoon) Code() FailCode { return CodeFinalExpiryTooSoon } // Returns a human readable string describing the target FailureMessage. // // NOTE: Implements the error interface. -func (f FailFinalExpiryTooSoon) Error() string { +func (f *FailFinalExpiryTooSoon) Error() string { return f.Code().String() } @@ -425,7 +425,7 @@ type FailInvalidOnionVersion struct { // Returns a human readable string describing the target FailureMessage. // // NOTE: Implements the error interface. -func (f FailInvalidOnionVersion) Error() string { +func (f *FailInvalidOnionVersion) Error() string { return fmt.Sprintf("InvalidOnionVersion(onion_sha=%x)", f.OnionSHA256[:]) } @@ -492,7 +492,7 @@ func (f *FailInvalidOnionHmac) Encode(w io.Writer, pver uint32) error { // Returns a human readable string describing the target FailureMessage. // // NOTE: Implements the error interface. -func (f FailInvalidOnionHmac) Error() string { +func (f *FailInvalidOnionHmac) Error() string { return fmt.Sprintf("InvalidOnionHMAC(onion_sha=%x)", f.OnionSHA256[:]) } @@ -534,7 +534,7 @@ func (f *FailInvalidOnionKey) Encode(w io.Writer, pver uint32) error { // Returns a human readable string describing the target FailureMessage. // // NOTE: Implements the error interface. -func (f FailInvalidOnionKey) Error() string { +func (f *FailInvalidOnionKey) Error() string { return fmt.Sprintf("InvalidOnionKey(onion_sha=%x)", f.OnionSHA256[:]) } @@ -604,7 +604,7 @@ func (f *FailTemporaryChannelFailure) Code() FailCode { // Returns a human readable string describing the target FailureMessage. // // NOTE: Implements the error interface. -func (f FailTemporaryChannelFailure) Error() string { +func (f *FailTemporaryChannelFailure) Error() string { if f.Update == nil { return f.Code().String() } @@ -688,7 +688,7 @@ func (f *FailAmountBelowMinimum) Code() FailCode { // Returns a human readable string describing the target FailureMessage. // // NOTE: Implements the error interface. -func (f FailAmountBelowMinimum) Error() string { +func (f *FailAmountBelowMinimum) Error() string { return fmt.Sprintf("AmountBelowMinimum(amt=%v, update=%v", f.HtlcMsat, spew.Sdump(f.Update)) } @@ -756,7 +756,7 @@ func (f *FailFeeInsufficient) Code() FailCode { // Returns a human readable string describing the target FailureMessage. // // NOTE: Implements the error interface. -func (f FailFeeInsufficient) Error() string { +func (f *FailFeeInsufficient) Error() string { return fmt.Sprintf("FeeInsufficient(htlc_amt==%v, update=%v", f.HtlcMsat, spew.Sdump(f.Update)) } @@ -945,7 +945,7 @@ func (f *FailChannelDisabled) Code() FailCode { // Returns a human readable string describing the target FailureMessage. // // NOTE: Implements the error interface. -func (f FailChannelDisabled) Error() string { +func (f *FailChannelDisabled) Error() string { return fmt.Sprintf("ChannelDisabled(flags=%v, update=%v", f.Flags, spew.Sdump(f.Update)) } @@ -993,7 +993,7 @@ type FailFinalIncorrectCltvExpiry struct { // Returns a human readable string describing the target FailureMessage. // // NOTE: Implements the error interface. -func (f FailFinalIncorrectCltvExpiry) Error() string { +func (f *FailFinalIncorrectCltvExpiry) Error() string { return fmt.Sprintf("FinalIncorrectCltvExpiry(expiry=%v)", f.CltvExpiry) } @@ -1038,7 +1038,7 @@ type FailFinalIncorrectHtlcAmount struct { // Returns a human readable string describing the target FailureMessage. // // NOTE: Implements the error interface. -func (f FailFinalIncorrectHtlcAmount) Error() string { +func (f *FailFinalIncorrectHtlcAmount) Error() string { return fmt.Sprintf("FinalIncorrectHtlcAmount(amt=%v)", f.IncomingHTLCAmount) } @@ -1081,14 +1081,14 @@ type FailExpiryTooFar struct{} // Code returns the failure unique code. // // NOTE: Part of the FailureMessage interface. -func (f FailExpiryTooFar) Code() FailCode { +func (f *FailExpiryTooFar) Code() FailCode { return CodeExpiryTooFar } // Returns a human readable string describing the target FailureMessage. // // NOTE: Implements the error interface. -func (f FailExpiryTooFar) Error() string { +func (f *FailExpiryTooFar) Error() string { return f.Code().String() } diff --git a/lnwire/onion_error_test.go b/lnwire/onion_error_test.go index e05bd2a18..9ecb9f1a9 100644 --- a/lnwire/onion_error_test.go +++ b/lnwire/onion_error_test.go @@ -177,7 +177,7 @@ func TestFailUnknownPaymentHashOptionalAmount(t *testing.T) { // Creation an error that is a non-pointer will allow us to skip the // type assertion for the Serializable interface. As a result, the // amount body won't be written. - onionError := FailUnknownPaymentHash{} + onionError := &FailUnknownPaymentHash{} var b bytes.Buffer if err := EncodeFailure(&b, onionError, 0); err != nil { @@ -189,7 +189,7 @@ func TestFailUnknownPaymentHashOptionalAmount(t *testing.T) { t.Fatalf("unable to decode error: %v", err) } - if !reflect.DeepEqual(onionError, onionError) { + if !reflect.DeepEqual(onionError, onionError2) { t.Fatalf("expected %v, got %v", spew.Sdump(onionError), spew.Sdump(onionError2)) } From 2726f50d7c0b1a6da68db665e3c7d06cc24105bf Mon Sep 17 00:00:00 2001 From: Joost Jager Date: Wed, 19 Jun 2019 15:09:23 +0200 Subject: [PATCH 7/7] htlcswitch: do not consider unknown failures an error --- htlcswitch/failure.go | 10 +- htlcswitch/switch_test.go | 33 +++++ lnrpc/routerrpc/router.pb.go | 206 ++++++++++++++++--------------- lnrpc/routerrpc/router.proto | 5 + lnrpc/routerrpc/router_server.go | 3 +- routing/router.go | 4 +- routing/router_test.go | 37 +++++- 7 files changed, 186 insertions(+), 112 deletions(-) diff --git a/htlcswitch/failure.go b/htlcswitch/failure.go index 29b72021c..8309f477c 100644 --- a/htlcswitch/failure.go +++ b/htlcswitch/failure.go @@ -31,10 +31,10 @@ type ForwardingError struct { // returned. func (f *ForwardingError) Error() string { if f.ExtraMsg == "" { - return f.FailureMessage.Error() + return fmt.Sprintf("%v", f.FailureMessage) } - return fmt.Sprintf("%v: %v", f.FailureMessage.Error(), f.ExtraMsg) + return fmt.Sprintf("%v: %v", f.FailureMessage, f.ExtraMsg) } // ErrorDecrypter is an interface that is used to decrypt the onion encrypted @@ -274,10 +274,14 @@ func (s *SphinxErrorDecrypter) DecryptError(reason lnwire.OpaqueReason) ( return nil, err } + // Decode the failure. If an error occurs, we leave the failure message + // field nil. r := bytes.NewReader(failure.Message) failureMsg, err := lnwire.DecodeFailure(r, 0) if err != nil { - return nil, err + return &ForwardingError{ + FailureSourceIdx: failure.SenderIdx, + }, nil } return &ForwardingError{ diff --git a/htlcswitch/switch_test.go b/htlcswitch/switch_test.go index 1e27abe9d..13f6acc37 100644 --- a/htlcswitch/switch_test.go +++ b/htlcswitch/switch_test.go @@ -2344,4 +2344,37 @@ func TestInvalidFailure(t *testing.T) { case <-time.After(time.Second): t.Fatal("err wasn't received") } + + // Modify the decryption to simulate that decryption went alright, but + // the failure cannot be decoded. + deobfuscator = SphinxErrorDecrypter{ + OnionErrorDecrypter: &mockOnionErrorDecryptor{ + sourceIdx: 2, + message: []byte{200}, + }, + } + + resultChan, err = s.GetPaymentResult( + paymentID, rhash, &deobfuscator, + ) + if err != nil { + t.Fatal(err) + } + + select { + case result := <-resultChan: + fErr, ok := result.Error.(*ForwardingError) + if !ok { + t.Fatal("expected ForwardingError") + } + if fErr.FailureSourceIdx != 2 { + t.Fatal("unexpected error source index") + } + if fErr.FailureMessage != nil { + t.Fatal("expected empty failure message") + } + + case <-time.After(time.Second): + t.Fatal("err wasn't received") + } } diff --git a/lnrpc/routerrpc/router.pb.go b/lnrpc/routerrpc/router.pb.go index d303c711f..cba7914d7 100644 --- a/lnrpc/routerrpc/router.pb.go +++ b/lnrpc/routerrpc/router.pb.go @@ -104,6 +104,9 @@ const ( Failure_PERMANENT_NODE_FAILURE Failure_FailureCode = 20 Failure_PERMANENT_CHANNEL_FAILURE Failure_FailureCode = 21 //* + //The error source is known, but the failure itself couldn't be decoded. + Failure_UNKNOWN_FAILURE Failure_FailureCode = 998 + //* //An unreadable failure result is returned if the received failure message //cannot be decrypted. In that case the error source is unknown. Failure_UNREADABLE_FAILURE Failure_FailureCode = 999 @@ -132,6 +135,7 @@ var Failure_FailureCode_name = map[int32]string{ 19: "TEMPORARY_NODE_FAILURE", 20: "PERMANENT_NODE_FAILURE", 21: "PERMANENT_CHANNEL_FAILURE", + 998: "UNKNOWN_FAILURE", 999: "UNREADABLE_FAILURE", } @@ -158,6 +162,7 @@ var Failure_FailureCode_value = map[string]int32{ "TEMPORARY_NODE_FAILURE": 19, "PERMANENT_NODE_FAILURE": 20, "PERMANENT_CHANNEL_FAILURE": 21, + "UNKNOWN_FAILURE": 998, "UNREADABLE_FAILURE": 999, } @@ -1173,110 +1178,111 @@ func init() { func init() { proto.RegisterFile("routerrpc/router.proto", fileDescriptor_7a0613f69d37b0a5) } var fileDescriptor_7a0613f69d37b0a5 = []byte{ - // 1648 bytes of a gzipped FileDescriptorProto + // 1659 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x57, 0x4f, 0x73, 0x22, 0xc7, 0x15, 0x37, 0x02, 0x84, 0x78, 0xfc, 0xd1, 0xa8, 0xa5, 0x95, 0x58, 0xb4, 0x5a, 0xcb, 0xe3, 0x64, 0xad, 0xda, 0x72, 0x24, 0x87, 0xd4, 0xba, 0x7c, 0x4a, 0x8a, 0x85, 0xc6, 0x8c, 0x17, 0x66, 0xe4, 0x06, 0xd6, 0xde, 0xe4, 0xd0, 0xd5, 0x62, 0x5a, 0x30, 0x25, 0x98, 0xc1, 0x33, 0x8d, 0xb3, 0xca, - 0x21, 0xb7, 0x1c, 0xf3, 0x29, 0xf2, 0x01, 0x92, 0x4f, 0x90, 0xaf, 0x93, 0x7b, 0x8e, 0x39, 0xa5, - 0xba, 0x7b, 0x80, 0x01, 0xa1, 0xf5, 0x9e, 0xc4, 0xfc, 0xde, 0xaf, 0xdf, 0xdf, 0x7e, 0xaf, 0x9f, - 0xe0, 0x38, 0x0c, 0xe6, 0x82, 0x87, 0xe1, 0x6c, 0x78, 0xa5, 0x7f, 0x5d, 0xce, 0xc2, 0x40, 0x04, - 0x28, 0xbf, 0xc4, 0xab, 0xf9, 0x70, 0x36, 0xd4, 0xa8, 0xf9, 0xbf, 0x1d, 0x40, 0x3d, 0xee, 0xbb, - 0xd7, 0xec, 0x7e, 0xca, 0x7d, 0x41, 0xf8, 0x4f, 0x73, 0x1e, 0x09, 0x84, 0x20, 0xe3, 0xf2, 0x48, - 0x54, 0x52, 0xe7, 0xa9, 0x8b, 0x22, 0x51, 0xbf, 0x91, 0x01, 0x69, 0x36, 0x15, 0x95, 0x9d, 0xf3, - 0xd4, 0x45, 0x9a, 0xc8, 0x9f, 0xe8, 0x33, 0x28, 0xce, 0xf4, 0x39, 0x3a, 0x66, 0xd1, 0xb8, 0x92, - 0x56, 0xec, 0x42, 0x8c, 0xb5, 0x59, 0x34, 0x46, 0x17, 0x60, 0xdc, 0x7a, 0x3e, 0x9b, 0xd0, 0xe1, - 0x44, 0xfc, 0x4c, 0x5d, 0x3e, 0x11, 0xac, 0x92, 0x39, 0x4f, 0x5d, 0x64, 0x49, 0x59, 0xe1, 0x8d, - 0x89, 0xf8, 0xb9, 0x29, 0x51, 0xf4, 0x05, 0xec, 0x2f, 0x94, 0x85, 0xda, 0x8b, 0x4a, 0xf6, 0x3c, - 0x75, 0x91, 0x27, 0xe5, 0xd9, 0xba, 0x6f, 0x5f, 0xc0, 0xbe, 0xf0, 0xa6, 0x3c, 0x98, 0x0b, 0x1a, - 0xf1, 0x61, 0xe0, 0xbb, 0x51, 0x65, 0x57, 0x6b, 0x8c, 0xe1, 0x9e, 0x46, 0x91, 0x09, 0xa5, 0x5b, - 0xce, 0xe9, 0xc4, 0x9b, 0x7a, 0x82, 0x46, 0x4c, 0x54, 0x72, 0xca, 0xf5, 0xc2, 0x2d, 0xe7, 0x1d, - 0x89, 0xf5, 0x98, 0x90, 0xfe, 0x05, 0x73, 0x31, 0x0a, 0x3c, 0x7f, 0x44, 0x87, 0x63, 0xe6, 0x53, - 0xcf, 0xad, 0xec, 0x9d, 0xa7, 0x2e, 0x32, 0xa4, 0xbc, 0xc0, 0x1b, 0x63, 0xe6, 0x5b, 0x2e, 0x3a, - 0x03, 0x50, 0x31, 0x28, 0x75, 0x95, 0xbc, 0xb2, 0x98, 0x97, 0x88, 0xd2, 0x85, 0x6a, 0x50, 0x50, - 0x09, 0xa6, 0x63, 0xcf, 0x17, 0x51, 0x05, 0xce, 0xd3, 0x17, 0x85, 0x9a, 0x71, 0x39, 0xf1, 0x65, - 0xae, 0x89, 0x94, 0xb4, 0x3d, 0x5f, 0x90, 0x24, 0xc9, 0xfc, 0x06, 0x0e, 0xfb, 0x21, 0x1b, 0xde, - 0x6d, 0x24, 0x7f, 0x33, 0xad, 0xa9, 0x07, 0x69, 0x35, 0xff, 0x0a, 0xa5, 0xf8, 0x50, 0x4f, 0x30, - 0x31, 0x8f, 0xd0, 0x6f, 0x20, 0x1b, 0x09, 0x26, 0xb8, 0x22, 0x97, 0x6b, 0x27, 0x97, 0xcb, 0x6a, - 0x5f, 0x26, 0x88, 0x9c, 0x68, 0x16, 0xaa, 0xc2, 0xde, 0x2c, 0xe4, 0xde, 0x94, 0x8d, 0xb8, 0x2a, - 0x68, 0x91, 0x2c, 0xbf, 0x91, 0x09, 0x59, 0x75, 0x58, 0x95, 0xb3, 0x50, 0x2b, 0x26, 0x63, 0x20, - 0x5a, 0x64, 0xfe, 0x1e, 0xf6, 0xd5, 0x77, 0x8b, 0xf3, 0x0f, 0x5d, 0x99, 0x13, 0xc8, 0xb1, 0xa9, - 0xce, 0xbd, 0xbe, 0x36, 0xbb, 0x6c, 0x2a, 0xd3, 0x6e, 0xba, 0x60, 0xac, 0xce, 0x47, 0xb3, 0xc0, - 0x8f, 0xb8, 0x2c, 0x85, 0x54, 0x2e, 0x2b, 0x21, 0xcb, 0x36, 0x95, 0xa7, 0x52, 0xea, 0x54, 0x39, - 0xc6, 0x5b, 0x9c, 0x77, 0x23, 0x26, 0xd0, 0x0b, 0x7d, 0x03, 0xe8, 0x24, 0x18, 0xde, 0xc9, 0x3b, - 0xc5, 0xee, 0x63, 0xf5, 0x25, 0x09, 0x77, 0x82, 0xe1, 0x5d, 0x53, 0x82, 0xe6, 0x9f, 0xf4, 0xdd, - 0xee, 0x07, 0xda, 0xf7, 0x8f, 0x4e, 0xef, 0x2a, 0x05, 0x3b, 0x8f, 0xa7, 0x80, 0xc2, 0xe1, 0x9a, - 0xf2, 0x38, 0x8a, 0x64, 0x66, 0x53, 0x1b, 0x99, 0xfd, 0x12, 0x72, 0xb7, 0xcc, 0x9b, 0xcc, 0xc3, - 0x85, 0x62, 0x94, 0x28, 0x53, 0x4b, 0x4b, 0xc8, 0x82, 0x62, 0xfe, 0x23, 0x07, 0xb9, 0x18, 0x44, - 0x35, 0xc8, 0x0c, 0x03, 0x77, 0x51, 0xdd, 0xe7, 0x0f, 0x8f, 0x2d, 0xfe, 0x36, 0x02, 0x97, 0x13, - 0xc5, 0x45, 0x7f, 0x80, 0xb2, 0xbc, 0xd1, 0x3e, 0x9f, 0xd0, 0xf9, 0xcc, 0x65, 0xcb, 0x82, 0x56, - 0x12, 0xa7, 0x1b, 0x9a, 0x30, 0x50, 0x72, 0x52, 0x1a, 0x26, 0x3f, 0xd1, 0x29, 0xe4, 0xc7, 0x62, - 0x32, 0xd4, 0x95, 0xc8, 0xa8, 0xa6, 0xd8, 0x93, 0x80, 0xaa, 0x81, 0x09, 0xa5, 0xc0, 0xf7, 0x02, - 0x9f, 0x46, 0x63, 0x46, 0x6b, 0xaf, 0xbe, 0x56, 0xcd, 0x5a, 0x24, 0x05, 0x05, 0xf6, 0xc6, 0xac, - 0xf6, 0xea, 0x6b, 0xf4, 0x29, 0x14, 0x54, 0xcb, 0xf0, 0xf7, 0x33, 0x2f, 0xbc, 0x57, 0x5d, 0x5a, - 0x22, 0xaa, 0x8b, 0xb0, 0x42, 0xd0, 0x11, 0x64, 0x6f, 0x27, 0x6c, 0x14, 0xa9, 0xce, 0x2c, 0x11, - 0xfd, 0x81, 0xbe, 0x82, 0xa3, 0x38, 0x07, 0x34, 0x0a, 0xe6, 0xe1, 0x90, 0x53, 0xcf, 0x77, 0xf9, - 0x7b, 0xd5, 0x97, 0x25, 0x82, 0x62, 0x59, 0x4f, 0x89, 0x2c, 0x29, 0x31, 0xff, 0x9b, 0x81, 0x42, - 0x22, 0x01, 0xa8, 0x08, 0x7b, 0x04, 0xf7, 0x30, 0x79, 0x8b, 0x9b, 0xc6, 0x27, 0xa8, 0x02, 0x47, - 0x03, 0xfb, 0x8d, 0xed, 0xfc, 0x60, 0xd3, 0xeb, 0xfa, 0xbb, 0x2e, 0xb6, 0xfb, 0xb4, 0x5d, 0xef, - 0xb5, 0x8d, 0x14, 0x7a, 0x06, 0x15, 0xcb, 0x6e, 0x38, 0x84, 0xe0, 0x46, 0x7f, 0x29, 0xab, 0x77, - 0x9d, 0x81, 0xdd, 0x37, 0x76, 0xd0, 0xa7, 0x70, 0xda, 0xb2, 0xec, 0x7a, 0x87, 0xae, 0x38, 0x8d, - 0x4e, 0xff, 0x2d, 0xc5, 0x3f, 0x5e, 0x5b, 0xe4, 0x9d, 0x91, 0xde, 0x46, 0x68, 0xf7, 0x3b, 0x8d, - 0x85, 0x86, 0x0c, 0x7a, 0x0a, 0x4f, 0x34, 0x41, 0x1f, 0xa1, 0x7d, 0xc7, 0xa1, 0x3d, 0xc7, 0xb1, - 0x8d, 0x2c, 0x3a, 0x80, 0x92, 0x65, 0xbf, 0xad, 0x77, 0xac, 0x26, 0x25, 0xb8, 0xde, 0xe9, 0x1a, - 0xbb, 0xe8, 0x10, 0xf6, 0x37, 0x79, 0x39, 0xa9, 0x62, 0xc1, 0x73, 0x6c, 0xcb, 0xb1, 0xe9, 0x5b, - 0x4c, 0x7a, 0x96, 0x63, 0x1b, 0x7b, 0xe8, 0x18, 0xd0, 0xba, 0xa8, 0xdd, 0xad, 0x37, 0x8c, 0x3c, - 0x7a, 0x02, 0x07, 0xeb, 0xf8, 0x1b, 0xfc, 0xce, 0x00, 0x99, 0x06, 0xed, 0x18, 0x7d, 0x8d, 0x3b, - 0xce, 0x0f, 0xb4, 0x6b, 0xd9, 0x56, 0x77, 0xd0, 0x35, 0x0a, 0xe8, 0x08, 0x8c, 0x16, 0xc6, 0xd4, - 0xb2, 0x7b, 0x83, 0x56, 0xcb, 0x6a, 0x58, 0xd8, 0xee, 0x1b, 0x45, 0x6d, 0x79, 0x5b, 0xe0, 0x25, - 0x79, 0xa0, 0xd1, 0xae, 0xdb, 0x36, 0xee, 0xd0, 0xa6, 0xd5, 0xab, 0xbf, 0xee, 0xe0, 0xa6, 0x51, - 0x46, 0x67, 0xf0, 0xb4, 0x8f, 0xbb, 0xd7, 0x0e, 0xa9, 0x93, 0x77, 0x74, 0x21, 0x6f, 0xd5, 0xad, - 0xce, 0x80, 0x60, 0x63, 0x1f, 0x7d, 0x06, 0x67, 0x04, 0x7f, 0x3f, 0xb0, 0x08, 0x6e, 0x52, 0xdb, - 0x69, 0x62, 0xda, 0xc2, 0xf5, 0xfe, 0x80, 0x60, 0xda, 0xb5, 0x7a, 0x3d, 0xcb, 0xfe, 0xd6, 0x30, - 0xd0, 0xaf, 0xe0, 0x7c, 0x49, 0x59, 0x2a, 0xd8, 0x60, 0x1d, 0xc8, 0xf8, 0x16, 0xf5, 0xb4, 0xf1, - 0x8f, 0x7d, 0x7a, 0x8d, 0x31, 0x31, 0x10, 0xaa, 0xc2, 0xf1, 0xca, 0xbc, 0x36, 0x10, 0xdb, 0x3e, - 0x94, 0xb2, 0x6b, 0x4c, 0xba, 0x75, 0x5b, 0x16, 0x78, 0x4d, 0x76, 0x24, 0xdd, 0x5e, 0xc9, 0x36, - 0xdd, 0x7e, 0x82, 0x4e, 0x00, 0x0d, 0x6c, 0x82, 0xeb, 0x4d, 0x19, 0xe6, 0x12, 0xff, 0x4f, 0xee, - 0xbb, 0xcc, 0xde, 0x8e, 0x91, 0x36, 0xff, 0x99, 0x86, 0xd2, 0x5a, 0x17, 0xa1, 0x67, 0x90, 0x8f, - 0xbc, 0x91, 0xcf, 0x84, 0xec, 0x73, 0x3d, 0x02, 0x56, 0x80, 0x7a, 0x46, 0xc6, 0xcc, 0xf3, 0xf5, - 0xec, 0xd1, 0xb3, 0x37, 0xaf, 0x10, 0x35, 0x79, 0x4e, 0x20, 0xb7, 0x78, 0x86, 0xd2, 0xaa, 0xe3, - 0x76, 0x87, 0xfa, 0xf9, 0x79, 0x06, 0x79, 0x39, 0xdc, 0x22, 0xc1, 0xa6, 0x33, 0xd5, 0x8c, 0x25, - 0xb2, 0x02, 0xd0, 0xe7, 0x50, 0x9a, 0xf2, 0x28, 0x62, 0x23, 0x4e, 0x75, 0x43, 0x81, 0x62, 0x14, - 0x63, 0xb0, 0xa5, 0xfa, 0xea, 0x73, 0x58, 0x34, 0x78, 0x4c, 0xca, 0x6a, 0x52, 0x0c, 0x6a, 0xd2, - 0xe6, 0x6c, 0x15, 0x2c, 0xee, 0xdb, 0xe4, 0x6c, 0x15, 0x0c, 0xbd, 0x84, 0x03, 0x3d, 0x1c, 0x3c, - 0xdf, 0x9b, 0xce, 0xa7, 0x7a, 0x48, 0xe4, 0x94, 0xcb, 0xfb, 0x6a, 0x48, 0x68, 0x5c, 0xcd, 0x8a, - 0xa7, 0xb0, 0x77, 0xc3, 0x22, 0x2e, 0xc7, 0x7a, 0xdc, 0xc4, 0x39, 0xf9, 0xdd, 0xe2, 0x5c, 0x8a, - 0xe4, 0xb0, 0x0f, 0xe5, 0x78, 0xca, 0x6b, 0xd1, 0x2d, 0xe7, 0x44, 0xe6, 0x71, 0x69, 0x81, 0xbd, - 0x5f, 0x59, 0x28, 0x24, 0x2c, 0x68, 0x5c, 0x59, 0x78, 0x09, 0x07, 0xfc, 0xbd, 0x08, 0x19, 0x0d, - 0x66, 0xec, 0xa7, 0x39, 0xa7, 0x2e, 0x13, 0xac, 0x52, 0x54, 0xc9, 0xdd, 0x57, 0x02, 0x47, 0xe1, - 0x4d, 0x26, 0x98, 0xf9, 0x0c, 0xaa, 0x84, 0x47, 0x5c, 0x74, 0xbd, 0x28, 0xf2, 0x02, 0xbf, 0x11, - 0xf8, 0x22, 0x0c, 0x26, 0xf1, 0xeb, 0x60, 0x9e, 0xc1, 0xe9, 0x56, 0xa9, 0x1e, 0xef, 0xf2, 0xf0, - 0xf7, 0x73, 0x1e, 0xde, 0x6f, 0x3f, 0xfc, 0x06, 0x4e, 0xb7, 0x4a, 0xe3, 0xb7, 0xe1, 0x4b, 0xc8, - 0xfa, 0x81, 0xcb, 0xa3, 0x4a, 0x4a, 0x6d, 0x07, 0xc7, 0x89, 0x41, 0x6c, 0x07, 0x2e, 0x6f, 0x7b, - 0x91, 0x08, 0xc2, 0x7b, 0xa2, 0x49, 0xe6, 0xbf, 0x53, 0x50, 0x48, 0xc0, 0xe8, 0x18, 0x76, 0x67, - 0xf3, 0x9b, 0x3b, 0x7e, 0x1f, 0x5f, 0xaa, 0xf8, 0x0b, 0xbd, 0x80, 0xf2, 0x84, 0x45, 0x82, 0xca, - 0xb9, 0x48, 0x65, 0x91, 0xe2, 0xc7, 0x70, 0x03, 0x45, 0xdf, 0xc0, 0x49, 0x20, 0xc6, 0x3c, 0xd4, - 0x7b, 0x4e, 0x34, 0x1f, 0x0e, 0x79, 0x14, 0xd1, 0x59, 0x18, 0xdc, 0xa8, 0xab, 0xb6, 0x43, 0x1e, - 0x13, 0xa3, 0x57, 0xb0, 0x17, 0xdf, 0x91, 0xa8, 0x92, 0x51, 0xae, 0x3f, 0x7d, 0xf8, 0x86, 0x2c, - 0xbc, 0x5f, 0x52, 0xcd, 0x7f, 0xa5, 0xa0, 0xbc, 0x2e, 0x44, 0xcf, 0xd5, 0xed, 0x57, 0x57, 0xd0, - 0x73, 0x55, 0x1c, 0x19, 0x92, 0x40, 0x3e, 0x3a, 0x96, 0x1a, 0x1c, 0x4d, 0x3d, 0x9f, 0xce, 0xb8, - 0xcf, 0x26, 0xde, 0x5f, 0x38, 0x5d, 0x6c, 0x19, 0x69, 0xc5, 0xde, 0x2a, 0x43, 0x26, 0x14, 0xd7, - 0x82, 0xce, 0xa8, 0xa0, 0xd7, 0xb0, 0x97, 0x7f, 0x4f, 0x41, 0x31, 0xb9, 0x2f, 0xa1, 0x12, 0xe4, - 0x2d, 0x9b, 0xb6, 0x3a, 0xd6, 0xb7, 0xed, 0xbe, 0xf1, 0x89, 0xfc, 0xec, 0x0d, 0x1a, 0x0d, 0x8c, - 0x9b, 0xb8, 0x69, 0xa4, 0x10, 0x82, 0xb2, 0x1c, 0x08, 0xb8, 0x49, 0xfb, 0x56, 0x17, 0x3b, 0x03, - 0xf9, 0x6a, 0x1c, 0xc2, 0x7e, 0x8c, 0xd9, 0x0e, 0x25, 0xce, 0xa0, 0x8f, 0x8d, 0x34, 0x32, 0xa0, - 0x18, 0x83, 0x98, 0x10, 0x87, 0x18, 0x19, 0x39, 0xea, 0x62, 0xe4, 0xe1, 0x0b, 0xd4, 0xc4, 0xfd, - 0xba, 0xd5, 0xe9, 0x19, 0xd9, 0xda, 0xdf, 0x32, 0xb0, 0xab, 0xf6, 0x8b, 0x10, 0xb5, 0xa1, 0x90, - 0x58, 0xd4, 0xd1, 0x59, 0xa2, 0x02, 0x0f, 0x17, 0xf8, 0x6a, 0x65, 0xfb, 0x02, 0x38, 0x8f, 0xbe, - 0x4a, 0xa1, 0xef, 0xa0, 0x98, 0x5c, 0x3b, 0x51, 0x72, 0x9d, 0xd8, 0xb2, 0x8f, 0x7e, 0x50, 0xd7, - 0x1b, 0x30, 0x70, 0x24, 0xbc, 0xa9, 0x5c, 0x1f, 0xe2, 0x85, 0x0e, 0x55, 0x13, 0xfc, 0x8d, 0x2d, - 0xb1, 0x7a, 0xba, 0x55, 0x16, 0xf7, 0x47, 0x47, 0x87, 0x18, 0xaf, 0x54, 0x0f, 0x42, 0x5c, 0xdf, - 0xe3, 0xaa, 0xcf, 0x1f, 0x13, 0xc7, 0xda, 0x5c, 0x38, 0xdc, 0xd2, 0xc9, 0xe8, 0xd7, 0x49, 0x0f, - 0x1e, 0x9d, 0x03, 0xd5, 0x17, 0xbf, 0x44, 0x5b, 0x59, 0xd9, 0xd2, 0xf2, 0x6b, 0x56, 0x1e, 0x1f, - 0x18, 0x6b, 0x56, 0x3e, 0x30, 0x39, 0x5e, 0xff, 0xf6, 0x8f, 0x57, 0x23, 0x4f, 0x8c, 0xe7, 0x37, - 0x97, 0xc3, 0x60, 0x7a, 0x35, 0xf1, 0x46, 0x63, 0xe1, 0x7b, 0xfe, 0xc8, 0xe7, 0xe2, 0xcf, 0x41, - 0x78, 0x77, 0x35, 0xf1, 0xdd, 0x2b, 0xb5, 0xa2, 0x5e, 0x2d, 0xd5, 0xdd, 0xec, 0xaa, 0x7f, 0xf0, - 0x7e, 0xf7, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x7c, 0x28, 0x1a, 0x3a, 0x10, 0x0e, 0x00, 0x00, + 0x21, 0xb7, 0x54, 0xe5, 0x92, 0xcf, 0x92, 0x5c, 0x73, 0xc9, 0xc7, 0x49, 0xbe, 0x43, 0x4e, 0xa9, + 0xee, 0x1e, 0x60, 0x40, 0x68, 0xb3, 0x27, 0x31, 0xbf, 0xdf, 0xaf, 0xdf, 0xeb, 0x7e, 0xaf, 0xdf, + 0xeb, 0x27, 0x38, 0x0e, 0x83, 0xb9, 0xe0, 0x61, 0x38, 0x1b, 0x5e, 0xe9, 0x5f, 0x97, 0xb3, 0x30, + 0x10, 0x01, 0xca, 0x2f, 0xf1, 0x6a, 0x3e, 0x9c, 0x0d, 0x35, 0x6a, 0xfe, 0x77, 0x07, 0x50, 0x8f, + 0xfb, 0xee, 0x35, 0xbb, 0x9f, 0x72, 0x5f, 0x10, 0xfe, 0xd3, 0x9c, 0x47, 0x02, 0x21, 0xc8, 0xb8, + 0x3c, 0x12, 0x95, 0xd4, 0x79, 0xea, 0xa2, 0x48, 0xd4, 0x6f, 0x64, 0x40, 0x9a, 0x4d, 0x45, 0x65, + 0xe7, 0x3c, 0x75, 0x91, 0x26, 0xf2, 0x27, 0xfa, 0x0c, 0x8a, 0x33, 0xbd, 0x8e, 0x8e, 0x59, 0x34, + 0xae, 0xa4, 0x95, 0xba, 0x10, 0x63, 0x6d, 0x16, 0x8d, 0xd1, 0x05, 0x18, 0xb7, 0x9e, 0xcf, 0x26, + 0x74, 0x38, 0x11, 0x3f, 0x53, 0x97, 0x4f, 0x04, 0xab, 0x64, 0xce, 0x53, 0x17, 0x59, 0x52, 0x56, + 0x78, 0x63, 0x22, 0x7e, 0x6e, 0x4a, 0x14, 0x7d, 0x01, 0xfb, 0x0b, 0x63, 0xa1, 0xde, 0x45, 0x25, + 0x7b, 0x9e, 0xba, 0xc8, 0x93, 0xf2, 0x6c, 0x7d, 0x6f, 0x5f, 0xc0, 0xbe, 0xf0, 0xa6, 0x3c, 0x98, + 0x0b, 0x1a, 0xf1, 0x61, 0xe0, 0xbb, 0x51, 0x65, 0x57, 0x5b, 0x8c, 0xe1, 0x9e, 0x46, 0x91, 0x09, + 0xa5, 0x5b, 0xce, 0xe9, 0xc4, 0x9b, 0x7a, 0x82, 0x46, 0x4c, 0x54, 0x72, 0x6a, 0xeb, 0x85, 0x5b, + 0xce, 0x3b, 0x12, 0xeb, 0x31, 0x21, 0xf7, 0x17, 0xcc, 0xc5, 0x28, 0xf0, 0xfc, 0x11, 0x1d, 0x8e, + 0x99, 0x4f, 0x3d, 0xb7, 0xb2, 0x77, 0x9e, 0xba, 0xc8, 0x90, 0xf2, 0x02, 0x6f, 0x8c, 0x99, 0x6f, + 0xb9, 0xe8, 0x0c, 0x40, 0x9d, 0x41, 0x99, 0xab, 0xe4, 0x95, 0xc7, 0xbc, 0x44, 0x94, 0x2d, 0x54, + 0x83, 0x82, 0x0a, 0x30, 0x1d, 0x7b, 0xbe, 0x88, 0x2a, 0x70, 0x9e, 0xbe, 0x28, 0xd4, 0x8c, 0xcb, + 0x89, 0x2f, 0x63, 0x4d, 0x24, 0xd3, 0xf6, 0x7c, 0x41, 0x92, 0x22, 0xf3, 0x1b, 0x38, 0xec, 0x87, + 0x6c, 0x78, 0xb7, 0x11, 0xfc, 0xcd, 0xb0, 0xa6, 0x1e, 0x84, 0xd5, 0xfc, 0x33, 0x94, 0xe2, 0x45, + 0x3d, 0xc1, 0xc4, 0x3c, 0x42, 0xbf, 0x82, 0x6c, 0x24, 0x98, 0xe0, 0x4a, 0x5c, 0xae, 0x9d, 0x5c, + 0x2e, 0xb3, 0x7d, 0x99, 0x10, 0x72, 0xa2, 0x55, 0xa8, 0x0a, 0x7b, 0xb3, 0x90, 0x7b, 0x53, 0x36, + 0xe2, 0x2a, 0xa1, 0x45, 0xb2, 0xfc, 0x46, 0x26, 0x64, 0xd5, 0x62, 0x95, 0xce, 0x42, 0xad, 0x98, + 0x3c, 0x03, 0xd1, 0x94, 0xf9, 0x5b, 0xd8, 0x57, 0xdf, 0x2d, 0xce, 0x3f, 0x74, 0x65, 0x4e, 0x20, + 0xc7, 0xa6, 0x3a, 0xf6, 0xfa, 0xda, 0xec, 0xb2, 0xa9, 0x0c, 0xbb, 0xe9, 0x82, 0xb1, 0x5a, 0x1f, + 0xcd, 0x02, 0x3f, 0xe2, 0x32, 0x15, 0xd2, 0xb8, 0xcc, 0x84, 0x4c, 0xdb, 0x54, 0xae, 0x4a, 0xa9, + 0x55, 0xe5, 0x18, 0x6f, 0x71, 0xde, 0x8d, 0x98, 0x40, 0x2f, 0xf4, 0x0d, 0xa0, 0x93, 0x60, 0x78, + 0x27, 0xef, 0x14, 0xbb, 0x8f, 0xcd, 0x97, 0x24, 0xdc, 0x09, 0x86, 0x77, 0x4d, 0x09, 0x9a, 0x7f, + 0xd0, 0x77, 0xbb, 0x1f, 0xe8, 0xbd, 0x7f, 0x74, 0x78, 0x57, 0x21, 0xd8, 0x79, 0x3c, 0x04, 0x14, + 0x0e, 0xd7, 0x8c, 0xc7, 0xa7, 0x48, 0x46, 0x36, 0xb5, 0x11, 0xd9, 0x2f, 0x21, 0x77, 0xcb, 0xbc, + 0xc9, 0x3c, 0x5c, 0x18, 0x46, 0x89, 0x34, 0xb5, 0x34, 0x43, 0x16, 0x12, 0xf3, 0x9f, 0x39, 0xc8, + 0xc5, 0x20, 0xaa, 0x41, 0x66, 0x18, 0xb8, 0x8b, 0xec, 0x3e, 0x7f, 0xb8, 0x6c, 0xf1, 0xb7, 0x11, + 0xb8, 0x9c, 0x28, 0x2d, 0xfa, 0x1d, 0x94, 0xe5, 0x8d, 0xf6, 0xf9, 0x84, 0xce, 0x67, 0x2e, 0x5b, + 0x26, 0xb4, 0x92, 0x58, 0xdd, 0xd0, 0x82, 0x81, 0xe2, 0x49, 0x69, 0x98, 0xfc, 0x44, 0xa7, 0x90, + 0x1f, 0x8b, 0xc9, 0x50, 0x67, 0x22, 0xa3, 0x8a, 0x62, 0x4f, 0x02, 0x2a, 0x07, 0x26, 0x94, 0x02, + 0xdf, 0x0b, 0x7c, 0x1a, 0x8d, 0x19, 0xad, 0xbd, 0xfa, 0x5a, 0x15, 0x6b, 0x91, 0x14, 0x14, 0xd8, + 0x1b, 0xb3, 0xda, 0xab, 0xaf, 0xd1, 0xa7, 0x50, 0x50, 0x25, 0xc3, 0xdf, 0xcf, 0xbc, 0xf0, 0x5e, + 0x55, 0x69, 0x89, 0xa8, 0x2a, 0xc2, 0x0a, 0x41, 0x47, 0x90, 0xbd, 0x9d, 0xb0, 0x51, 0xa4, 0x2a, + 0xb3, 0x44, 0xf4, 0x07, 0xfa, 0x0a, 0x8e, 0xe2, 0x18, 0xd0, 0x28, 0x98, 0x87, 0x43, 0x4e, 0x3d, + 0xdf, 0xe5, 0xef, 0x55, 0x5d, 0x96, 0x08, 0x8a, 0xb9, 0x9e, 0xa2, 0x2c, 0xc9, 0x98, 0x7f, 0xcd, + 0x42, 0x21, 0x11, 0x00, 0x54, 0x84, 0x3d, 0x82, 0x7b, 0x98, 0xbc, 0xc5, 0x4d, 0xe3, 0x13, 0x54, + 0x81, 0xa3, 0x81, 0xfd, 0xc6, 0x76, 0x7e, 0xb0, 0xe9, 0x75, 0xfd, 0x5d, 0x17, 0xdb, 0x7d, 0xda, + 0xae, 0xf7, 0xda, 0x46, 0x0a, 0x3d, 0x83, 0x8a, 0x65, 0x37, 0x1c, 0x42, 0x70, 0xa3, 0xbf, 0xe4, + 0xea, 0x5d, 0x67, 0x60, 0xf7, 0x8d, 0x1d, 0xf4, 0x29, 0x9c, 0xb6, 0x2c, 0xbb, 0xde, 0xa1, 0x2b, + 0x4d, 0xa3, 0xd3, 0x7f, 0x4b, 0xf1, 0x8f, 0xd7, 0x16, 0x79, 0x67, 0xa4, 0xb7, 0x09, 0xda, 0xfd, + 0x4e, 0x63, 0x61, 0x21, 0x83, 0x9e, 0xc2, 0x13, 0x2d, 0xd0, 0x4b, 0x68, 0xdf, 0x71, 0x68, 0xcf, + 0x71, 0x6c, 0x23, 0x8b, 0x0e, 0xa0, 0x64, 0xd9, 0x6f, 0xeb, 0x1d, 0xab, 0x49, 0x09, 0xae, 0x77, + 0xba, 0xc6, 0x2e, 0x3a, 0x84, 0xfd, 0x4d, 0x5d, 0x4e, 0x9a, 0x58, 0xe8, 0x1c, 0xdb, 0x72, 0x6c, + 0xfa, 0x16, 0x93, 0x9e, 0xe5, 0xd8, 0xc6, 0x1e, 0x3a, 0x06, 0xb4, 0x4e, 0xb5, 0xbb, 0xf5, 0x86, + 0x91, 0x47, 0x4f, 0xe0, 0x60, 0x1d, 0x7f, 0x83, 0xdf, 0x19, 0x20, 0xc3, 0xa0, 0x37, 0x46, 0x5f, + 0xe3, 0x8e, 0xf3, 0x03, 0xed, 0x5a, 0xb6, 0xd5, 0x1d, 0x74, 0x8d, 0x02, 0x3a, 0x02, 0xa3, 0x85, + 0x31, 0xb5, 0xec, 0xde, 0xa0, 0xd5, 0xb2, 0x1a, 0x16, 0xb6, 0xfb, 0x46, 0x51, 0x7b, 0xde, 0x76, + 0xf0, 0x92, 0x5c, 0xd0, 0x68, 0xd7, 0x6d, 0x1b, 0x77, 0x68, 0xd3, 0xea, 0xd5, 0x5f, 0x77, 0x70, + 0xd3, 0x28, 0xa3, 0x33, 0x78, 0xda, 0xc7, 0xdd, 0x6b, 0x87, 0xd4, 0xc9, 0x3b, 0xba, 0xe0, 0x5b, + 0x75, 0xab, 0x33, 0x20, 0xd8, 0xd8, 0x47, 0x9f, 0xc1, 0x19, 0xc1, 0xdf, 0x0f, 0x2c, 0x82, 0x9b, + 0xd4, 0x76, 0x9a, 0x98, 0xb6, 0x70, 0xbd, 0x3f, 0x20, 0x98, 0x76, 0xad, 0x5e, 0xcf, 0xb2, 0xbf, + 0x35, 0x0c, 0xf4, 0x0b, 0x38, 0x5f, 0x4a, 0x96, 0x06, 0x36, 0x54, 0x07, 0xf2, 0x7c, 0x8b, 0x7c, + 0xda, 0xf8, 0xc7, 0x3e, 0xbd, 0xc6, 0x98, 0x18, 0x08, 0x55, 0xe1, 0x78, 0xe5, 0x5e, 0x3b, 0x88, + 0x7d, 0x1f, 0x4a, 0xee, 0x1a, 0x93, 0x6e, 0xdd, 0x96, 0x09, 0x5e, 0xe3, 0x8e, 0xe4, 0xb6, 0x57, + 0xdc, 0xe6, 0xb6, 0x9f, 0xa0, 0x23, 0xd8, 0x5f, 0x78, 0x5b, 0x80, 0xff, 0xce, 0xa1, 0x13, 0x40, + 0x03, 0x9b, 0xe0, 0x7a, 0x53, 0x1e, 0x7e, 0x49, 0xfc, 0x27, 0xf7, 0x5d, 0x66, 0x6f, 0xc7, 0x48, + 0x9b, 0x7f, 0x4f, 0x43, 0x69, 0xad, 0xb6, 0xd0, 0x33, 0xc8, 0x47, 0xde, 0xc8, 0x67, 0x42, 0x56, + 0xbf, 0x6e, 0x0c, 0x2b, 0x40, 0x3d, 0x2e, 0x63, 0xe6, 0xf9, 0xba, 0x23, 0xe9, 0x8e, 0x9c, 0x57, + 0x88, 0xea, 0x47, 0x27, 0x90, 0x5b, 0x3c, 0x4e, 0x69, 0x55, 0x87, 0xbb, 0x43, 0xfd, 0x28, 0x3d, + 0x83, 0xbc, 0x6c, 0x79, 0x91, 0x60, 0xd3, 0x99, 0x2a, 0xd1, 0x12, 0x59, 0x01, 0xe8, 0x73, 0x28, + 0x4d, 0x79, 0x14, 0xb1, 0x11, 0xa7, 0xba, 0xcc, 0x40, 0x29, 0x8a, 0x31, 0xd8, 0x52, 0xd5, 0xf6, + 0x39, 0x2c, 0xca, 0x3e, 0x16, 0x65, 0xb5, 0x28, 0x06, 0xb5, 0x68, 0xb3, 0xe3, 0x0a, 0x16, 0x57, + 0x73, 0xb2, 0xe3, 0x0a, 0x86, 0x5e, 0xc2, 0x81, 0x6e, 0x19, 0x9e, 0xef, 0x4d, 0xe7, 0x53, 0xdd, + 0x3a, 0x72, 0x6a, 0xcb, 0xfb, 0xaa, 0x75, 0x68, 0x5c, 0x75, 0x90, 0xa7, 0xb0, 0x77, 0xc3, 0x22, + 0x2e, 0x9b, 0x7d, 0x5c, 0xda, 0x39, 0xf9, 0xdd, 0xe2, 0x5c, 0x52, 0xf2, 0x09, 0x08, 0x65, 0xd3, + 0xca, 0x6b, 0xea, 0x96, 0x73, 0x22, 0xe3, 0xb8, 0xf4, 0xc0, 0xde, 0xaf, 0x3c, 0x14, 0x12, 0x1e, + 0x34, 0xae, 0x3c, 0xbc, 0x84, 0x03, 0xfe, 0x5e, 0x84, 0x8c, 0x06, 0x33, 0xf6, 0xd3, 0x9c, 0x53, + 0x97, 0x09, 0x56, 0x29, 0xaa, 0xe0, 0xee, 0x2b, 0xc2, 0x51, 0x78, 0x93, 0x09, 0x66, 0x3e, 0x83, + 0x2a, 0xe1, 0x11, 0x17, 0x5d, 0x2f, 0x8a, 0xbc, 0xc0, 0x6f, 0x04, 0xbe, 0x08, 0x83, 0x49, 0xfc, + 0x66, 0x98, 0x67, 0x70, 0xba, 0x95, 0xd5, 0x4d, 0x5f, 0x2e, 0xfe, 0x7e, 0xce, 0xc3, 0xfb, 0xed, + 0x8b, 0xdf, 0xc0, 0xe9, 0x56, 0x36, 0x7e, 0x31, 0xbe, 0x84, 0xac, 0x1f, 0xb8, 0x3c, 0xaa, 0xa4, + 0xd4, 0xcc, 0x70, 0x9c, 0x68, 0xcf, 0x76, 0xe0, 0xf2, 0xb6, 0x17, 0x89, 0x20, 0xbc, 0x27, 0x5a, + 0x64, 0xfe, 0x2b, 0x05, 0x85, 0x04, 0x8c, 0x8e, 0x61, 0x77, 0x36, 0xbf, 0xb9, 0xe3, 0xf7, 0xf1, + 0xa5, 0x8a, 0xbf, 0xd0, 0x0b, 0x28, 0x4f, 0x58, 0x24, 0xa8, 0xec, 0x96, 0x54, 0x26, 0x29, 0x7e, + 0x22, 0x37, 0x50, 0xf4, 0x0d, 0x9c, 0x04, 0x62, 0xcc, 0x43, 0x3d, 0xfd, 0x44, 0xf3, 0xe1, 0x90, + 0x47, 0x11, 0x9d, 0x85, 0xc1, 0x8d, 0xba, 0x6a, 0x3b, 0xe4, 0x31, 0x1a, 0xbd, 0x82, 0xbd, 0xf8, + 0x8e, 0x44, 0x95, 0x8c, 0xda, 0xfa, 0xd3, 0x87, 0x2f, 0xcb, 0x62, 0xf7, 0x4b, 0xa9, 0xf9, 0x8f, + 0x14, 0x94, 0xd7, 0x49, 0xf4, 0x5c, 0xdd, 0x7e, 0x75, 0x05, 0x3d, 0x57, 0x9d, 0x23, 0x43, 0x12, + 0xc8, 0x47, 0x9f, 0xa5, 0x06, 0x47, 0x53, 0xcf, 0xa7, 0x33, 0xee, 0xb3, 0x89, 0xf7, 0x27, 0x4e, + 0x17, 0xb3, 0x47, 0x5a, 0xa9, 0xb7, 0x72, 0xc8, 0x84, 0xe2, 0xda, 0xa1, 0x33, 0xea, 0xd0, 0x6b, + 0xd8, 0xcb, 0xbf, 0xa5, 0xa0, 0x98, 0x9c, 0xa2, 0x50, 0x09, 0xf2, 0x96, 0x4d, 0x5b, 0x1d, 0xeb, + 0xdb, 0x76, 0xdf, 0xf8, 0x44, 0x7e, 0xf6, 0x06, 0x8d, 0x06, 0xc6, 0x4d, 0xdc, 0x34, 0x52, 0x08, + 0x41, 0x59, 0x36, 0x04, 0xdc, 0xa4, 0x7d, 0xab, 0x8b, 0x9d, 0x81, 0x7c, 0x4b, 0x0e, 0x61, 0x3f, + 0xc6, 0x6c, 0x87, 0x12, 0x67, 0xd0, 0xc7, 0x46, 0x1a, 0x19, 0x50, 0x8c, 0x41, 0x4c, 0x88, 0x43, + 0x8c, 0x8c, 0x6c, 0x80, 0x31, 0xf2, 0xf0, 0x5d, 0x6a, 0xe2, 0x7e, 0xdd, 0xea, 0xf4, 0x8c, 0x6c, + 0xed, 0x2f, 0x19, 0xd8, 0x55, 0x53, 0x47, 0x88, 0xda, 0x50, 0x48, 0x8c, 0xef, 0xe8, 0x2c, 0x91, + 0x81, 0x87, 0x63, 0x7d, 0xb5, 0xb2, 0x7d, 0x2c, 0x9c, 0x47, 0x5f, 0xa5, 0xd0, 0x77, 0x50, 0x4c, + 0x0e, 0xa3, 0x28, 0x39, 0x64, 0x6c, 0x99, 0x52, 0x3f, 0x68, 0xeb, 0x0d, 0x18, 0x38, 0x12, 0xde, + 0x54, 0x0e, 0x15, 0xf1, 0x98, 0x87, 0xaa, 0x09, 0xfd, 0xc6, 0xec, 0x58, 0x3d, 0xdd, 0xca, 0xc5, + 0xf5, 0xd1, 0xd1, 0x47, 0x8c, 0x07, 0xad, 0x07, 0x47, 0x5c, 0x9f, 0xee, 0xaa, 0xcf, 0x1f, 0xa3, + 0x63, 0x6b, 0x2e, 0x1c, 0x6e, 0xa9, 0x64, 0xf4, 0xcb, 0xe4, 0x0e, 0x1e, 0xed, 0x03, 0xd5, 0x17, + 0xff, 0x4f, 0xb6, 0xf2, 0xb2, 0xa5, 0xe4, 0xd7, 0xbc, 0x3c, 0xde, 0x30, 0xd6, 0xbc, 0x7c, 0xa0, + 0x73, 0xbc, 0xfe, 0xf5, 0xef, 0xaf, 0x46, 0x9e, 0x18, 0xcf, 0x6f, 0x2e, 0x87, 0xc1, 0xf4, 0x6a, + 0xe2, 0x8d, 0xc6, 0xc2, 0xf7, 0xfc, 0x91, 0xcf, 0xc5, 0x1f, 0x83, 0xf0, 0xee, 0x6a, 0xe2, 0xbb, + 0x57, 0x6a, 0x70, 0xbd, 0x5a, 0x9a, 0xbb, 0xd9, 0x55, 0xff, 0xf6, 0xfd, 0xe6, 0x7f, 0x01, 0x00, + 0x00, 0xff, 0xff, 0xcc, 0x5a, 0xee, 0x77, 0x26, 0x0e, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. diff --git a/lnrpc/routerrpc/router.proto b/lnrpc/routerrpc/router.proto index 670dd06e6..18f08444a 100644 --- a/lnrpc/routerrpc/router.proto +++ b/lnrpc/routerrpc/router.proto @@ -195,6 +195,11 @@ message Failure { PERMANENT_NODE_FAILURE = 20; PERMANENT_CHANNEL_FAILURE = 21; + /** + The error source is known, but the failure itself couldn't be decoded. + */ + UNKNOWN_FAILURE = 998; + /** An unreadable failure result is returned if the received failure message cannot be decrypted. In that case the error source is unknown. diff --git a/lnrpc/routerrpc/router_server.go b/lnrpc/routerrpc/router_server.go index 21ef6a660..fc5779693 100644 --- a/lnrpc/routerrpc/router_server.go +++ b/lnrpc/routerrpc/router_server.go @@ -399,9 +399,8 @@ func marshallError(sendError error) (*Failure, error) { case *lnwire.FailPermanentChannelFailure: response.Code = Failure_PERMANENT_CHANNEL_FAILURE - default: - return nil, errors.New("unknown wire error") + response.Code = Failure_UNKNOWN_FAILURE } response.FailureSourceIndex = uint32(fErr.FailureSourceIdx) diff --git a/routing/router.go b/routing/router.go index f67387a6c..87bb5c753 100644 --- a/routing/router.go +++ b/routing/router.go @@ -2076,8 +2076,10 @@ func (r *ChannelRouter) processSendError(paySession PaymentSession, }, 0) return false, 0 + // Any other failure or an empty failure will get the node pruned. default: - return true, channeldb.FailureReasonError + paySession.ReportVertexFailure(failureVertex) + return false, 0 } } diff --git a/routing/router_test.go b/routing/router_test.go index f83ef7ba8..c50dba1c4 100644 --- a/routing/router_test.go +++ b/routing/router_test.go @@ -751,7 +751,7 @@ func TestSendPaymentErrorPathPruning(t *testing.T) { // roasbeef not having enough capacity. return [32]byte{}, &htlcswitch.ForwardingError{ FailureSourceIdx: 0, - FailureMessage: &lnwire.FailTemporaryChannelFailure{}, + FailureMessage: &lnwire.FailTemporaryChannelFailure{}, } } @@ -762,7 +762,7 @@ func TestSendPaymentErrorPathPruning(t *testing.T) { if firstHop == roasbeefSatoshi { return [32]byte{}, &htlcswitch.ForwardingError{ FailureSourceIdx: 1, - FailureMessage: &lnwire.FailUnknownNextPeer{}, + FailureMessage: &lnwire.FailUnknownNextPeer{}, } } @@ -795,7 +795,7 @@ func TestSendPaymentErrorPathPruning(t *testing.T) { if firstHop == roasbeefLuoji { return [32]byte{}, &htlcswitch.ForwardingError{ FailureSourceIdx: 0, - FailureMessage: &lnwire.FailUnknownNextPeer{}, + FailureMessage: &lnwire.FailUnknownNextPeer{}, } } @@ -841,7 +841,7 @@ func TestSendPaymentErrorPathPruning(t *testing.T) { // roasbeef not having enough capacity. return [32]byte{}, &htlcswitch.ForwardingError{ FailureSourceIdx: 0, - FailureMessage: &lnwire.FailTemporaryChannelFailure{}, + FailureMessage: &lnwire.FailTemporaryChannelFailure{}, } } return preImage, nil @@ -2512,6 +2512,31 @@ func TestUnknownErrorSource(t *testing.T) { if err != nil { t.Fatalf("expected payment to succeed, but got: %v", err) } + + // Next we modify payment result to return an unknown failure. + ctx.router.cfg.Payer.(*mockPaymentAttemptDispatcher).setPaymentResult( + func(firstHop lnwire.ShortChannelID) ([32]byte, error) { + + // If channel a->b is used, simulate that the failure + // couldn't be decoded (FailureMessage is nil). + if firstHop.ToUint64() == 2 { + return [32]byte{}, + &htlcswitch.ForwardingError{ + FailureSourceIdx: 1, + } + } + + // Otherwise the payment succeeds. + return lntypes.Preimage{}, nil + }) + + // Send off the payment request to the router. We expect the payment to + // fail because both routes have been pruned. + payment.PaymentHash = lntypes.Hash{1} + _, _, err = ctx.router.SendPayment(&payment) + if err == nil { + t.Fatalf("expected payment to fail") + } } // assertChannelsPruned ensures that only the given channels are pruned from the @@ -3055,7 +3080,7 @@ func TestRouterPaymentStateMachine(t *testing.T) { select { case sendResult <- &htlcswitch.ForwardingError{ FailureSourceIdx: 1, - FailureMessage: &lnwire.FailTemporaryChannelFailure{}, + FailureMessage: &lnwire.FailTemporaryChannelFailure{}, }: case <-time.After(1 * time.Second): t.Fatalf("unable to send result") @@ -3081,7 +3106,7 @@ func TestRouterPaymentStateMachine(t *testing.T) { case getPaymentResult <- &htlcswitch.PaymentResult{ Error: &htlcswitch.ForwardingError{ FailureSourceIdx: 1, - FailureMessage: &lnwire.FailTemporaryChannelFailure{}, + FailureMessage: &lnwire.FailTemporaryChannelFailure{}, }, }: case <-time.After(1 * time.Second):