Merge pull request #3188 from joostjager/error-source-idx

htlcswitch+routing+routerrpc: error source as index and expose decrypt failure
This commit is contained in:
Conner Fromknecht 2019-07-11 13:27:14 -07:00 committed by GitHub
commit a4f4ff0f83
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 716 additions and 438 deletions

2
go.mod

@ -30,7 +30,7 @@ require (
github.com/juju/version v0.0.0-20180108022336-b64dbd566305 // indirect github.com/juju/version v0.0.0-20180108022336-b64dbd566305 // indirect
github.com/kkdai/bstream v0.0.0-20181106074824-b3251f7901ec github.com/kkdai/bstream v0.0.0-20181106074824-b3251f7901ec
github.com/lightninglabs/neutrino v0.0.0-20190629001446-52dd89dd1aaa 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/queue v1.0.1
github.com/lightningnetwork/lnd/ticker v1.0.0 github.com/lightningnetwork/lnd/ticker v1.0.0
github.com/ltcsuite/ltcd v0.0.0-20190101042124-f37f8bf35796 github.com/ltcsuite/ltcd v0.0.0-20190101042124-f37f8bf35796

4
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-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 h1:vLsdHrqHKNDShoSRkrFEM0FsNt5waIKS87IgP1YLz+g=
github.com/lightninglabs/neutrino v0.0.0-20190629001446-52dd89dd1aaa/go.mod h1:vzLU75ll8qbRJIzW5dvK/UXtR9c2FecJ6VNOM8chyVM= 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-20190703000913-ecc936dc56c9 h1:u6dbtgPtilk/HWg9GwA8GniHqzCW/7an3ZSpZARfHx4=
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/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 h1:sjOGyegMIhvgfq5oaue6Td+hxZuf3tDC8lAPrFldqFw=
github.com/ltcsuite/ltcd v0.0.0-20190101042124-f37f8bf35796/go.mod h1:3p7ZTf9V1sNPI5H8P3NkTFF4LuwMdPl2DodF60qAKqY= 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= github.com/ltcsuite/ltcutil v0.0.0-20181217130922-17f3b04680b6/go.mod h1:8Vg/LTOO0KYa/vlHWJ6XZAevPQThGH5sufO0Hrou/lA=

@ -6,17 +6,18 @@ import (
"io" "io"
"github.com/btcsuite/btcd/btcec" "github.com/btcsuite/btcd/btcec"
"github.com/lightningnetwork/lightning-onion" sphinx "github.com/lightningnetwork/lightning-onion"
"github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/lnwire"
) )
// ForwardingError wraps an lnwire.FailureMessage in a struct that also // ForwardingError wraps an lnwire.FailureMessage in a struct that also
// includes the source of the error. // includes the source of the error.
type ForwardingError struct { type ForwardingError struct {
// ErrorSource is the public key of the node that sent the error. With // FailureSourceIdx is the index of the node that sent the failure. With
// this information, the dispatcher of a payment can modify their set // this information, the dispatcher of a payment can modify their set of
// of candidate routes in response to the type of error extracted. // candidate routes in response to the type of failure extracted. Index
ErrorSource *btcec.PublicKey // zero is the self node.
FailureSourceIdx int
// ExtraMsg is an additional error message that callers can provide in // ExtraMsg is an additional error message that callers can provide in
// order to provide context specific error details. // order to provide context specific error details.
@ -30,10 +31,10 @@ type ForwardingError struct {
// returned. // returned.
func (f *ForwardingError) Error() string { func (f *ForwardingError) Error() string {
if f.ExtraMsg == "" { 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 // ErrorDecrypter is an interface that is used to decrypt the onion encrypted
@ -243,10 +244,21 @@ func (s *SphinxErrorEncrypter) Reextract(
// ErrorEncrypter interface. // ErrorEncrypter interface.
var _ ErrorEncrypter = (*SphinxErrorEncrypter)(nil) 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 // SphinxErrorDecrypter wraps the sphinx data SphinxErrorDecrypter and maps the
// returned errors to concrete lnwire.FailureMessage instances. // returned errors to concrete lnwire.FailureMessage instances.
type SphinxErrorDecrypter struct { type SphinxErrorDecrypter struct {
*sphinx.OnionErrorDecrypter OnionErrorDecrypter
} }
// DecryptError peels off each layer of onion encryption from the first hop, to // DecryptError peels off each layer of onion encryption from the first hop, to
@ -254,22 +266,27 @@ type SphinxErrorDecrypter struct {
// along with the source of the error. // along with the source of the error.
// //
// NOTE: Part of the ErrorDecrypter interface. // NOTE: Part of the ErrorDecrypter interface.
func (s *SphinxErrorDecrypter) DecryptError(reason lnwire.OpaqueReason) (*ForwardingError, error) { 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 { if err != nil {
return nil, err return nil, err
} }
r := bytes.NewReader(failureData) // 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) failureMsg, err := lnwire.DecodeFailure(r, 0)
if err != nil { if err != nil {
return nil, err return &ForwardingError{
FailureSourceIdx: failure.SenderIdx,
}, nil
} }
return &ForwardingError{ return &ForwardingError{
ErrorSource: source, FailureSourceIdx: failure.SenderIdx,
FailureMessage: failureMsg, FailureMessage: failureMsg,
}, nil }, nil
} }

@ -1214,7 +1214,7 @@ func (l *channelLink) processHodlEvent(hodlEvent invoices.HodlEvent,
) )
case invoices.CancelExpiryTooSoon: case invoices.CancelExpiryTooSoon:
failure = lnwire.FailFinalExpiryTooSoon{} failure = &lnwire.FailFinalExpiryTooSoon{}
default: default:
return fmt.Errorf("unknown cancel reason: %v", return fmt.Errorf("unknown cancel reason: %v",

@ -163,10 +163,7 @@ func initSwitchWithDB(startingHeight uint32, db *channeldb.DB) (*Switch, error)
} }
} }
priv, _ := btcec.NewPrivateKey(btcec.S256())
pubkey := priv.PubKey()
cfg := Config{ cfg := Config{
SelfKey: pubkey,
DB: db, DB: db,
SwitchPackager: channeldb.NewSwitchPackager(), SwitchPackager: channeldb.NewSwitchPackager(),
FwdingLog: &mockForwardingLog{ FwdingLog: &mockForwardingLog{
@ -393,12 +390,9 @@ func (o *mockDeobfuscator) DecryptError(reason lnwire.OpaqueReason) (*Forwarding
return nil, err return nil, err
} }
priv, _ := btcec.NewPrivateKey(btcec.S256())
pubkey := priv.PubKey()
return &ForwardingError{ return &ForwardingError{
ErrorSource: pubkey, FailureSourceIdx: 1,
FailureMessage: failure, FailureMessage: failure,
}, nil }, nil
} }
@ -971,3 +965,18 @@ func (m *mockCircuitMap) NumPending() int {
func (m *mockCircuitMap) NumOpen() int { func (m *mockCircuitMap) NumOpen() int {
return 0 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
}

@ -9,7 +9,6 @@ import (
"sync/atomic" "sync/atomic"
"time" "time"
"github.com/btcsuite/btcd/btcec"
"github.com/btcsuite/btcd/wire" "github.com/btcsuite/btcd/wire"
"github.com/btcsuite/btcutil" "github.com/btcsuite/btcutil"
"github.com/coreos/bbolt" "github.com/coreos/bbolt"
@ -59,6 +58,10 @@ var (
// active links in the switch for a specific destination. // active links in the switch for a specific destination.
ErrNoLinksFound = errors.New("no channel links found") 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 // zeroPreimage is the empty preimage which is returned when we have
// some errors. // some errors.
zeroPreimage [sha256.Size]byte zeroPreimage [sha256.Size]byte
@ -112,11 +115,6 @@ type ChanClose struct {
// Config defines the configuration for the service. ALL elements within the // Config defines the configuration for the service. ALL elements within the
// configuration MUST be non-nil for the service to carry out its duties. // configuration MUST be non-nil for the service to carry out its duties.
type Config struct { 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 // FwdingLog is an interface that will be used by the switch to log
// forwarding events. A forwarding event happens each time a payment // forwarding events. A forwarding event happens each time a payment
// circuit is successfully completed. So when we forward an HTLC, and a // circuit is successfully completed. So when we forward an HTLC, and a
@ -776,8 +774,8 @@ func (s *Switch) handleLocalDispatch(pkt *htlcPacket) error {
if err != nil { if err != nil {
log.Errorf("Link %v not found", pkt.outgoingChanID) log.Errorf("Link %v not found", pkt.outgoingChanID)
return &ForwardingError{ return &ForwardingError{
ErrorSource: s.cfg.SelfKey, FailureSourceIdx: 0,
FailureMessage: &lnwire.FailUnknownNextPeer{}, FailureMessage: &lnwire.FailUnknownNextPeer{},
} }
} }
@ -790,9 +788,9 @@ func (s *Switch) handleLocalDispatch(pkt *htlcPacket) error {
// will be returned back to the router. // will be returned back to the router.
htlcErr := lnwire.NewTemporaryChannelFailure(nil) htlcErr := lnwire.NewTemporaryChannelFailure(nil)
return &ForwardingError{ return &ForwardingError{
ErrorSource: s.cfg.SelfKey, FailureSourceIdx: 0,
ExtraMsg: err.Error(), ExtraMsg: err.Error(),
FailureMessage: htlcErr, FailureMessage: htlcErr,
} }
} }
@ -808,8 +806,8 @@ func (s *Switch) handleLocalDispatch(pkt *htlcPacket) error {
"satisfied", pkt.outgoingChanID) "satisfied", pkt.outgoingChanID)
return &ForwardingError{ return &ForwardingError{
ErrorSource: s.cfg.SelfKey, FailureSourceIdx: 0,
FailureMessage: htlcErr, FailureMessage: htlcErr,
} }
} }
@ -823,9 +821,9 @@ func (s *Switch) handleLocalDispatch(pkt *htlcPacket) error {
// will be returned back to the router. // will be returned back to the router.
htlcErr := lnwire.NewTemporaryChannelFailure(nil) htlcErr := lnwire.NewTemporaryChannelFailure(nil)
return &ForwardingError{ return &ForwardingError{
ErrorSource: s.cfg.SelfKey, FailureSourceIdx: 0,
ExtraMsg: err.Error(), ExtraMsg: err.Error(),
FailureMessage: htlcErr, FailureMessage: htlcErr,
} }
} }
@ -942,9 +940,7 @@ func (s *Switch) extractResult(deobfuscator ErrorDecrypter, n *networkResult,
// the payment deobfuscator. // the payment deobfuscator.
func (s *Switch) parseFailedPayment(deobfuscator ErrorDecrypter, func (s *Switch) parseFailedPayment(deobfuscator ErrorDecrypter,
paymentID uint64, paymentHash lntypes.Hash, unencrypted, paymentID uint64, paymentHash lntypes.Hash, unencrypted,
isResolution bool, htlc *lnwire.UpdateFailHTLC) *ForwardingError { isResolution bool, htlc *lnwire.UpdateFailHTLC) error {
var failure *ForwardingError
switch { switch {
@ -966,10 +962,11 @@ func (s *Switch) parseFailedPayment(deobfuscator ErrorDecrypter,
// router. // router.
failureMsg = lnwire.NewTemporaryChannelFailure(nil) failureMsg = lnwire.NewTemporaryChannelFailure(nil)
} }
failure = &ForwardingError{
ErrorSource: s.cfg.SelfKey, return &ForwardingError{
ExtraMsg: userErr, FailureSourceIdx: 0,
FailureMessage: failureMsg, ExtraMsg: userErr,
FailureMessage: failureMsg,
} }
// A payment had to be timed out on chain before it got past // A payment had to be timed out on chain before it got past
@ -980,33 +977,29 @@ func (s *Switch) parseFailedPayment(deobfuscator ErrorDecrypter,
userErr := fmt.Sprintf("payment was resolved "+ userErr := fmt.Sprintf("payment was resolved "+
"on-chain, then cancelled back (hash=%v, pid=%d)", "on-chain, then cancelled back (hash=%v, pid=%d)",
paymentHash, paymentID) paymentHash, paymentID)
failure = &ForwardingError{
ErrorSource: s.cfg.SelfKey, return &ForwardingError{
ExtraMsg: userErr, FailureSourceIdx: 0,
FailureMessage: lnwire.FailPermanentChannelFailure{}, ExtraMsg: userErr,
FailureMessage: &lnwire.FailPermanentChannelFailure{},
} }
// A regular multi-hop payment error that we'll need to // A regular multi-hop payment error that we'll need to
// decrypt. // decrypt.
default: default:
var err error
// We'll attempt to fully decrypt the onion encrypted // We'll attempt to fully decrypt the onion encrypted
// error. If we're unable to then we'll bail early. // 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 { if err != nil {
userErr := fmt.Sprintf("unable to de-obfuscate "+ log.Errorf("unable to de-obfuscate onion failure "+
"onion failure (hash=%v, pid=%d): %v", "(hash=%v, pid=%d): %v",
paymentHash, paymentID, err) paymentHash, paymentID, err)
log.Error(userErr)
failure = &ForwardingError{
ErrorSource: s.cfg.SelfKey,
ExtraMsg: userErr,
FailureMessage: lnwire.NewTemporaryChannelFailure(nil),
}
}
}
return failure return ErrUnreadableFailureMessage
}
return failure
}
} }
// handlePacketForward is used in cases when we need forward the htlc update // handlePacketForward is used in cases when we need forward the htlc update

@ -6,7 +6,6 @@ import (
"fmt" "fmt"
"io" "io"
"io/ioutil" "io/ioutil"
"strings"
"testing" "testing"
"time" "time"
@ -1825,9 +1824,13 @@ func TestSwitchSendPayment(t *testing.T) {
select { select {
case err := <-errChan: case err := <-errChan:
if !strings.Contains(err.Error(), lnwire.CodeUnknownPaymentHash.String()) { fErr, ok := err.(*ForwardingError)
t.Fatalf("expected %v got %v", err, if !ok {
lnwire.CodeUnknownPaymentHash) t.Fatal("expected ForwardingError")
}
if _, ok := fErr.FailureMessage.(*lnwire.FailUnknownPaymentHash); !ok {
t.Fatalf("expected UnknownPaymentHash got %v", fErr)
} }
case <-time.After(time.Second): case <-time.After(time.Second):
t.Fatal("err wasn't received") t.Fatal("err wasn't received")
@ -2238,3 +2241,140 @@ func TestSwitchGetPaymentResult(t *testing.T) {
t.Fatalf("result not received") 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")
}
// 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")
}
}

@ -103,31 +103,40 @@ const (
Failure_TEMPORARY_NODE_FAILURE Failure_FailureCode = 19 Failure_TEMPORARY_NODE_FAILURE Failure_FailureCode = 19
Failure_PERMANENT_NODE_FAILURE Failure_FailureCode = 20 Failure_PERMANENT_NODE_FAILURE Failure_FailureCode = 20
Failure_PERMANENT_CHANNEL_FAILURE Failure_FailureCode = 21 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
) )
var Failure_FailureCode_name = map[int32]string{ var Failure_FailureCode_name = map[int32]string{
0: "RESERVED", 0: "RESERVED",
1: "UNKNOWN_PAYMENT_HASH", 1: "UNKNOWN_PAYMENT_HASH",
2: "INCORRECT_PAYMENT_AMOUNT", 2: "INCORRECT_PAYMENT_AMOUNT",
3: "FINAL_INCORRECT_CLTV_EXPIRY", 3: "FINAL_INCORRECT_CLTV_EXPIRY",
4: "FINAL_INCORRECT_HTLC_AMOUNT", 4: "FINAL_INCORRECT_HTLC_AMOUNT",
5: "FINAL_EXPIRY_TOO_SOON", 5: "FINAL_EXPIRY_TOO_SOON",
6: "INVALID_REALM", 6: "INVALID_REALM",
7: "EXPIRY_TOO_SOON", 7: "EXPIRY_TOO_SOON",
8: "INVALID_ONION_VERSION", 8: "INVALID_ONION_VERSION",
9: "INVALID_ONION_HMAC", 9: "INVALID_ONION_HMAC",
10: "INVALID_ONION_KEY", 10: "INVALID_ONION_KEY",
11: "AMOUNT_BELOW_MINIMUM", 11: "AMOUNT_BELOW_MINIMUM",
12: "FEE_INSUFFICIENT", 12: "FEE_INSUFFICIENT",
13: "INCORRECT_CLTV_EXPIRY", 13: "INCORRECT_CLTV_EXPIRY",
14: "CHANNEL_DISABLED", 14: "CHANNEL_DISABLED",
15: "TEMPORARY_CHANNEL_FAILURE", 15: "TEMPORARY_CHANNEL_FAILURE",
16: "REQUIRED_NODE_FEATURE_MISSING", 16: "REQUIRED_NODE_FEATURE_MISSING",
17: "REQUIRED_CHANNEL_FEATURE_MISSING", 17: "REQUIRED_CHANNEL_FEATURE_MISSING",
18: "UNKNOWN_NEXT_PEER", 18: "UNKNOWN_NEXT_PEER",
19: "TEMPORARY_NODE_FAILURE", 19: "TEMPORARY_NODE_FAILURE",
20: "PERMANENT_NODE_FAILURE", 20: "PERMANENT_NODE_FAILURE",
21: "PERMANENT_CHANNEL_FAILURE", 21: "PERMANENT_CHANNEL_FAILURE",
998: "UNKNOWN_FAILURE",
999: "UNREADABLE_FAILURE",
} }
var Failure_FailureCode_value = map[string]int32{ var Failure_FailureCode_value = map[string]int32{
@ -153,6 +162,8 @@ var Failure_FailureCode_value = map[string]int32{
"TEMPORARY_NODE_FAILURE": 19, "TEMPORARY_NODE_FAILURE": 19,
"PERMANENT_NODE_FAILURE": 20, "PERMANENT_NODE_FAILURE": 20,
"PERMANENT_CHANNEL_FAILURE": 21, "PERMANENT_CHANNEL_FAILURE": 21,
"UNKNOWN_FAILURE": 998,
"UNREADABLE_FAILURE": 999,
} }
func (x Failure_FailureCode) String() string { func (x Failure_FailureCode) String() string {
@ -610,10 +621,6 @@ func (m *SendToRouteResponse) GetFailure() *Failure {
type Failure struct { type Failure struct {
/// Failure code as defined in the Lightning spec /// 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"` 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. /// An optional channel update message.
ChannelUpdate *ChannelUpdate `protobuf:"bytes,3,opt,name=channel_update,json=channelUpdate,proto3" json:"channel_update,omitempty"` ChannelUpdate *ChannelUpdate `protobuf:"bytes,3,opt,name=channel_update,json=channelUpdate,proto3" json:"channel_update,omitempty"`
/// A failure type-dependent htlc value. /// A failure type-dependent htlc value.
@ -623,7 +630,11 @@ type Failure struct {
/// A failure type-dependent cltv expiry value. /// A failure type-dependent cltv expiry value.
CltvExpiry uint32 `protobuf:"varint,6,opt,name=cltv_expiry,json=cltvExpiry,proto3" json:"cltv_expiry,omitempty"` CltvExpiry uint32 `protobuf:"varint,6,opt,name=cltv_expiry,json=cltvExpiry,proto3" json:"cltv_expiry,omitempty"`
/// A failure type-dependent flags value. /// 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_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"` XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"` XXX_sizecache int32 `json:"-"`
@ -661,13 +672,6 @@ func (m *Failure) GetCode() Failure_FailureCode {
return Failure_RESERVED return Failure_RESERVED
} }
func (m *Failure) GetFailureSourcePubkey() []byte {
if m != nil {
return m.FailureSourcePubkey
}
return nil
}
func (m *Failure) GetChannelUpdate() *ChannelUpdate { func (m *Failure) GetChannelUpdate() *ChannelUpdate {
if m != nil { if m != nil {
return m.ChannelUpdate return m.ChannelUpdate
@ -703,6 +707,13 @@ func (m *Failure) GetFlags() uint32 {
return 0 return 0
} }
func (m *Failure) GetFailureSourceIndex() uint32 {
if m != nil {
return m.FailureSourceIndex
}
return 0
}
type ChannelUpdate struct { type ChannelUpdate struct {
//* //*
//The signature that validates the announced data and proves the ownership //The signature that validates the announced data and proves the ownership
@ -1167,109 +1178,111 @@ func init() {
func init() { proto.RegisterFile("routerrpc/router.proto", fileDescriptor_7a0613f69d37b0a5) } func init() { proto.RegisterFile("routerrpc/router.proto", fileDescriptor_7a0613f69d37b0a5) }
var fileDescriptor_7a0613f69d37b0a5 = []byte{ var fileDescriptor_7a0613f69d37b0a5 = []byte{
// 1623 bytes of a gzipped FileDescriptorProto // 1659 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x57, 0xdd, 0x72, 0x1a, 0xc9, 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x57, 0x4f, 0x73, 0x22, 0xc7,
0x15, 0x5e, 0x04, 0x08, 0x71, 0xf8, 0x1b, 0xb5, 0x64, 0x79, 0x8c, 0x2c, 0xaf, 0x76, 0x92, 0x78, 0x15, 0x37, 0x02, 0x84, 0x78, 0xfc, 0xd1, 0xa8, 0xa5, 0x95, 0x58, 0xb4, 0x5a, 0xcb, 0xe3, 0x64,
0x55, 0xae, 0x8d, 0x94, 0x90, 0xf2, 0xd6, 0x5e, 0x25, 0x85, 0xa1, 0x59, 0x26, 0x86, 0x19, 0x6d, 0xad, 0xda, 0x72, 0x24, 0x87, 0xd4, 0xba, 0x7c, 0x4a, 0x8a, 0x85, 0xc6, 0x8c, 0x17, 0x66, 0xe4,
0x03, 0xde, 0x75, 0x72, 0xd1, 0xd5, 0x82, 0x16, 0x4c, 0x09, 0x66, 0xd8, 0x99, 0x66, 0x63, 0xe5, 0x06, 0xd6, 0xde, 0xe4, 0xd0, 0xd5, 0x62, 0x5a, 0x30, 0x25, 0x98, 0xc1, 0x33, 0x8d, 0xb3, 0xca,
0x22, 0x77, 0xc9, 0x5d, 0x9e, 0x25, 0x79, 0x82, 0xbc, 0x47, 0x5e, 0x25, 0x57, 0xa9, 0xee, 0x1e, 0x21, 0xb7, 0x54, 0xe5, 0x92, 0xcf, 0x92, 0x5c, 0x73, 0xc9, 0xc7, 0x49, 0xbe, 0x43, 0x4e, 0xa9,
0x60, 0x40, 0xc8, 0xeb, 0x2b, 0x31, 0xdf, 0xf9, 0xfa, 0x9c, 0xd3, 0xe7, 0xaf, 0x8f, 0xe0, 0x24, 0xee, 0x1e, 0x60, 0x40, 0x68, 0xb3, 0x27, 0x31, 0xbf, 0xdf, 0xaf, 0xdf, 0xeb, 0x7e, 0xaf, 0xdf,
0x0c, 0x16, 0x82, 0x87, 0xe1, 0x7c, 0x78, 0xa5, 0x7f, 0x5d, 0xce, 0xc3, 0x40, 0x04, 0x28, 0xbf, 0xeb, 0x27, 0x38, 0x0e, 0x83, 0xb9, 0xe0, 0x61, 0x38, 0x1b, 0x5e, 0xe9, 0x5f, 0x97, 0xb3, 0x30,
0xc2, 0xab, 0xf9, 0x70, 0x3e, 0xd4, 0xa8, 0xf5, 0xbf, 0x3d, 0x40, 0x3d, 0xee, 0x8f, 0xae, 0xd9, 0x10, 0x01, 0xca, 0x2f, 0xf1, 0x6a, 0x3e, 0x9c, 0x0d, 0x35, 0x6a, 0xfe, 0x77, 0x07, 0x50, 0x8f,
0xfd, 0x8c, 0xfb, 0x82, 0xf0, 0x1f, 0x17, 0x3c, 0x12, 0x08, 0x41, 0x66, 0xc4, 0x23, 0x61, 0xa6, 0xfb, 0xee, 0x35, 0xbb, 0x9f, 0x72, 0x5f, 0x10, 0xfe, 0xd3, 0x9c, 0x47, 0x02, 0x21, 0xc8, 0xb8,
0xce, 0x53, 0x17, 0x45, 0xa2, 0x7e, 0x23, 0x03, 0xd2, 0x6c, 0x26, 0xcc, 0xbd, 0xf3, 0xd4, 0x45, 0x3c, 0x12, 0x95, 0xd4, 0x79, 0xea, 0xa2, 0x48, 0xd4, 0x6f, 0x64, 0x40, 0x9a, 0x4d, 0x45, 0x65,
0x9a, 0xc8, 0x9f, 0xe8, 0x0b, 0x28, 0xce, 0xf5, 0x39, 0x3a, 0x61, 0xd1, 0xc4, 0x4c, 0x2b, 0x76, 0xe7, 0x3c, 0x75, 0x91, 0x26, 0xf2, 0x27, 0xfa, 0x0c, 0x8a, 0x33, 0xbd, 0x8e, 0x8e, 0x59, 0x34,
0x21, 0xc6, 0xda, 0x2c, 0x9a, 0xa0, 0x0b, 0x30, 0x6e, 0x3d, 0x9f, 0x4d, 0xe9, 0x70, 0x2a, 0x7e, 0xae, 0xa4, 0x95, 0xba, 0x10, 0x63, 0x6d, 0x16, 0x8d, 0xd1, 0x05, 0x18, 0xb7, 0x9e, 0xcf, 0x26,
0xa2, 0x23, 0x3e, 0x15, 0xcc, 0xcc, 0x9c, 0xa7, 0x2e, 0xb2, 0xa4, 0xac, 0xf0, 0xc6, 0x54, 0xfc, 0x74, 0x38, 0x11, 0x3f, 0x53, 0x97, 0x4f, 0x04, 0xab, 0x64, 0xce, 0x53, 0x17, 0x59, 0x52, 0x56,
0xd4, 0x94, 0x28, 0xfa, 0x12, 0x2a, 0x4b, 0x65, 0xa1, 0xf6, 0xc2, 0xcc, 0x9e, 0xa7, 0x2e, 0xf2, 0x78, 0x63, 0x22, 0x7e, 0x6e, 0x4a, 0x14, 0x7d, 0x01, 0xfb, 0x0b, 0x63, 0xa1, 0xde, 0x45, 0x25,
0xa4, 0x3c, 0xdf, 0xf4, 0xed, 0x4b, 0xa8, 0x08, 0x6f, 0xc6, 0x83, 0x85, 0xa0, 0x11, 0x1f, 0x06, 0x7b, 0x9e, 0xba, 0xc8, 0x93, 0xf2, 0x6c, 0x7d, 0x6f, 0x5f, 0xc0, 0xbe, 0xf0, 0xa6, 0x3c, 0x98,
0xfe, 0x28, 0x32, 0xf7, 0xb5, 0xc6, 0x18, 0xee, 0x69, 0x14, 0x59, 0x50, 0xba, 0xe5, 0x9c, 0x4e, 0x0b, 0x1a, 0xf1, 0x61, 0xe0, 0xbb, 0x51, 0x65, 0x57, 0x5b, 0x8c, 0xe1, 0x9e, 0x46, 0x91, 0x09,
0xbd, 0x99, 0x27, 0x68, 0xc4, 0x84, 0x99, 0x53, 0xae, 0x17, 0x6e, 0x39, 0xef, 0x48, 0xac, 0xc7, 0xa5, 0x5b, 0xce, 0xe9, 0xc4, 0x9b, 0x7a, 0x82, 0x46, 0x4c, 0x54, 0x72, 0x6a, 0xeb, 0x85, 0x5b,
0x84, 0xf4, 0x2f, 0x58, 0x88, 0x71, 0xe0, 0xf9, 0x63, 0x3a, 0x9c, 0x30, 0x9f, 0x7a, 0x23, 0xf3, 0xce, 0x3b, 0x12, 0xeb, 0x31, 0x21, 0xf7, 0x17, 0xcc, 0xc5, 0x28, 0xf0, 0xfc, 0x11, 0x1d, 0x8e,
0xe0, 0x3c, 0x75, 0x91, 0x21, 0xe5, 0x25, 0xde, 0x98, 0x30, 0xdf, 0x1e, 0xa1, 0x33, 0x00, 0x75, 0x99, 0x4f, 0x3d, 0xb7, 0xb2, 0x77, 0x9e, 0xba, 0xc8, 0x90, 0xf2, 0x02, 0x6f, 0x8c, 0x99, 0x6f,
0x07, 0xa5, 0xce, 0xcc, 0x2b, 0x8b, 0x79, 0x89, 0x28, 0x5d, 0xa8, 0x06, 0x05, 0x15, 0x60, 0x3a, 0xb9, 0xe8, 0x0c, 0x40, 0x9d, 0x41, 0x99, 0xab, 0xe4, 0x95, 0xc7, 0xbc, 0x44, 0x94, 0x2d, 0x54,
0xf1, 0x7c, 0x11, 0x99, 0x70, 0x9e, 0xbe, 0x28, 0xd4, 0x8c, 0xcb, 0xa9, 0x2f, 0x63, 0x4d, 0xa4, 0x83, 0x82, 0x0a, 0x30, 0x1d, 0x7b, 0xbe, 0x88, 0x2a, 0x70, 0x9e, 0xbe, 0x28, 0xd4, 0x8c, 0xcb,
0xa4, 0xed, 0xf9, 0x82, 0x24, 0x49, 0xd6, 0x37, 0x70, 0xd4, 0x0f, 0xd9, 0xf0, 0x6e, 0x2b, 0xf8, 0x89, 0x2f, 0x63, 0x4d, 0x24, 0xd3, 0xf6, 0x7c, 0x41, 0x92, 0x22, 0xf3, 0x1b, 0x38, 0xec, 0x87,
0xdb, 0x61, 0x4d, 0x3d, 0x08, 0xab, 0xf5, 0x37, 0x28, 0xc5, 0x87, 0x7a, 0x82, 0x89, 0x45, 0x84, 0x6c, 0x78, 0xb7, 0x11, 0xfc, 0xcd, 0xb0, 0xa6, 0x1e, 0x84, 0xd5, 0xfc, 0x33, 0x94, 0xe2, 0x45,
0x7e, 0x0d, 0xd9, 0x48, 0x30, 0xc1, 0x15, 0xb9, 0x5c, 0x7b, 0x7a, 0xb9, 0xca, 0xf6, 0x65, 0x82, 0x3d, 0xc1, 0xc4, 0x3c, 0x42, 0xbf, 0x82, 0x6c, 0x24, 0x98, 0xe0, 0x4a, 0x5c, 0xae, 0x9d, 0x5c,
0xc8, 0x89, 0x66, 0xa1, 0x2a, 0x1c, 0xcc, 0x43, 0xee, 0xcd, 0xd8, 0x98, 0xab, 0x84, 0x16, 0xc9, 0x2e, 0xb3, 0x7d, 0x99, 0x10, 0x72, 0xa2, 0x55, 0xa8, 0x0a, 0x7b, 0xb3, 0x90, 0x7b, 0x53, 0x36,
0xea, 0x1b, 0x59, 0x90, 0x55, 0x87, 0x55, 0x3a, 0x0b, 0xb5, 0x62, 0xf2, 0x0e, 0x44, 0x8b, 0xac, 0xe2, 0x2a, 0xa1, 0x45, 0xb2, 0xfc, 0x46, 0x26, 0x64, 0xd5, 0x62, 0x95, 0xce, 0x42, 0xad, 0x98,
0xdf, 0x43, 0x45, 0x7d, 0xb7, 0x38, 0xff, 0x58, 0xc9, 0x3c, 0x85, 0x1c, 0x9b, 0xe9, 0xd8, 0xeb, 0x3c, 0x03, 0xd1, 0x94, 0xf9, 0x5b, 0xd8, 0x57, 0xdf, 0x2d, 0xce, 0x3f, 0x74, 0x65, 0x4e, 0x20,
0xb2, 0xd9, 0x67, 0x33, 0x19, 0x76, 0x6b, 0x04, 0xc6, 0xfa, 0x7c, 0x34, 0x0f, 0xfc, 0x88, 0xcb, 0xc7, 0xa6, 0x3a, 0xf6, 0xfa, 0xda, 0xec, 0xb2, 0xa9, 0x0c, 0xbb, 0xe9, 0x82, 0xb1, 0x5a, 0x1f,
0x54, 0x48, 0xe5, 0x32, 0x13, 0x32, 0x6d, 0x33, 0x79, 0x2a, 0xa5, 0x4e, 0x95, 0x63, 0xbc, 0xc5, 0xcd, 0x02, 0x3f, 0xe2, 0x32, 0x15, 0xd2, 0xb8, 0xcc, 0x84, 0x4c, 0xdb, 0x54, 0xae, 0x4a, 0xa9,
0x79, 0x37, 0x62, 0x02, 0xbd, 0xd4, 0x15, 0x40, 0xa7, 0xc1, 0xf0, 0x4e, 0xd6, 0x14, 0xbb, 0x8f, 0x55, 0xe5, 0x18, 0x6f, 0x71, 0xde, 0x8d, 0x98, 0x40, 0x2f, 0xf4, 0x0d, 0xa0, 0x93, 0x60, 0x78,
0xd5, 0x97, 0x24, 0xdc, 0x09, 0x86, 0x77, 0x4d, 0x09, 0x5a, 0x7f, 0xd6, 0xb5, 0xdd, 0x0f, 0xb4, 0x27, 0xef, 0x14, 0xbb, 0x8f, 0xcd, 0x97, 0x24, 0xdc, 0x09, 0x86, 0x77, 0x4d, 0x09, 0x9a, 0x7f,
0xef, 0x9f, 0x1c, 0xde, 0x75, 0x08, 0xf6, 0x1e, 0x0f, 0x01, 0x85, 0xa3, 0x0d, 0xe5, 0xf1, 0x2d, 0xd0, 0x77, 0xbb, 0x1f, 0xe8, 0xbd, 0x7f, 0x74, 0x78, 0x57, 0x21, 0xd8, 0x79, 0x3c, 0x04, 0x14,
0x92, 0x91, 0x4d, 0x6d, 0x45, 0xf6, 0x2b, 0xc8, 0xdd, 0x32, 0x6f, 0xba, 0x08, 0x97, 0x8a, 0x51, 0x0e, 0xd7, 0x8c, 0xc7, 0xa7, 0x48, 0x46, 0x36, 0xb5, 0x11, 0xd9, 0x2f, 0x21, 0x77, 0xcb, 0xbc,
0x22, 0x4d, 0x2d, 0x2d, 0x21, 0x4b, 0x8a, 0xf5, 0x8f, 0x1c, 0xe4, 0x62, 0x10, 0xd5, 0x20, 0x33, 0xc9, 0x3c, 0x5c, 0x18, 0x46, 0x89, 0x34, 0xb5, 0x34, 0x43, 0x16, 0x12, 0xf3, 0x9f, 0x39, 0xc8,
0x0c, 0x46, 0xcb, 0xec, 0xbe, 0x78, 0x78, 0x6c, 0xf9, 0xb7, 0x11, 0x8c, 0x38, 0x51, 0x5c, 0x54, 0xc5, 0x20, 0xaa, 0x41, 0x66, 0x18, 0xb8, 0x8b, 0xec, 0x3e, 0x7f, 0xb8, 0x6c, 0xf1, 0xb7, 0x11,
0x83, 0x27, 0xb1, 0x2a, 0x1a, 0x05, 0x8b, 0x70, 0xc8, 0xe9, 0x7c, 0x71, 0x73, 0xc7, 0xef, 0xe3, 0xb8, 0x9c, 0x28, 0x2d, 0xfa, 0x1d, 0x94, 0xe5, 0x8d, 0xf6, 0xf9, 0x84, 0xce, 0x67, 0x2e, 0x5b,
0x84, 0x1f, 0xc5, 0xc2, 0x9e, 0x92, 0x5d, 0x2b, 0x11, 0xfa, 0x03, 0x94, 0x65, 0x17, 0xf8, 0x7c, 0x26, 0xb4, 0x92, 0x58, 0xdd, 0xd0, 0x82, 0x81, 0xe2, 0x49, 0x69, 0x98, 0xfc, 0x44, 0xa7, 0x90,
0x4a, 0x17, 0xf3, 0x11, 0x5b, 0x15, 0x81, 0x99, 0xb0, 0xd8, 0xd0, 0x84, 0x81, 0x92, 0x93, 0xd2, 0x1f, 0x8b, 0xc9, 0x50, 0x67, 0x22, 0xa3, 0x8a, 0x62, 0x4f, 0x02, 0x2a, 0x07, 0x26, 0x94, 0x02,
0x30, 0xf9, 0x89, 0x4e, 0x21, 0x3f, 0x11, 0xd3, 0xa1, 0xce, 0x5e, 0x46, 0x35, 0xd2, 0x81, 0x04, 0xdf, 0x0b, 0x7c, 0x1a, 0x8d, 0x19, 0xad, 0xbd, 0xfa, 0x5a, 0x15, 0x6b, 0x91, 0x14, 0x14, 0xd8,
0x54, 0xde, 0x2c, 0x28, 0x05, 0xbe, 0x17, 0xf8, 0x34, 0x9a, 0x30, 0x5a, 0x7b, 0xfd, 0xb5, 0x6a, 0x1b, 0xb3, 0xda, 0xab, 0xaf, 0xd1, 0xa7, 0x50, 0x50, 0x25, 0xc3, 0xdf, 0xcf, 0xbc, 0xf0, 0x5e,
0xf0, 0x22, 0x29, 0x28, 0xb0, 0x37, 0x61, 0xb5, 0xd7, 0x5f, 0xa3, 0xcf, 0xa1, 0xa0, 0xda, 0x8c, 0x55, 0x69, 0x89, 0xa8, 0x2a, 0xc2, 0x0a, 0x41, 0x47, 0x90, 0xbd, 0x9d, 0xb0, 0x51, 0xa4, 0x2a,
0x7f, 0x98, 0x7b, 0xe1, 0xbd, 0xea, 0xec, 0x12, 0x51, 0x9d, 0x87, 0x15, 0x82, 0x8e, 0x21, 0x7b, 0xb3, 0x44, 0xf4, 0x07, 0xfa, 0x0a, 0x8e, 0xe2, 0x18, 0xd0, 0x28, 0x98, 0x87, 0x43, 0x4e, 0x3d,
0x3b, 0x65, 0xe3, 0x48, 0x75, 0x73, 0x89, 0xe8, 0x0f, 0xeb, 0xbf, 0x19, 0x28, 0x24, 0x42, 0x80, 0xdf, 0xe5, 0xef, 0x55, 0x5d, 0x96, 0x08, 0x8a, 0xb9, 0x9e, 0xa2, 0x2c, 0xc9, 0x98, 0x7f, 0xcd,
0x8a, 0x70, 0x40, 0x70, 0x0f, 0x93, 0x77, 0xb8, 0x69, 0x7c, 0x86, 0x4c, 0x38, 0x1e, 0x38, 0x6f, 0x42, 0x21, 0x11, 0x00, 0x54, 0x84, 0x3d, 0x82, 0x7b, 0x98, 0xbc, 0xc5, 0x4d, 0xe3, 0x13, 0x54,
0x1d, 0xf7, 0x7b, 0x87, 0x5e, 0xd7, 0xdf, 0x77, 0xb1, 0xd3, 0xa7, 0xed, 0x7a, 0xaf, 0x6d, 0xa4, 0x81, 0xa3, 0x81, 0xfd, 0xc6, 0x76, 0x7e, 0xb0, 0xe9, 0x75, 0xfd, 0x5d, 0x17, 0xdb, 0x7d, 0xda,
0xd0, 0x73, 0x30, 0x6d, 0xa7, 0xe1, 0x12, 0x82, 0x1b, 0xfd, 0x95, 0xac, 0xde, 0x75, 0x07, 0x4e, 0xae, 0xf7, 0xda, 0x46, 0x0a, 0x3d, 0x83, 0x8a, 0x65, 0x37, 0x1c, 0x42, 0x70, 0xa3, 0xbf, 0xe4,
0xdf, 0xd8, 0x43, 0x9f, 0xc3, 0x69, 0xcb, 0x76, 0xea, 0x1d, 0xba, 0xe6, 0x34, 0x3a, 0xfd, 0x77, 0xea, 0x5d, 0x67, 0x60, 0xf7, 0x8d, 0x1d, 0xf4, 0x29, 0x9c, 0xb6, 0x2c, 0xbb, 0xde, 0xa1, 0x2b,
0x14, 0xff, 0x70, 0x6d, 0x93, 0xf7, 0x46, 0x7a, 0x17, 0xa1, 0xdd, 0xef, 0x34, 0x96, 0x1a, 0x32, 0x4d, 0xa3, 0xd3, 0x7f, 0x4b, 0xf1, 0x8f, 0xd7, 0x16, 0x79, 0x67, 0xa4, 0xb7, 0x09, 0xda, 0xfd,
0xe8, 0x19, 0x3c, 0xd1, 0x04, 0x7d, 0x84, 0xf6, 0x5d, 0x97, 0xf6, 0x5c, 0xd7, 0x31, 0xb2, 0xe8, 0x4e, 0x63, 0x61, 0x21, 0x83, 0x9e, 0xc2, 0x13, 0x2d, 0xd0, 0x4b, 0x68, 0xdf, 0x71, 0x68, 0xcf,
0x10, 0x4a, 0xb6, 0xf3, 0xae, 0xde, 0xb1, 0x9b, 0x94, 0xe0, 0x7a, 0xa7, 0x6b, 0xec, 0xa3, 0x23, 0x71, 0x6c, 0x23, 0x8b, 0x0e, 0xa0, 0x64, 0xd9, 0x6f, 0xeb, 0x1d, 0xab, 0x49, 0x09, 0xae, 0x77,
0xa8, 0x6c, 0xf3, 0x72, 0x52, 0xc5, 0x92, 0xe7, 0x3a, 0xb6, 0xeb, 0xd0, 0x77, 0x98, 0xf4, 0x6c, 0xba, 0xc6, 0x2e, 0x3a, 0x84, 0xfd, 0x4d, 0x5d, 0x4e, 0x9a, 0x58, 0xe8, 0x1c, 0xdb, 0x72, 0x6c,
0xd7, 0x31, 0x0e, 0xd0, 0x09, 0xa0, 0x4d, 0x51, 0xbb, 0x5b, 0x6f, 0x18, 0x79, 0xf4, 0x04, 0x0e, 0xfa, 0x16, 0x93, 0x9e, 0xe5, 0xd8, 0xc6, 0x1e, 0x3a, 0x06, 0xb4, 0x4e, 0xb5, 0xbb, 0xf5, 0x86,
0x37, 0xf1, 0xb7, 0xf8, 0xbd, 0x01, 0x32, 0x0c, 0xda, 0x31, 0xfa, 0x06, 0x77, 0xdc, 0xef, 0x69, 0x91, 0x47, 0x4f, 0xe0, 0x60, 0x1d, 0x7f, 0x83, 0xdf, 0x19, 0x20, 0xc3, 0xa0, 0x37, 0x46, 0x5f,
0xd7, 0x76, 0xec, 0xee, 0xa0, 0x6b, 0x14, 0xd0, 0x31, 0x18, 0x2d, 0x8c, 0xa9, 0xed, 0xf4, 0x06, 0xe3, 0x8e, 0xf3, 0x03, 0xed, 0x5a, 0xb6, 0xd5, 0x1d, 0x74, 0x8d, 0x02, 0x3a, 0x02, 0xa3, 0x85,
0xad, 0x96, 0xdd, 0xb0, 0xb1, 0xd3, 0x37, 0x8a, 0xda, 0xf2, 0xae, 0x8b, 0x97, 0xe4, 0x81, 0x46, 0x31, 0xb5, 0xec, 0xde, 0xa0, 0xd5, 0xb2, 0x1a, 0x16, 0xb6, 0xfb, 0x46, 0x51, 0x7b, 0xde, 0x76,
0xbb, 0xee, 0x38, 0xb8, 0x43, 0x9b, 0x76, 0xaf, 0xfe, 0xa6, 0x83, 0x9b, 0x46, 0x19, 0x9d, 0xc1, 0xf0, 0x92, 0x5c, 0xd0, 0x68, 0xd7, 0x6d, 0x1b, 0x77, 0x68, 0xd3, 0xea, 0xd5, 0x5f, 0x77, 0x70,
0xb3, 0x3e, 0xee, 0x5e, 0xbb, 0xa4, 0x4e, 0xde, 0xd3, 0xa5, 0xbc, 0x55, 0xb7, 0x3b, 0x03, 0x82, 0xd3, 0x28, 0xa3, 0x33, 0x78, 0xda, 0xc7, 0xdd, 0x6b, 0x87, 0xd4, 0xc9, 0x3b, 0xba, 0xe0, 0x5b,
0x8d, 0x0a, 0xfa, 0x02, 0xce, 0x08, 0xfe, 0x6e, 0x60, 0x13, 0xdc, 0xa4, 0x8e, 0xdb, 0xc4, 0xb4, 0x75, 0xab, 0x33, 0x20, 0xd8, 0xd8, 0x47, 0x9f, 0xc1, 0x19, 0xc1, 0xdf, 0x0f, 0x2c, 0x82, 0x9b,
0x85, 0xeb, 0xfd, 0x01, 0xc1, 0xb4, 0x6b, 0xf7, 0x7a, 0xb6, 0xf3, 0xad, 0x61, 0xa0, 0x5f, 0xc2, 0xd4, 0x76, 0x9a, 0x98, 0xb6, 0x70, 0xbd, 0x3f, 0x20, 0x98, 0x76, 0xad, 0x5e, 0xcf, 0xb2, 0xbf,
0xf9, 0x8a, 0xb2, 0x52, 0xb0, 0xc5, 0x3a, 0x94, 0xf7, 0x5b, 0xe6, 0xd3, 0xc1, 0x3f, 0xf4, 0xe9, 0x35, 0x0c, 0xf4, 0x0b, 0x38, 0x5f, 0x4a, 0x96, 0x06, 0x36, 0x54, 0x07, 0xf2, 0x7c, 0x8b, 0x7c,
0x35, 0xc6, 0xc4, 0x40, 0xa8, 0x0a, 0x27, 0x6b, 0xf3, 0xda, 0x40, 0x6c, 0xfb, 0x48, 0xca, 0xae, 0xda, 0xf8, 0xc7, 0x3e, 0xbd, 0xc6, 0x98, 0x18, 0x08, 0x55, 0xe1, 0x78, 0xe5, 0x5e, 0x3b, 0x88,
0x31, 0xe9, 0xd6, 0x1d, 0x99, 0xe0, 0x0d, 0xd9, 0xb1, 0x74, 0x7b, 0x2d, 0xdb, 0x76, 0xfb, 0x89, 0x7d, 0x1f, 0x4a, 0xee, 0x1a, 0x93, 0x6e, 0xdd, 0x96, 0x09, 0x5e, 0xe3, 0x8e, 0xe4, 0xb6, 0x57,
0xf5, 0xaf, 0x34, 0x94, 0x36, 0x8a, 0x1e, 0x3d, 0x87, 0x7c, 0xe4, 0x8d, 0x7d, 0x26, 0x64, 0x2b, 0xdc, 0xe6, 0xb6, 0x9f, 0xa0, 0x23, 0xd8, 0x5f, 0x78, 0x5b, 0x80, 0xff, 0xce, 0xa1, 0x13, 0x40,
0xeb, 0x2e, 0x5f, 0x03, 0xea, 0xa5, 0x98, 0x30, 0xcf, 0xd7, 0xe3, 0x45, 0x77, 0x5b, 0x5e, 0x21, 0x03, 0x9b, 0xe0, 0x7a, 0x53, 0x1e, 0x7e, 0x49, 0xfc, 0x27, 0xf7, 0x5d, 0x66, 0x6f, 0xc7, 0x48,
0x6a, 0xb8, 0x3c, 0x85, 0xdc, 0xf2, 0xa5, 0x49, 0xab, 0x06, 0xd9, 0x1f, 0xea, 0x17, 0xe6, 0x39, 0x9b, 0x7f, 0x4f, 0x43, 0x69, 0xad, 0xb6, 0xd0, 0x33, 0xc8, 0x47, 0xde, 0xc8, 0x67, 0x42, 0x56,
0xe4, 0xe5, 0xfc, 0x8a, 0x04, 0x9b, 0xcd, 0x55, 0xef, 0x94, 0xc8, 0x1a, 0x40, 0xbf, 0x80, 0xd2, 0xbf, 0x6e, 0x0c, 0x2b, 0x40, 0x3d, 0x2e, 0x63, 0xe6, 0xf9, 0xba, 0x23, 0xe9, 0x8e, 0x9c, 0x57,
0x8c, 0x47, 0x11, 0x1b, 0x73, 0xaa, 0xeb, 0x1f, 0x14, 0xa3, 0x18, 0x83, 0x2d, 0x89, 0x49, 0xd2, 0x88, 0xea, 0x47, 0x27, 0x90, 0x5b, 0x3c, 0x4e, 0x69, 0x55, 0x87, 0xbb, 0x43, 0xfd, 0x28, 0x3d,
0xb2, 0x7f, 0x35, 0x29, 0xab, 0x49, 0x31, 0xa8, 0x49, 0xdb, 0xe3, 0x53, 0xb0, 0xb8, 0xcd, 0x92, 0x83, 0xbc, 0x6c, 0x79, 0x91, 0x60, 0xd3, 0x99, 0x2a, 0xd1, 0x12, 0x59, 0x01, 0xe8, 0x73, 0x28,
0xe3, 0x53, 0x30, 0xf4, 0x0a, 0x0e, 0x75, 0x2f, 0x7b, 0xbe, 0x37, 0x5b, 0xcc, 0x74, 0x4f, 0xe7, 0x4d, 0x79, 0x14, 0xb1, 0x11, 0xa7, 0xba, 0xcc, 0x40, 0x29, 0x8a, 0x31, 0xd8, 0x52, 0xd5, 0xf6,
0x94, 0xcb, 0x15, 0xd5, 0xd3, 0x1a, 0x57, 0xad, 0xfd, 0x0c, 0x0e, 0x6e, 0x58, 0xc4, 0xe5, 0xe4, 0x39, 0x2c, 0xca, 0x3e, 0x16, 0x65, 0xb5, 0x28, 0x06, 0xb5, 0x68, 0xb3, 0xe3, 0x0a, 0x16, 0x57,
0x56, 0xef, 0x67, 0x89, 0xe4, 0xe4, 0x77, 0x8b, 0x73, 0x29, 0x92, 0xf3, 0x3c, 0x94, 0xd3, 0x24, 0x73, 0xb2, 0xe3, 0x0a, 0x86, 0x5e, 0xc2, 0x81, 0x6e, 0x19, 0x9e, 0xef, 0x4d, 0xe7, 0x53, 0xdd,
0xaf, 0x45, 0xb7, 0x9c, 0x13, 0x19, 0xc7, 0x95, 0x05, 0xf6, 0x61, 0x6d, 0xa1, 0x90, 0xb0, 0xa0, 0x3a, 0x72, 0x6a, 0xcb, 0xfb, 0xaa, 0x75, 0x68, 0x5c, 0x75, 0x90, 0xa7, 0xb0, 0x77, 0xc3, 0x22,
0x71, 0x65, 0xe1, 0x15, 0x1c, 0xf2, 0x0f, 0x22, 0x64, 0x34, 0x98, 0xb3, 0x1f, 0x17, 0x9c, 0x8e, 0x2e, 0x9b, 0x7d, 0x5c, 0xda, 0x39, 0xf9, 0xdd, 0xe2, 0x5c, 0x52, 0xf2, 0x09, 0x08, 0x65, 0xd3,
0x98, 0x60, 0x66, 0x51, 0x05, 0xb7, 0xa2, 0x04, 0xae, 0xc2, 0x9b, 0x4c, 0x30, 0xeb, 0x39, 0x54, 0xca, 0x6b, 0xea, 0x96, 0x73, 0x22, 0xe3, 0xb8, 0xf4, 0xc0, 0xde, 0xaf, 0x3c, 0x14, 0x12, 0x1e,
0x09, 0x8f, 0xb8, 0xe8, 0x7a, 0x51, 0xe4, 0x05, 0x7e, 0x23, 0xf0, 0x45, 0x18, 0x4c, 0xe3, 0x07, 0x34, 0xae, 0x3c, 0xbc, 0x84, 0x03, 0xfe, 0x5e, 0x84, 0x8c, 0x06, 0x33, 0xf6, 0xd3, 0x9c, 0x53,
0xc0, 0x3a, 0x83, 0xd3, 0x9d, 0x52, 0x3d, 0xc1, 0xe5, 0xe1, 0xef, 0x16, 0x3c, 0xbc, 0xdf, 0x7d, 0x97, 0x09, 0x56, 0x29, 0xaa, 0xe0, 0xee, 0x2b, 0xc2, 0x51, 0x78, 0x93, 0x09, 0x66, 0x3e, 0x83,
0xf8, 0x2d, 0x9c, 0xee, 0x94, 0xc6, 0xe3, 0xff, 0x2b, 0xc8, 0xfa, 0xc1, 0x88, 0x47, 0x66, 0x4a, 0x2a, 0xe1, 0x11, 0x17, 0x5d, 0x2f, 0x8a, 0xbc, 0xc0, 0x6f, 0x04, 0xbe, 0x08, 0x83, 0x49, 0xfc,
0x2d, 0x00, 0x27, 0x89, 0xb9, 0xe9, 0x04, 0x23, 0xde, 0xf6, 0x22, 0x11, 0x84, 0xf7, 0x44, 0x93, 0x66, 0x98, 0x67, 0x70, 0xba, 0x95, 0xd5, 0x4d, 0x5f, 0x2e, 0xfe, 0x7e, 0xce, 0xc3, 0xfb, 0xed,
0xac, 0xff, 0xa4, 0xa0, 0x90, 0x80, 0xd1, 0x09, 0xec, 0xc7, 0x33, 0x5a, 0x17, 0x55, 0xfc, 0x85, 0x8b, 0xdf, 0xc0, 0xe9, 0x56, 0x36, 0x7e, 0x31, 0xbe, 0x84, 0xac, 0x1f, 0xb8, 0x3c, 0xaa, 0xa4,
0x5e, 0x42, 0x79, 0xca, 0x22, 0x41, 0xe5, 0xc8, 0xa6, 0x32, 0x49, 0xf1, 0x7b, 0xb7, 0x85, 0xa2, 0xd4, 0xcc, 0x70, 0x9c, 0x68, 0xcf, 0x76, 0xe0, 0xf2, 0xb6, 0x17, 0x89, 0x20, 0xbc, 0x27, 0x5a,
0x6f, 0xe0, 0x69, 0x20, 0x26, 0x3c, 0xd4, 0xab, 0x4c, 0xb4, 0x18, 0x0e, 0x79, 0x14, 0xd1, 0x79, 0x64, 0xfe, 0x2b, 0x05, 0x85, 0x04, 0x8c, 0x8e, 0x61, 0x77, 0x36, 0xbf, 0xb9, 0xe3, 0xf7, 0xf1,
0x18, 0xdc, 0xa8, 0x52, 0xdb, 0x23, 0x8f, 0x89, 0xd1, 0x6b, 0x38, 0x88, 0x6b, 0x24, 0x32, 0x33, 0xa5, 0x8a, 0xbf, 0xd0, 0x0b, 0x28, 0x4f, 0x58, 0x24, 0xa8, 0xec, 0x96, 0x54, 0x26, 0x29, 0x7e,
0xca, 0xf5, 0x67, 0x0f, 0x47, 0xfe, 0xd2, 0xfb, 0x15, 0xd5, 0xfa, 0x77, 0x0a, 0xca, 0x9b, 0x42, 0x22, 0x37, 0x50, 0xf4, 0x0d, 0x9c, 0x04, 0x62, 0xcc, 0x43, 0x3d, 0xfd, 0x44, 0xf3, 0xe1, 0x90,
0xf4, 0x42, 0x55, 0xbf, 0x2a, 0x41, 0x6f, 0xa4, 0xee, 0x91, 0x21, 0x09, 0xe4, 0x93, 0xef, 0x52, 0x47, 0x11, 0x9d, 0x85, 0xc1, 0x8d, 0xba, 0x6a, 0x3b, 0xe4, 0x31, 0x1a, 0xbd, 0x82, 0xbd, 0xf8,
0x83, 0xe3, 0x99, 0xe7, 0xd3, 0x39, 0xf7, 0xd9, 0xd4, 0xfb, 0x2b, 0xa7, 0xcb, 0x45, 0x22, 0xad, 0x8e, 0x44, 0x95, 0x8c, 0xda, 0xfa, 0xd3, 0x87, 0x2f, 0xcb, 0x62, 0xf7, 0x4b, 0xa9, 0xf9, 0x8f,
0xd8, 0x3b, 0x65, 0xc8, 0x82, 0xe2, 0xc6, 0xa5, 0x33, 0xea, 0xd2, 0x1b, 0xd8, 0xab, 0x7f, 0xa6, 0x14, 0x94, 0xd7, 0x49, 0xf4, 0x5c, 0xdd, 0x7e, 0x75, 0x05, 0x3d, 0x57, 0x9d, 0x23, 0x43, 0x12,
0xa0, 0x98, 0x5c, 0x89, 0x50, 0x09, 0xf2, 0xb6, 0x43, 0x5b, 0x1d, 0xfb, 0xdb, 0x76, 0xdf, 0xf8, 0xc8, 0x47, 0x9f, 0xa5, 0x06, 0x47, 0x53, 0xcf, 0xa7, 0x33, 0xee, 0xb3, 0x89, 0xf7, 0x27, 0x4e,
0x4c, 0x7e, 0xf6, 0x06, 0x8d, 0x06, 0xc6, 0x4d, 0xdc, 0x34, 0x52, 0x08, 0x41, 0x59, 0x4e, 0x02, 0x17, 0xb3, 0x47, 0x5a, 0xa9, 0xb7, 0x72, 0xc8, 0x84, 0xe2, 0xda, 0xa1, 0x33, 0xea, 0xd0, 0x6b,
0xdc, 0xa4, 0x7d, 0xbb, 0x8b, 0xdd, 0x81, 0x7c, 0x16, 0x8e, 0xa0, 0x12, 0x63, 0x8e, 0x4b, 0x89, 0xd8, 0xcb, 0xbf, 0xa5, 0xa0, 0x98, 0x9c, 0xa2, 0x50, 0x09, 0xf2, 0x96, 0x4d, 0x5b, 0x1d, 0xeb,
0x3b, 0xe8, 0x63, 0x23, 0x8d, 0x0c, 0x28, 0xc6, 0x20, 0x26, 0xc4, 0x25, 0x46, 0x46, 0xce, 0xb2, 0xdb, 0x76, 0xdf, 0xf8, 0x44, 0x7e, 0xf6, 0x06, 0x8d, 0x06, 0xc6, 0x4d, 0xdc, 0x34, 0x52, 0x08,
0x18, 0x79, 0xf8, 0xc4, 0x34, 0x71, 0xbf, 0x6e, 0x77, 0x7a, 0x46, 0xb6, 0xf6, 0xf7, 0x0c, 0xec, 0x41, 0x59, 0x36, 0x04, 0xdc, 0xa4, 0x7d, 0xab, 0x8b, 0x9d, 0x81, 0x7c, 0x4b, 0x0e, 0x61, 0x3f,
0xab, 0x15, 0x22, 0x44, 0x6d, 0x28, 0x24, 0x76, 0x71, 0x74, 0x96, 0xc8, 0xc0, 0xc3, 0x1d, 0xbd, 0xc6, 0x6c, 0x87, 0x12, 0x67, 0xd0, 0xc7, 0x46, 0x1a, 0x19, 0x50, 0x8c, 0x41, 0x4c, 0x88, 0x43,
0x6a, 0xee, 0xde, 0xf1, 0x16, 0xd1, 0x6f, 0x52, 0xe8, 0x8f, 0x50, 0x4c, 0x6e, 0x96, 0x28, 0xb9, 0x8c, 0x8c, 0x6c, 0x80, 0x31, 0xf2, 0xf0, 0x5d, 0x6a, 0xe2, 0x7e, 0xdd, 0xea, 0xf4, 0x8c, 0x6c,
0x31, 0xec, 0x58, 0x39, 0x3f, 0xaa, 0xeb, 0x2d, 0x18, 0x38, 0x12, 0xde, 0x4c, 0xbe, 0xf6, 0xf1, 0xed, 0x2f, 0x19, 0xd8, 0x55, 0x53, 0x47, 0x88, 0xda, 0x50, 0x48, 0x8c, 0xef, 0xe8, 0x2c, 0x91,
0xce, 0x86, 0xaa, 0x09, 0xfe, 0xd6, 0x22, 0x58, 0x3d, 0xdd, 0x29, 0x8b, 0xfb, 0xa3, 0xa3, 0xaf, 0x81, 0x87, 0x63, 0x7d, 0xb5, 0xb2, 0x7d, 0x2c, 0x9c, 0x47, 0x5f, 0xa5, 0xd0, 0x77, 0x50, 0x4c,
0x18, 0x6f, 0x4d, 0x0f, 0xae, 0xb8, 0xb9, 0xaa, 0x55, 0x5f, 0x3c, 0x26, 0x8e, 0xb5, 0x8d, 0xe0, 0x0e, 0xa3, 0x28, 0x39, 0x64, 0x6c, 0x99, 0x52, 0x3f, 0x68, 0xeb, 0x0d, 0x18, 0x38, 0x12, 0xde,
0x68, 0x47, 0x27, 0xa3, 0x5f, 0x25, 0x3d, 0x78, 0x74, 0x0e, 0x54, 0x5f, 0xfe, 0x1c, 0x6d, 0x6d, 0x54, 0x0e, 0x15, 0xf1, 0x98, 0x87, 0xaa, 0x09, 0xfd, 0xc6, 0xec, 0x58, 0x3d, 0xdd, 0xca, 0xc5,
0x65, 0x47, 0xcb, 0x6f, 0x58, 0x79, 0x7c, 0x60, 0x6c, 0x58, 0xf9, 0xc8, 0xe4, 0x78, 0xf3, 0xdb, 0xf5, 0xd1, 0xd1, 0x47, 0x8c, 0x07, 0xad, 0x07, 0x47, 0x5c, 0x9f, 0xee, 0xaa, 0xcf, 0x1f, 0xa3,
0x3f, 0x5d, 0x8d, 0x3d, 0x31, 0x59, 0xdc, 0x5c, 0x0e, 0x83, 0xd9, 0xd5, 0xd4, 0x1b, 0x4f, 0x84, 0x63, 0x6b, 0x2e, 0x1c, 0x6e, 0xa9, 0x64, 0xf4, 0xcb, 0xe4, 0x0e, 0x1e, 0xed, 0x03, 0xd5, 0x17,
0xef, 0xf9, 0x63, 0x9f, 0x8b, 0xbf, 0x04, 0xe1, 0xdd, 0xd5, 0xd4, 0x1f, 0x5d, 0xa9, 0x2d, 0xf4, 0xff, 0x4f, 0xb6, 0xf2, 0xb2, 0xa5, 0xe4, 0xd7, 0xbc, 0x3c, 0xde, 0x30, 0xd6, 0xbc, 0x7c, 0xa0,
0x6a, 0xa5, 0xee, 0x66, 0x5f, 0xfd, 0x0f, 0xf7, 0xbb, 0xff, 0x07, 0x00, 0x00, 0xff, 0xff, 0xb7, 0x73, 0xbc, 0xfe, 0xf5, 0xef, 0xaf, 0x46, 0x9e, 0x18, 0xcf, 0x6f, 0x2e, 0x87, 0xc1, 0xf4, 0x6a,
0x0c, 0x44, 0x60, 0xf3, 0x0d, 0x00, 0x00, 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. // Reference imports to suppress errors if they are not otherwise used.

@ -194,16 +194,23 @@ message Failure {
TEMPORARY_NODE_FAILURE = 19; TEMPORARY_NODE_FAILURE = 19;
PERMANENT_NODE_FAILURE = 20; PERMANENT_NODE_FAILURE = 20;
PERMANENT_CHANNEL_FAILURE = 21; 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.
*/
UNREADABLE_FAILURE = 999;
} }
/// Failure code as defined in the Lightning spec /// Failure code as defined in the Lightning spec
FailureCode code = 1; FailureCode code = 1;
/** reserved 2;
The node pubkey of the intermediate or final node that generated the failure
message.
**/
bytes failure_source_pubkey = 2;
/// An optional channel update message. /// An optional channel update message.
ChannelUpdate channel_update = 3; ChannelUpdate channel_update = 3;
@ -219,6 +226,12 @@ message Failure {
/// A failure type-dependent flags value. /// A failure type-dependent flags value.
uint32 flags = 7; 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;
} }

@ -310,6 +310,11 @@ func (s *Server) SendToRoute(ctx context.Context,
func marshallError(sendError error) (*Failure, error) { func marshallError(sendError error) (*Failure, error) {
response := &Failure{} response := &Failure{}
if sendError == htlcswitch.ErrUnreadableFailureMessage {
response.Code = Failure_UNREADABLE_FAILURE
return response, nil
}
fErr, ok := sendError.(*htlcswitch.ForwardingError) fErr, ok := sendError.(*htlcswitch.ForwardingError)
if !ok { if !ok {
return nil, sendError return nil, sendError
@ -394,12 +399,11 @@ func marshallError(sendError error) (*Failure, error) {
case *lnwire.FailPermanentChannelFailure: case *lnwire.FailPermanentChannelFailure:
response.Code = Failure_PERMANENT_CHANNEL_FAILURE response.Code = Failure_PERMANENT_CHANNEL_FAILURE
default: default:
return nil, errors.New("unknown wire error") response.Code = Failure_UNKNOWN_FAILURE
} }
response.FailureSourcePubkey = fErr.ErrorSource.SerializeCompressed() response.FailureSourceIndex = uint32(fErr.FailureSourceIdx)
return response, nil return response, nil
} }

@ -162,14 +162,14 @@ type FailInvalidRealm struct{}
// Returns a human readable string describing the target FailureMessage. // Returns a human readable string describing the target FailureMessage.
// //
// NOTE: Implements the error interface. // NOTE: Implements the error interface.
func (f FailInvalidRealm) Error() string { func (f *FailInvalidRealm) Error() string {
return f.Code().String() return f.Code().String()
} }
// Code returns the failure unique code. // Code returns the failure unique code.
// //
// NOTE: Part of the FailureMessage interface. // NOTE: Part of the FailureMessage interface.
func (f FailInvalidRealm) Code() FailCode { func (f *FailInvalidRealm) Code() FailCode {
return CodeInvalidRealm return CodeInvalidRealm
} }
@ -181,14 +181,14 @@ type FailTemporaryNodeFailure struct{}
// Code returns the failure unique code. // Code returns the failure unique code.
// NOTE: Part of the FailureMessage interface. // NOTE: Part of the FailureMessage interface.
func (f FailTemporaryNodeFailure) Code() FailCode { func (f *FailTemporaryNodeFailure) Code() FailCode {
return CodeTemporaryNodeFailure return CodeTemporaryNodeFailure
} }
// Returns a human readable string describing the target FailureMessage. // Returns a human readable string describing the target FailureMessage.
// //
// NOTE: Implements the error interface. // NOTE: Implements the error interface.
func (f FailTemporaryNodeFailure) Error() string { func (f *FailTemporaryNodeFailure) Error() string {
return f.Code().String() return f.Code().String()
} }
@ -201,14 +201,14 @@ type FailPermanentNodeFailure struct{}
// Code returns the failure unique code. // Code returns the failure unique code.
// //
// NOTE: Part of the FailureMessage interface. // NOTE: Part of the FailureMessage interface.
func (f FailPermanentNodeFailure) Code() FailCode { func (f *FailPermanentNodeFailure) Code() FailCode {
return CodePermanentNodeFailure return CodePermanentNodeFailure
} }
// Returns a human readable string describing the target FailureMessage. // Returns a human readable string describing the target FailureMessage.
// //
// NOTE: Implements the error interface. // NOTE: Implements the error interface.
func (f FailPermanentNodeFailure) Error() string { func (f *FailPermanentNodeFailure) Error() string {
return f.Code().String() return f.Code().String()
} }
@ -222,14 +222,14 @@ type FailRequiredNodeFeatureMissing struct{}
// Code returns the failure unique code. // Code returns the failure unique code.
// //
// NOTE: Part of the FailureMessage interface. // NOTE: Part of the FailureMessage interface.
func (f FailRequiredNodeFeatureMissing) Code() FailCode { func (f *FailRequiredNodeFeatureMissing) Code() FailCode {
return CodeRequiredNodeFeatureMissing return CodeRequiredNodeFeatureMissing
} }
// Returns a human readable string describing the target FailureMessage. // Returns a human readable string describing the target FailureMessage.
// //
// NOTE: Implements the error interface. // NOTE: Implements the error interface.
func (f FailRequiredNodeFeatureMissing) Error() string { func (f *FailRequiredNodeFeatureMissing) Error() string {
return f.Code().String() return f.Code().String()
} }
@ -242,14 +242,14 @@ type FailPermanentChannelFailure struct{}
// Code returns the failure unique code. // Code returns the failure unique code.
// //
// NOTE: Part of the FailureMessage interface. // NOTE: Part of the FailureMessage interface.
func (f FailPermanentChannelFailure) Code() FailCode { func (f *FailPermanentChannelFailure) Code() FailCode {
return CodePermanentChannelFailure return CodePermanentChannelFailure
} }
// Returns a human readable string describing the target FailureMessage. // Returns a human readable string describing the target FailureMessage.
// //
// NOTE: Implements the error interface. // NOTE: Implements the error interface.
func (f FailPermanentChannelFailure) Error() string { func (f *FailPermanentChannelFailure) Error() string {
return f.Code().String() return f.Code().String()
} }
@ -263,14 +263,14 @@ type FailRequiredChannelFeatureMissing struct{}
// Code returns the failure unique code. // Code returns the failure unique code.
// //
// NOTE: Part of the FailureMessage interface. // NOTE: Part of the FailureMessage interface.
func (f FailRequiredChannelFeatureMissing) Code() FailCode { func (f *FailRequiredChannelFeatureMissing) Code() FailCode {
return CodeRequiredChannelFeatureMissing return CodeRequiredChannelFeatureMissing
} }
// Returns a human readable string describing the target FailureMessage. // Returns a human readable string describing the target FailureMessage.
// //
// NOTE: Implements the error interface. // NOTE: Implements the error interface.
func (f FailRequiredChannelFeatureMissing) Error() string { func (f *FailRequiredChannelFeatureMissing) Error() string {
return f.Code().String() return f.Code().String()
} }
@ -283,14 +283,14 @@ type FailUnknownNextPeer struct{}
// Code returns the failure unique code. // Code returns the failure unique code.
// //
// NOTE: Part of the FailureMessage interface. // NOTE: Part of the FailureMessage interface.
func (f FailUnknownNextPeer) Code() FailCode { func (f *FailUnknownNextPeer) Code() FailCode {
return CodeUnknownNextPeer return CodeUnknownNextPeer
} }
// Returns a human readable string describing the target FailureMessage. // Returns a human readable string describing the target FailureMessage.
// //
// NOTE: Implements the error interface. // NOTE: Implements the error interface.
func (f FailUnknownNextPeer) Error() string { func (f *FailUnknownNextPeer) Error() string {
return f.Code().String() return f.Code().String()
} }
@ -306,14 +306,14 @@ type FailIncorrectPaymentAmount struct{}
// Code returns the failure unique code. // Code returns the failure unique code.
// //
// NOTE: Part of the FailureMessage interface. // NOTE: Part of the FailureMessage interface.
func (f FailIncorrectPaymentAmount) Code() FailCode { func (f *FailIncorrectPaymentAmount) Code() FailCode {
return CodeIncorrectPaymentAmount return CodeIncorrectPaymentAmount
} }
// Returns a human readable string describing the target FailureMessage. // Returns a human readable string describing the target FailureMessage.
// //
// NOTE: Implements the error interface. // NOTE: Implements the error interface.
func (f FailIncorrectPaymentAmount) Error() string { func (f *FailIncorrectPaymentAmount) Error() string {
return f.Code().String() return f.Code().String()
} }
@ -344,21 +344,21 @@ func NewFailUnknownPaymentHash(amt MilliSatoshi) *FailUnknownPaymentHash {
} }
// Amount is the value of the extended HTLC. // Amount is the value of the extended HTLC.
func (f FailUnknownPaymentHash) Amount() MilliSatoshi { func (f *FailUnknownPaymentHash) Amount() MilliSatoshi {
return f.amount return f.amount
} }
// Code returns the failure unique code. // Code returns the failure unique code.
// //
// NOTE: Part of the FailureMessage interface. // NOTE: Part of the FailureMessage interface.
func (f FailUnknownPaymentHash) Code() FailCode { func (f *FailUnknownPaymentHash) Code() FailCode {
return CodeUnknownPaymentHash return CodeUnknownPaymentHash
} }
// Returns a human readable string describing the target FailureMessage. // Returns a human readable string describing the target FailureMessage.
// //
// NOTE: Implements the error interface. // NOTE: Implements the error interface.
func (f FailUnknownPaymentHash) Error() string { func (f *FailUnknownPaymentHash) Error() string {
return fmt.Sprintf("UnknownPaymentHash(amt=%v)", f.amount) return fmt.Sprintf("UnknownPaymentHash(amt=%v)", f.amount)
} }
@ -398,14 +398,14 @@ type FailFinalExpiryTooSoon struct{}
// Code returns the failure unique code. // Code returns the failure unique code.
// //
// NOTE: Part of the FailureMessage interface. // NOTE: Part of the FailureMessage interface.
func (f FailFinalExpiryTooSoon) Code() FailCode { func (f *FailFinalExpiryTooSoon) Code() FailCode {
return CodeFinalExpiryTooSoon return CodeFinalExpiryTooSoon
} }
// Returns a human readable string describing the target FailureMessage. // Returns a human readable string describing the target FailureMessage.
// //
// NOTE: Implements the error interface. // NOTE: Implements the error interface.
func (f FailFinalExpiryTooSoon) Error() string { func (f *FailFinalExpiryTooSoon) Error() string {
return f.Code().String() return f.Code().String()
} }
@ -425,7 +425,7 @@ type FailInvalidOnionVersion struct {
// Returns a human readable string describing the target FailureMessage. // Returns a human readable string describing the target FailureMessage.
// //
// NOTE: Implements the error interface. // NOTE: Implements the error interface.
func (f FailInvalidOnionVersion) Error() string { func (f *FailInvalidOnionVersion) Error() string {
return fmt.Sprintf("InvalidOnionVersion(onion_sha=%x)", f.OnionSHA256[:]) 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. // Returns a human readable string describing the target FailureMessage.
// //
// NOTE: Implements the error interface. // NOTE: Implements the error interface.
func (f FailInvalidOnionHmac) Error() string { func (f *FailInvalidOnionHmac) Error() string {
return fmt.Sprintf("InvalidOnionHMAC(onion_sha=%x)", f.OnionSHA256[:]) 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. // Returns a human readable string describing the target FailureMessage.
// //
// NOTE: Implements the error interface. // NOTE: Implements the error interface.
func (f FailInvalidOnionKey) Error() string { func (f *FailInvalidOnionKey) Error() string {
return fmt.Sprintf("InvalidOnionKey(onion_sha=%x)", f.OnionSHA256[:]) 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. // Returns a human readable string describing the target FailureMessage.
// //
// NOTE: Implements the error interface. // NOTE: Implements the error interface.
func (f FailTemporaryChannelFailure) Error() string { func (f *FailTemporaryChannelFailure) Error() string {
if f.Update == nil { if f.Update == nil {
return f.Code().String() return f.Code().String()
} }
@ -688,7 +688,7 @@ func (f *FailAmountBelowMinimum) Code() FailCode {
// Returns a human readable string describing the target FailureMessage. // Returns a human readable string describing the target FailureMessage.
// //
// NOTE: Implements the error interface. // 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, return fmt.Sprintf("AmountBelowMinimum(amt=%v, update=%v", f.HtlcMsat,
spew.Sdump(f.Update)) spew.Sdump(f.Update))
} }
@ -756,7 +756,7 @@ func (f *FailFeeInsufficient) Code() FailCode {
// Returns a human readable string describing the target FailureMessage. // Returns a human readable string describing the target FailureMessage.
// //
// NOTE: Implements the error interface. // 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, return fmt.Sprintf("FeeInsufficient(htlc_amt==%v, update=%v", f.HtlcMsat,
spew.Sdump(f.Update)) spew.Sdump(f.Update))
} }
@ -945,7 +945,7 @@ func (f *FailChannelDisabled) Code() FailCode {
// Returns a human readable string describing the target FailureMessage. // Returns a human readable string describing the target FailureMessage.
// //
// NOTE: Implements the error interface. // 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, return fmt.Sprintf("ChannelDisabled(flags=%v, update=%v", f.Flags,
spew.Sdump(f.Update)) spew.Sdump(f.Update))
} }
@ -993,7 +993,7 @@ type FailFinalIncorrectCltvExpiry struct {
// Returns a human readable string describing the target FailureMessage. // Returns a human readable string describing the target FailureMessage.
// //
// NOTE: Implements the error interface. // NOTE: Implements the error interface.
func (f FailFinalIncorrectCltvExpiry) Error() string { func (f *FailFinalIncorrectCltvExpiry) Error() string {
return fmt.Sprintf("FinalIncorrectCltvExpiry(expiry=%v)", f.CltvExpiry) return fmt.Sprintf("FinalIncorrectCltvExpiry(expiry=%v)", f.CltvExpiry)
} }
@ -1038,7 +1038,7 @@ type FailFinalIncorrectHtlcAmount struct {
// Returns a human readable string describing the target FailureMessage. // Returns a human readable string describing the target FailureMessage.
// //
// NOTE: Implements the error interface. // NOTE: Implements the error interface.
func (f FailFinalIncorrectHtlcAmount) Error() string { func (f *FailFinalIncorrectHtlcAmount) Error() string {
return fmt.Sprintf("FinalIncorrectHtlcAmount(amt=%v)", return fmt.Sprintf("FinalIncorrectHtlcAmount(amt=%v)",
f.IncomingHTLCAmount) f.IncomingHTLCAmount)
} }
@ -1081,14 +1081,14 @@ type FailExpiryTooFar struct{}
// Code returns the failure unique code. // Code returns the failure unique code.
// //
// NOTE: Part of the FailureMessage interface. // NOTE: Part of the FailureMessage interface.
func (f FailExpiryTooFar) Code() FailCode { func (f *FailExpiryTooFar) Code() FailCode {
return CodeExpiryTooFar return CodeExpiryTooFar
} }
// Returns a human readable string describing the target FailureMessage. // Returns a human readable string describing the target FailureMessage.
// //
// NOTE: Implements the error interface. // NOTE: Implements the error interface.
func (f FailExpiryTooFar) Error() string { func (f *FailExpiryTooFar) Error() string {
return f.Code().String() return f.Code().String()
} }

@ -177,7 +177,7 @@ func TestFailUnknownPaymentHashOptionalAmount(t *testing.T) {
// Creation an error that is a non-pointer will allow us to skip the // Creation an error that is a non-pointer will allow us to skip the
// type assertion for the Serializable interface. As a result, the // type assertion for the Serializable interface. As a result, the
// amount body won't be written. // amount body won't be written.
onionError := FailUnknownPaymentHash{} onionError := &FailUnknownPaymentHash{}
var b bytes.Buffer var b bytes.Buffer
if err := EncodeFailure(&b, onionError, 0); err != nil { 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) 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), t.Fatalf("expected %v, got %v", spew.Sdump(onionError),
spew.Sdump(onionError2)) spew.Sdump(onionError2))
} }

@ -386,7 +386,9 @@ type testGraphInstance struct {
// a deterministical way and added to the channel graph. A list of nodes is // 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 // not required and derived from the channel data. The goal is to keep
// instantiating a test channel graph as light weight as possible. // 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 // 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 // our tests. This value isn't needed for path finding so it doesn't
// need to be unique. // need to be unique.
@ -444,13 +446,13 @@ func createTestGraphFromChannels(testChannels []*testChannel) (*testGraphInstanc
return dbNode, nil return dbNode, nil
} }
var source *channeldb.LightningNode // Add the source node.
if source, err = addNodeWithAlias("roasbeef"); err != nil { dbNode, err := addNodeWithAlias(source)
if err != nil {
return nil, err return nil, err
} }
// Set the source node if err = graph.SetSourceNode(dbNode); err != nil {
if err := graph.SetSourceNode(source); err != nil {
return nil, err return nil, err
} }
@ -464,7 +466,10 @@ func createTestGraphFromChannels(testChannels []*testChannel) (*testGraphInstanc
_, exists := aliasMap[alias] _, exists := aliasMap[alias]
if !exists { 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 { if err != nil {
t.Fatalf("unable to create graph: %v", err) 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 { if err != nil {
t.Fatalf("unable to create graph: %v", err) 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 { if err != nil {
t.Fatalf("unable to create graph: %v", err) 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 { if err != nil {
t.Fatalf("unable to create graph: %v", err) t.Fatalf("unable to create graph: %v", err)
} }
@ -2121,7 +2132,9 @@ func testProbabilityRouting(t *testing.T, p10, p11, p20, minProbability float64,
}, 20), }, 20),
} }
testGraphInstance, err := createTestGraphFromChannels(testChannels) testGraphInstance, err := createTestGraphFromChannels(
testChannels, "roasbeef",
)
if err != nil { if err != nil {
t.Fatalf("unable to create graph: %v", err) t.Fatalf("unable to create graph: %v", err)
} }

@ -17,7 +17,7 @@ import (
type errNoRoute struct { type errNoRoute struct {
// lastError is the error encountered during the last payment attempt, // lastError is the error encountered during the last payment attempt,
// if at least one attempt has been made. // if at least one attempt has been made.
lastError *htlcswitch.ForwardingError lastError error
} }
// Error returns a string representation of the error. // Error returns a string representation of the error.
@ -37,7 +37,7 @@ type paymentLifecycle struct {
finalCLTVDelta uint16 finalCLTVDelta uint16
attempt *channeldb.PaymentAttemptInfo attempt *channeldb.PaymentAttemptInfo
circuit *sphinx.Circuit circuit *sphinx.Circuit
lastError *htlcswitch.ForwardingError lastError error
} }
// resumePayment resumes the paymentLifecycle from the current state. // 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 // handleSendError inspects the given error from the Switch and determines
// whether we should make another payment attempt. // whether we should make another payment attempt.
func (p *paymentLifecycle) handleSendError(sendErr error) error { func (p *paymentLifecycle) handleSendError(sendErr error) error {
var reason channeldb.FailureReason
// If an internal, non-forwarding error occurred, we can stop trying. final, reason := p.router.processSendError(
fErr, ok := sendErr.(*htlcswitch.ForwardingError) p.paySession, &p.attempt.Route, sendErr,
if !ok { )
reason = channeldb.FailureReasonError if !final {
} else { // Save the forwarding error so it can be returned if
var final bool // this turns out to be the last attempt.
final, reason = p.router.processSendError( p.lastError = sendErr
p.paySession, &p.attempt.Route, fErr,
)
if !final { return nil
// Save the forwarding error so it can be returned if
// this turns out to be the last attempt.
p.lastError = fErr
return nil
}
} }
log.Debugf("Payment %x failed: final_outcome=%v, raw_err=%v", log.Debugf("Payment %x failed: final_outcome=%v, raw_err=%v",

@ -1812,22 +1812,58 @@ func (r *ChannelRouter) sendPayment(
// to continue with an alternative route. This is indicated by the boolean // to continue with an alternative route. This is indicated by the boolean
// return value. // return value.
func (r *ChannelRouter) processSendError(paySession PaymentSession, func (r *ChannelRouter) processSendError(paySession PaymentSession,
rt *route.Route, fErr *htlcswitch.ForwardingError) ( rt *route.Route, sendErr error) (bool, channeldb.FailureReason) {
bool, channeldb.FailureReason) {
errSource := fErr.ErrorSource // If the failure message could not be decrypted, attribute the failure
errVertex := route.NewVertex(errSource) // 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),
}
}
log.Tracef("node=%x reported failure when sending htlc", errVertex) // 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
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 // Always determine chan id ourselves, because a channel
// update with id may not be available. // update with id may not be available.
failedEdge, failedAmt, err := getFailedEdge( failedEdge, failedAmt := getFailedEdge(rt, failureSourceIdx)
rt, route.Vertex(errVertex),
)
if err != nil {
return true, channeldb.FailureReasonError
}
// processChannelUpdateAndRetry is a closure that // processChannelUpdateAndRetry is a closure that
// handles a failure message containing a channel // handles a failure message containing a channel
@ -1919,8 +1955,8 @@ func (r *ChannelRouter) processSendError(paySession PaymentSession,
// that sent us this error, as it doesn't now what the // that sent us this error, as it doesn't now what the
// correct block height is. // correct block height is.
case *lnwire.FailExpiryTooSoon: case *lnwire.FailExpiryTooSoon:
r.applyChannelUpdate(&onionErr.Update, errSource) r.applyChannelUpdate(&onionErr.Update, failureSource)
paySession.ReportVertexFailure(errVertex) paySession.ReportVertexFailure(failureVertex)
return false, 0 return false, 0
// If we hit an instance of onion payload corruption or an invalid // If we hit an instance of onion payload corruption or an invalid
@ -1941,7 +1977,7 @@ func (r *ChannelRouter) processSendError(paySession PaymentSession,
// routing. // routing.
case *lnwire.FailAmountBelowMinimum: case *lnwire.FailAmountBelowMinimum:
processChannelUpdateAndRetry( processChannelUpdateAndRetry(
&onionErr.Update, errSource, &onionErr.Update, failureSource,
) )
return false, 0 return false, 0
@ -1950,7 +1986,7 @@ func (r *ChannelRouter) processSendError(paySession PaymentSession,
// newly updated fees. // newly updated fees.
case *lnwire.FailFeeInsufficient: case *lnwire.FailFeeInsufficient:
processChannelUpdateAndRetry( processChannelUpdateAndRetry(
&onionErr.Update, errSource, &onionErr.Update, failureSource,
) )
return false, 0 return false, 0
@ -1959,7 +1995,7 @@ func (r *ChannelRouter) processSendError(paySession PaymentSession,
// apply the new delta value and try it once more. // apply the new delta value and try it once more.
case *lnwire.FailIncorrectCltvExpiry: case *lnwire.FailIncorrectCltvExpiry:
processChannelUpdateAndRetry( processChannelUpdateAndRetry(
&onionErr.Update, errSource, &onionErr.Update, failureSource,
) )
return false, 0 return false, 0
@ -1967,7 +2003,7 @@ func (r *ChannelRouter) processSendError(paySession PaymentSession,
// forward one is currently disabled, so we'll apply // forward one is currently disabled, so we'll apply
// the update and continue. // the update and continue.
case *lnwire.FailChannelDisabled: case *lnwire.FailChannelDisabled:
r.applyChannelUpdate(&onionErr.Update, errSource) r.applyChannelUpdate(&onionErr.Update, failureSource)
paySession.ReportEdgeFailure(failedEdge, 0) paySession.ReportEdgeFailure(failedEdge, 0)
return false, 0 return false, 0
@ -1975,7 +2011,7 @@ func (r *ChannelRouter) processSendError(paySession PaymentSession,
// sufficient capacity, so we'll prune this edge for // sufficient capacity, so we'll prune this edge for
// now, and continue onwards with our path finding. // now, and continue onwards with our path finding.
case *lnwire.FailTemporaryChannelFailure: case *lnwire.FailTemporaryChannelFailure:
r.applyChannelUpdate(onionErr.Update, errSource) r.applyChannelUpdate(onionErr.Update, failureSource)
paySession.ReportEdgeFailure(failedEdge, failedAmt) paySession.ReportEdgeFailure(failedEdge, failedAmt)
return false, 0 return false, 0
@ -1983,14 +2019,14 @@ func (r *ChannelRouter) processSendError(paySession PaymentSession,
// required features, then we'll note this error and // required features, then we'll note this error and
// continue. // continue.
case *lnwire.FailRequiredNodeFeatureMissing: case *lnwire.FailRequiredNodeFeatureMissing:
paySession.ReportVertexFailure(errVertex) paySession.ReportVertexFailure(failureVertex)
return false, 0 return false, 0
// If the send fail due to a node not having the // If the send fail due to a node not having the
// required features, then we'll note this error and // required features, then we'll note this error and
// continue. // continue.
case *lnwire.FailRequiredChannelFeatureMissing: case *lnwire.FailRequiredChannelFeatureMissing:
paySession.ReportVertexFailure(errVertex) paySession.ReportVertexFailure(failureVertex)
return false, 0 return false, 0
// If the next hop in the route wasn't known or // If the next hop in the route wasn't known or
@ -2008,11 +2044,11 @@ func (r *ChannelRouter) processSendError(paySession PaymentSession,
// reason, then we'll note this and continue with the // reason, then we'll note this and continue with the
// routes. // routes.
case *lnwire.FailTemporaryNodeFailure: case *lnwire.FailTemporaryNodeFailure:
paySession.ReportVertexFailure(errVertex) paySession.ReportVertexFailure(failureVertex)
return false, 0 return false, 0
case *lnwire.FailPermanentNodeFailure: case *lnwire.FailPermanentNodeFailure:
paySession.ReportVertexFailure(errVertex) paySession.ReportVertexFailure(failureVertex)
return false, 0 return false, 0
// If we crafted a route that contains a too long time // If we crafted a route that contains a too long time
@ -2025,7 +2061,7 @@ func (r *ChannelRouter) processSendError(paySession PaymentSession,
// that as a hint during future path finding through // that as a hint during future path finding through
// that node. // that node.
case *lnwire.FailExpiryTooFar: case *lnwire.FailExpiryTooFar:
paySession.ReportVertexFailure(errVertex) paySession.ReportVertexFailure(failureVertex)
return false, 0 return false, 0
// If we get a permanent channel or node failure, then // If we get a permanent channel or node failure, then
@ -2040,51 +2076,51 @@ func (r *ChannelRouter) processSendError(paySession PaymentSession,
}, 0) }, 0)
return false, 0 return false, 0
// Any other failure or an empty failure will get the node pruned.
default: default:
return true, channeldb.FailureReasonError paySession.ReportVertexFailure(failureVertex)
return false, 0
} }
} }
// getFailedEdge tries to locate the failing channel given a route and the // 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 // pubkey of the node that sent the failure. It will assume that the failure is
// associated with the outgoing channel of the error node. As a second result, // associated with the outgoing channel of the failing node. As a second result,
// it returns the amount sent over the edge. // it returns the amount sent over the edge.
func getFailedEdge(route *route.Route, errSource route.Vertex) (edge, func getFailedEdge(route *route.Route, failureSource int) (edge,
lnwire.MilliSatoshi, error) { lnwire.MilliSatoshi) {
hopCount := len(route.Hops) // Determine if we have a failure from the final hop. If it is, we
fromNode := route.SourcePubKey // assume that the failing channel is the incoming channel. In this
amt := route.TotalAmount // function the outgoing channel of the hop indicated by failureSource
for i, hop := range route.Hops { // is returned, where index zero is the self node. By decrementing
toNode := hop.PubKeyBytes // failureSource by one, the outgoing channel of the penultimate hop is
// returned, which is the same as the incoming channel of the final
// Determine if we have a failure from the final hop. // node.
// //
// TODO(joostjager): In this case, certain types of errors are // TODO(joostjager): In this case, certain types of failures are not
// not expected. For example FailUnknownNextPeer. This could be // expected. For example FailUnknownNextPeer. This could be a reason to
// a reason to prune the node? // prune the node?
finalHopFailing := i == hopCount-1 && errSource == toNode if failureSource == len(route.Hops) {
failureSource--
// 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
} }
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 // applyChannelUpdate validates a channel update and if valid, applies it to the

@ -265,8 +265,6 @@ func TestSendPaymentRouteFailureFallback(t *testing.T) {
var preImage [32]byte var preImage [32]byte
copy(preImage[:], bytes.Repeat([]byte{9}, 32)) copy(preImage[:], bytes.Repeat([]byte{9}, 32))
sourceNode := ctx.router.selfNode
// We'll modify the SendToSwitch method that's been set within the // 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 // router's configuration to ignore the path that has luo ji as the
// first hop. This should force the router to instead take 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) roasbeefLuoji := lnwire.NewShortChanIDFromInt(689530843)
if firstHop == roasbeefLuoji { if firstHop == roasbeefLuoji {
pub, err := sourceNode.PubKey()
if err != nil {
return preImage, err
}
return [32]byte{}, &htlcswitch.ForwardingError{ return [32]byte{}, &htlcswitch.ForwardingError{
ErrorSource: pub, FailureSourceIdx: 0,
// TODO(roasbeef): temp node failure should be? // TODO(roasbeef): temp node failure should be?
FailureMessage: &lnwire.FailTemporaryChannelFailure{}, FailureMessage: &lnwire.FailTemporaryChannelFailure{},
} }
@ -342,7 +336,7 @@ func TestChannelUpdateValidation(t *testing.T) {
}, 2), }, 2),
} }
testGraph, err := createTestGraphFromChannels(testChannels) testGraph, err := createTestGraphFromChannels(testChannels, "a")
defer testGraph.cleanUp() defer testGraph.cleanUp()
if err != nil { if err != nil {
t.Fatalf("unable to create graph: %v", err) t.Fatalf("unable to create graph: %v", err)
@ -410,17 +404,8 @@ func TestChannelUpdateValidation(t *testing.T) {
// The unsigned channel update is attached to the failure message. // The unsigned channel update is attached to the failure message.
ctx.router.cfg.Payer.(*mockPaymentAttemptDispatcher).setPaymentResult( ctx.router.cfg.Payer.(*mockPaymentAttemptDispatcher).setPaymentResult(
func(firstHop lnwire.ShortChannelID) ([32]byte, error) { 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{ return [32]byte{}, &htlcswitch.ForwardingError{
ErrorSource: source, FailureSourceIdx: 1,
FailureMessage: &lnwire.FailFeeInsufficient{ FailureMessage: &lnwire.FailFeeInsufficient{
Update: errChanUpdate, Update: errChanUpdate,
}, },
@ -533,9 +518,6 @@ func TestSendPaymentErrorRepeatedFeeInsufficient(t *testing.T) {
FeeRate: uint32(edgeUpdateToFail.FeeProportionalMillionths), 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 // 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 // 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. // 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) roasbeefSongoku := lnwire.NewShortChanIDFromInt(chanID)
if firstHop == roasbeefSongoku { if firstHop == roasbeefSongoku {
sourceKey, err := btcec.ParsePubKey(
sourceNode[:], btcec.S256(),
)
if err != nil {
t.Fatal(err)
}
return [32]byte{}, &htlcswitch.ForwardingError{ return [32]byte{}, &htlcswitch.ForwardingError{
ErrorSource: sourceKey, FailureSourceIdx: 1,
// Within our error, we'll add a channel update // Within our error, we'll add a channel update
// which is meant to reflect he new fee // which is meant to reflect he new fee
@ -647,9 +622,6 @@ func TestSendPaymentErrorNonFinalTimeLockErrors(t *testing.T) {
FeeRate: uint32(edgeUpdateToFail.FeeProportionalMillionths), 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 // 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 // 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 // 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) { func(firstHop lnwire.ShortChannelID) ([32]byte, error) {
if firstHop == roasbeefSongoku { if firstHop == roasbeefSongoku {
sourceKey, err := btcec.ParsePubKey(
sourceNode[:], btcec.S256(),
)
if err != nil {
t.Fatal(err)
}
return [32]byte{}, &htlcswitch.ForwardingError{ return [32]byte{}, &htlcswitch.ForwardingError{
ErrorSource: sourceKey, FailureSourceIdx: 1,
FailureMessage: &lnwire.FailExpiryTooSoon{ FailureMessage: &lnwire.FailExpiryTooSoon{
Update: errChanUpdate, Update: errChanUpdate,
}, },
@ -719,15 +684,8 @@ func TestSendPaymentErrorNonFinalTimeLockErrors(t *testing.T) {
func(firstHop lnwire.ShortChannelID) ([32]byte, error) { func(firstHop lnwire.ShortChannelID) ([32]byte, error) {
if firstHop == roasbeefSongoku { if firstHop == roasbeefSongoku {
sourceKey, err := btcec.ParsePubKey(
sourceNode[:], btcec.S256(),
)
if err != nil {
t.Fatal(err)
}
return [32]byte{}, &htlcswitch.ForwardingError{ return [32]byte{}, &htlcswitch.ForwardingError{
ErrorSource: sourceKey, FailureSourceIdx: 1,
FailureMessage: &lnwire.FailIncorrectCltvExpiry{ FailureMessage: &lnwire.FailIncorrectCltvExpiry{
Update: errChanUpdate, Update: errChanUpdate,
}, },
@ -776,16 +734,6 @@ func TestSendPaymentErrorPathPruning(t *testing.T) {
var preImage [32]byte var preImage [32]byte
copy(preImage[:], bytes.Repeat([]byte{9}, 32)) 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) roasbeefLuoji := lnwire.NewShortChanIDFromInt(689530843)
// First, we'll modify the SendToSwitch method to return an error // First, we'll modify the SendToSwitch method to return an error
@ -802,8 +750,8 @@ func TestSendPaymentErrorPathPruning(t *testing.T) {
// outgoing link to simulate the channel from luo ji to // outgoing link to simulate the channel from luo ji to
// roasbeef not having enough capacity. // roasbeef not having enough capacity.
return [32]byte{}, &htlcswitch.ForwardingError{ return [32]byte{}, &htlcswitch.ForwardingError{
ErrorSource: sourcePub, FailureSourceIdx: 0,
FailureMessage: &lnwire.FailTemporaryChannelFailure{}, FailureMessage: &lnwire.FailTemporaryChannelFailure{},
} }
} }
@ -812,17 +760,9 @@ func TestSendPaymentErrorPathPruning(t *testing.T) {
// prune out the rest of the routes. // prune out the rest of the routes.
roasbeefSatoshi := lnwire.NewShortChanIDFromInt(2340213491) roasbeefSatoshi := lnwire.NewShortChanIDFromInt(2340213491)
if firstHop == roasbeefSatoshi { 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{ return [32]byte{}, &htlcswitch.ForwardingError{
ErrorSource: key, FailureSourceIdx: 1,
FailureMessage: &lnwire.FailUnknownNextPeer{}, FailureMessage: &lnwire.FailUnknownNextPeer{},
} }
} }
@ -854,8 +794,8 @@ func TestSendPaymentErrorPathPruning(t *testing.T) {
if firstHop == roasbeefLuoji { if firstHop == roasbeefLuoji {
return [32]byte{}, &htlcswitch.ForwardingError{ return [32]byte{}, &htlcswitch.ForwardingError{
ErrorSource: sourcePub, FailureSourceIdx: 0,
FailureMessage: &lnwire.FailUnknownNextPeer{}, FailureMessage: &lnwire.FailUnknownNextPeer{},
} }
} }
@ -900,8 +840,8 @@ func TestSendPaymentErrorPathPruning(t *testing.T) {
// outgoing link to simulate the channel from luo ji to // outgoing link to simulate the channel from luo ji to
// roasbeef not having enough capacity. // roasbeef not having enough capacity.
return [32]byte{}, &htlcswitch.ForwardingError{ return [32]byte{}, &htlcswitch.ForwardingError{
ErrorSource: sourcePub, FailureSourceIdx: 0,
FailureMessage: &lnwire.FailTemporaryChannelFailure{}, FailureMessage: &lnwire.FailTemporaryChannelFailure{},
} }
} }
return preImage, nil return preImage, nil
@ -1039,7 +979,9 @@ func TestIgnoreChannelEdgePolicyForUnknownChannel(t *testing.T) {
// Setup an initially empty network. // Setup an initially empty network.
testChannels := []*testChannel{} testChannels := []*testChannel{}
testGraph, err := createTestGraphFromChannels(testChannels) testGraph, err := createTestGraphFromChannels(
testChannels, "roasbeef",
)
if err != nil { if err != nil {
t.Fatalf("unable to create graph: %v", err) t.Fatalf("unable to create graph: %v", err)
} }
@ -2027,7 +1969,7 @@ func TestPruneChannelGraphStaleEdges(t *testing.T) {
// We'll create our test graph and router backed with these test // We'll create our test graph and router backed with these test
// channels we've created. // channels we've created.
testGraph, err := createTestGraphFromChannels(testChannels) testGraph, err := createTestGraphFromChannels(testChannels, "a")
if err != nil { if err != nil {
t.Fatalf("unable to create test graph: %v", err) t.Fatalf("unable to create test graph: %v", err)
} }
@ -2064,6 +2006,14 @@ func TestPruneChannelGraphDoubleDisabled(t *testing.T) {
// according to that heuristic. // according to that heuristic.
timestamp := time.Now() timestamp := time.Now()
testChannels := []*testChannel{ testChannels := []*testChannel{
// Channel from self shouldn't be pruned.
symmetricTestChannel(
"self", "a", 100000, &testChannelPolicy{
LastUpdate: timestamp,
Disabled: true,
}, 99,
),
// No edges. // No edges.
{ {
Node1: &testChannelEnd{Alias: "a"}, Node1: &testChannelEnd{Alias: "a"},
@ -2135,7 +2085,7 @@ func TestPruneChannelGraphDoubleDisabled(t *testing.T) {
// We'll create our test graph and router backed with these test // We'll create our test graph and router backed with these test
// channels we've created. // channels we've created.
testGraph, err := createTestGraphFromChannels(testChannels) testGraph, err := createTestGraphFromChannels(testChannels, "self")
if err != nil { if err != nil {
t.Fatalf("unable to create test graph: %v", err) t.Fatalf("unable to create test graph: %v", err)
} }
@ -2475,6 +2425,120 @@ 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)
}
// 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 // assertChannelsPruned ensures that only the given channels are pruned from the
// graph out of the set of all channels. // graph out of the set of all channels.
func assertChannelsPruned(t *testing.T, graph *channeldb.ChannelGraph, func assertChannelsPruned(t *testing.T, graph *channeldb.ChannelGraph,
@ -2543,7 +2607,7 @@ func TestRouterPaymentStateMachine(t *testing.T) {
}, 2), }, 2),
} }
testGraph, err := createTestGraphFromChannels(testChannels) testGraph, err := createTestGraphFromChannels(testChannels, "a")
if err != nil { if err != nil {
t.Fatalf("unable to create graph: %v", err) t.Fatalf("unable to create graph: %v", err)
} }
@ -2938,11 +3002,6 @@ func TestRouterPaymentStateMachine(t *testing.T) {
PaymentHash: payHash, 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)) copy(preImage[:], bytes.Repeat([]byte{9}, 32))
router.cfg.MissionControl = &mockPaymentSessionSource{ router.cfg.MissionControl = &mockPaymentSessionSource{
@ -3020,8 +3079,8 @@ func TestRouterPaymentStateMachine(t *testing.T) {
case sendToSwitchResultFailure: case sendToSwitchResultFailure:
select { select {
case sendResult <- &htlcswitch.ForwardingError{ case sendResult <- &htlcswitch.ForwardingError{
ErrorSource: errSource, FailureSourceIdx: 1,
FailureMessage: &lnwire.FailTemporaryChannelFailure{}, FailureMessage: &lnwire.FailTemporaryChannelFailure{},
}: }:
case <-time.After(1 * time.Second): case <-time.After(1 * time.Second):
t.Fatalf("unable to send result") t.Fatalf("unable to send result")
@ -3046,8 +3105,8 @@ func TestRouterPaymentStateMachine(t *testing.T) {
select { select {
case getPaymentResult <- &htlcswitch.PaymentResult{ case getPaymentResult <- &htlcswitch.PaymentResult{
Error: &htlcswitch.ForwardingError{ Error: &htlcswitch.ForwardingError{
ErrorSource: errSource, FailureSourceIdx: 1,
FailureMessage: &lnwire.FailTemporaryChannelFailure{}, FailureMessage: &lnwire.FailTemporaryChannelFailure{},
}, },
}: }:
case <-time.After(1 * time.Second): case <-time.After(1 * time.Second):
@ -3164,7 +3223,7 @@ func TestSendToRouteStructuredError(t *testing.T) {
}, 2), }, 2),
} }
testGraph, err := createTestGraphFromChannels(testChannels) testGraph, err := createTestGraphFromChannels(testChannels, "a")
if err != nil { if err != nil {
t.Fatalf("unable to create graph: %v", err) t.Fatalf("unable to create graph: %v", err)
} }
@ -3214,17 +3273,8 @@ func TestSendToRouteStructuredError(t *testing.T) {
// The unsigned channel update is attached to the failure message. // The unsigned channel update is attached to the failure message.
ctx.router.cfg.Payer.(*mockPaymentAttemptDispatcher).setPaymentResult( ctx.router.cfg.Payer.(*mockPaymentAttemptDispatcher).setPaymentResult(
func(firstHop lnwire.ShortChannelID) ([32]byte, error) { 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{ return [32]byte{}, &htlcswitch.ForwardingError{
ErrorSource: source, FailureSourceIdx: 1,
FailureMessage: &lnwire.FailFeeInsufficient{ FailureMessage: &lnwire.FailFeeInsufficient{
Update: lnwire.ChannelUpdate{}, Update: lnwire.ChannelUpdate{},
}, },

@ -409,8 +409,7 @@ func newServer(listenAddrs []net.Addr, chanDB *channeldb.DB,
} }
s.htlcSwitch, err = htlcswitch.New(htlcswitch.Config{ s.htlcSwitch, err = htlcswitch.New(htlcswitch.Config{
DB: chanDB, DB: chanDB,
SelfKey: s.identityPriv.PubKey(),
LocalChannelClose: func(pubKey []byte, LocalChannelClose: func(pubKey []byte,
request *htlcswitch.ChanClose) { request *htlcswitch.ChanClose) {