htlcswitch+routing+routerrpc: return error source as index

This commit is contained in:
Joost Jager 2019-06-11 10:24:19 +02:00
parent 55bef96cf3
commit 2b47632b26
No known key found for this signature in database
GPG Key ID: A61B9D4C393C59C7
11 changed files with 245 additions and 310 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.
@ -256,20 +257,20 @@ type SphinxErrorDecrypter struct {
// 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) 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 nil, err
} }
return &ForwardingError{ return &ForwardingError{
ErrorSource: source, FailureSourceIdx: failure.SenderIdx,
FailureMessage: failureMsg, FailureMessage: failureMsg,
}, nil }, nil
} }

@ -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
} }

@ -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"
@ -112,11 +111,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 +770,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 +784,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 +802,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 +817,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,
} }
} }
@ -967,9 +961,9 @@ func (s *Switch) parseFailedPayment(deobfuscator ErrorDecrypter,
failureMsg = lnwire.NewTemporaryChannelFailure(nil) failureMsg = lnwire.NewTemporaryChannelFailure(nil)
} }
failure = &ForwardingError{ failure = &ForwardingError{
ErrorSource: s.cfg.SelfKey, FailureSourceIdx: 0,
ExtraMsg: userErr, ExtraMsg: userErr,
FailureMessage: failureMsg, 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
@ -981,9 +975,9 @@ func (s *Switch) parseFailedPayment(deobfuscator ErrorDecrypter,
"on-chain, then cancelled back (hash=%v, pid=%d)", "on-chain, then cancelled back (hash=%v, pid=%d)",
paymentHash, paymentID) paymentHash, paymentID)
failure = &ForwardingError{ failure = &ForwardingError{
ErrorSource: s.cfg.SelfKey, FailureSourceIdx: 0,
ExtraMsg: userErr, ExtraMsg: userErr,
FailureMessage: lnwire.FailPermanentChannelFailure{}, 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
@ -999,9 +993,9 @@ func (s *Switch) parseFailedPayment(deobfuscator ErrorDecrypter,
paymentHash, paymentID, err) paymentHash, paymentID, err)
log.Error(userErr) log.Error(userErr)
failure = &ForwardingError{ failure = &ForwardingError{
ErrorSource: s.cfg.SelfKey, FailureSourceIdx: 0,
ExtraMsg: userErr, ExtraMsg: userErr,
FailureMessage: lnwire.NewTemporaryChannelFailure(nil), FailureMessage: lnwire.NewTemporaryChannelFailure(nil),
} }
} }
} }

@ -610,10 +610,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 +619,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 +661,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 +696,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 +1167,110 @@ 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 // 1635 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, 0x5f, 0x73, 0x22, 0xc7,
0x15, 0x5e, 0x04, 0x08, 0x71, 0xf8, 0x1b, 0xb5, 0x64, 0x79, 0x8c, 0x2c, 0xaf, 0x76, 0x92, 0x78, 0x11, 0x37, 0x02, 0x09, 0xd1, 0xfc, 0xd1, 0x6a, 0xa4, 0xd3, 0xed, 0xa1, 0xd3, 0x59, 0x5e, 0x27,
0x55, 0xae, 0x8d, 0x94, 0x90, 0xf2, 0xd6, 0x5e, 0x25, 0x85, 0xa1, 0x59, 0x26, 0x86, 0x19, 0x6d, 0x67, 0xd5, 0x95, 0x23, 0x39, 0xa4, 0xce, 0xe5, 0xa7, 0xa4, 0x38, 0x18, 0xcc, 0xfa, 0x60, 0x57,
0x03, 0xde, 0x75, 0x72, 0xd1, 0xd5, 0x82, 0x16, 0x4c, 0x09, 0x66, 0xd8, 0x99, 0x66, 0x63, 0xe5, 0x1e, 0xe0, 0xec, 0x4b, 0x1e, 0xa6, 0x46, 0xec, 0x08, 0xb6, 0x04, 0xbb, 0x78, 0x77, 0x70, 0x4e,
0x22, 0x77, 0xc9, 0x5d, 0x9e, 0x25, 0x79, 0x82, 0xbc, 0x47, 0x5e, 0x25, 0x57, 0xa9, 0xee, 0x1e, 0x79, 0xc8, 0x5b, 0x5e, 0x52, 0x95, 0xcf, 0x92, 0x7c, 0x82, 0x7c, 0x8f, 0x7c, 0x95, 0x3c, 0xa5,
0x60, 0x40, 0xc8, 0xeb, 0x2b, 0x31, 0xdf, 0xf9, 0xfa, 0x9c, 0xd3, 0xe7, 0xaf, 0x8f, 0xe0, 0x24, 0x66, 0x66, 0x81, 0x05, 0xa1, 0x8b, 0x9f, 0xc4, 0xfe, 0xfa, 0xd7, 0x7f, 0xa6, 0x7b, 0xba, 0xa7,
0x0c, 0x16, 0x82, 0x87, 0xe1, 0x7c, 0x78, 0xa5, 0x7f, 0x5d, 0xce, 0xc3, 0x40, 0x04, 0x28, 0xbf, 0x05, 0x27, 0x51, 0x38, 0x17, 0x3c, 0x8a, 0x66, 0xc3, 0x2b, 0xfd, 0xeb, 0x72, 0x16, 0x85, 0x22,
0xc2, 0xab, 0xf9, 0x70, 0x3e, 0xd4, 0xa8, 0xf5, 0xbf, 0x3d, 0x40, 0x3d, 0xee, 0x8f, 0xae, 0xd9, 0x44, 0x85, 0x25, 0x5e, 0x2d, 0x44, 0xb3, 0xa1, 0x46, 0xad, 0xff, 0xee, 0x00, 0xea, 0xf1, 0xc0,
0xfd, 0x8c, 0xfb, 0x82, 0xf0, 0x1f, 0x17, 0x3c, 0x12, 0x08, 0x41, 0x66, 0xc4, 0x23, 0x61, 0xa6, 0xbb, 0x66, 0xf7, 0x53, 0x1e, 0x08, 0xc2, 0x7f, 0x9a, 0xf3, 0x58, 0x20, 0x04, 0x39, 0x8f, 0xc7,
0xce, 0x53, 0x17, 0x45, 0xa2, 0x7e, 0x23, 0x03, 0xd2, 0x6c, 0x26, 0xcc, 0xbd, 0xf3, 0xd4, 0x45, 0xc2, 0xcc, 0x9c, 0x67, 0x2e, 0x4a, 0x44, 0xfd, 0x46, 0x06, 0x64, 0xd9, 0x54, 0x98, 0x3b, 0xe7,
0x9a, 0xc8, 0x9f, 0xe8, 0x0b, 0x28, 0xce, 0xf5, 0x39, 0x3a, 0x61, 0xd1, 0xc4, 0x4c, 0x2b, 0x76, 0x99, 0x8b, 0x2c, 0x91, 0x3f, 0xd1, 0x67, 0x50, 0x9a, 0x69, 0x3d, 0x3a, 0x66, 0xf1, 0xd8, 0xcc,
0x21, 0xc6, 0xda, 0x2c, 0x9a, 0xa0, 0x0b, 0x30, 0x6e, 0x3d, 0x9f, 0x4d, 0xe9, 0x70, 0x2a, 0x7e, 0x2a, 0x76, 0x31, 0xc1, 0xda, 0x2c, 0x1e, 0xa3, 0x0b, 0x30, 0x6e, 0xfd, 0x80, 0x4d, 0xe8, 0x70,
0xa2, 0x23, 0x3e, 0x15, 0xcc, 0xcc, 0x9c, 0xa7, 0x2e, 0xb2, 0xa4, 0xac, 0xf0, 0xc6, 0x54, 0xfc, 0x22, 0x7e, 0xa6, 0x1e, 0x9f, 0x08, 0x66, 0xe6, 0xce, 0x33, 0x17, 0xbb, 0xa4, 0xa2, 0xf0, 0xc6,
0xd4, 0x94, 0x28, 0xfa, 0x12, 0x2a, 0x4b, 0x65, 0xa1, 0xf6, 0xc2, 0xcc, 0x9e, 0xa7, 0x2e, 0xf2, 0x44, 0xfc, 0xdc, 0x94, 0x28, 0xfa, 0x02, 0x0e, 0x16, 0xc6, 0x22, 0x1d, 0x85, 0xb9, 0x7b, 0x9e,
0xa4, 0x3c, 0xdf, 0xf4, 0xed, 0x4b, 0xa8, 0x08, 0x6f, 0xc6, 0x83, 0x85, 0xa0, 0x11, 0x1f, 0x06, 0xb9, 0x28, 0x90, 0xca, 0x6c, 0x3d, 0xb6, 0x2f, 0xe0, 0x40, 0xf8, 0x53, 0x1e, 0xce, 0x05, 0x8d,
0xfe, 0x28, 0x32, 0xf7, 0xb5, 0xc6, 0x18, 0xee, 0x69, 0x14, 0x59, 0x50, 0xba, 0xe5, 0x9c, 0x4e, 0xf9, 0x30, 0x0c, 0xbc, 0xd8, 0xdc, 0xd3, 0x16, 0x13, 0xb8, 0xa7, 0x51, 0x64, 0x41, 0xf9, 0x96,
0xbd, 0x99, 0x27, 0x68, 0xc4, 0x84, 0x99, 0x53, 0xae, 0x17, 0x6e, 0x39, 0xef, 0x48, 0xac, 0xc7, 0x73, 0x3a, 0xf1, 0xa7, 0xbe, 0xa0, 0x31, 0x13, 0x66, 0x5e, 0x85, 0x5e, 0xbc, 0xe5, 0xbc, 0x23,
0x84, 0xf4, 0x2f, 0x58, 0x88, 0x71, 0xe0, 0xf9, 0x63, 0x3a, 0x9c, 0x30, 0x9f, 0x7a, 0x23, 0xf3, 0xb1, 0x1e, 0x13, 0x32, 0xbe, 0x70, 0x2e, 0x46, 0xa1, 0x1f, 0x8c, 0xe8, 0x70, 0xcc, 0x02, 0xea,
0xe0, 0x3c, 0x75, 0x91, 0x21, 0xe5, 0x25, 0xde, 0x98, 0x30, 0xdf, 0x1e, 0xa1, 0x33, 0x00, 0x75, 0x7b, 0xe6, 0xfe, 0x79, 0xe6, 0x22, 0x47, 0x2a, 0x0b, 0xbc, 0x31, 0x66, 0x81, 0xed, 0xa1, 0x33,
0x07, 0xa5, 0xce, 0xcc, 0x2b, 0x8b, 0x79, 0x89, 0x28, 0x5d, 0xa8, 0x06, 0x05, 0x15, 0x60, 0x3a, 0x00, 0x75, 0x06, 0x65, 0xce, 0x2c, 0x28, 0x8f, 0x05, 0x89, 0x28, 0x5b, 0xa8, 0x06, 0x45, 0x95,
0xf1, 0x7c, 0x11, 0x99, 0x70, 0x9e, 0xbe, 0x28, 0xd4, 0x8c, 0xcb, 0xa9, 0x2f, 0x63, 0x4d, 0xa4, 0x60, 0x3a, 0xf6, 0x03, 0x11, 0x9b, 0x70, 0x9e, 0xbd, 0x28, 0xd6, 0x8c, 0xcb, 0x49, 0x20, 0x73,
0xa4, 0xed, 0xf9, 0x82, 0x24, 0x49, 0xd6, 0x37, 0x70, 0xd4, 0x0f, 0xd9, 0xf0, 0x6e, 0x2b, 0xf8, 0x4d, 0xa4, 0xa4, 0xed, 0x07, 0x82, 0xa4, 0x49, 0xd6, 0x37, 0x70, 0xd4, 0x8f, 0xd8, 0xf0, 0x6e,
0xdb, 0x61, 0x4d, 0x3d, 0x08, 0xab, 0xf5, 0x37, 0x28, 0xc5, 0x87, 0x7a, 0x82, 0x89, 0x45, 0x84, 0x23, 0xf9, 0x9b, 0x69, 0xcd, 0x3c, 0x48, 0xab, 0xf5, 0x57, 0x28, 0x27, 0x4a, 0x3d, 0xc1, 0xc4,
0x7e, 0x0d, 0xd9, 0x48, 0x30, 0xc1, 0x15, 0xb9, 0x5c, 0x7b, 0x7a, 0xb9, 0xca, 0xf6, 0x65, 0x82, 0x3c, 0x46, 0xbf, 0x81, 0xdd, 0x58, 0x30, 0xc1, 0x15, 0xb9, 0x52, 0x7b, 0x7a, 0xb9, 0xac, 0xf6,
0xc8, 0x89, 0x66, 0xa1, 0x2a, 0x1c, 0xcc, 0x43, 0xee, 0xcd, 0xd8, 0x98, 0xab, 0x84, 0x16, 0xc9, 0x65, 0x8a, 0xc8, 0x89, 0x66, 0xa1, 0x2a, 0xec, 0xcf, 0x22, 0xee, 0x4f, 0xd9, 0x88, 0xab, 0x82,
0xea, 0x1b, 0x59, 0x90, 0x55, 0x87, 0x55, 0x3a, 0x0b, 0xb5, 0x62, 0xf2, 0x0e, 0x44, 0x8b, 0xac, 0x96, 0xc8, 0xf2, 0x1b, 0x59, 0xb0, 0xab, 0x94, 0x55, 0x39, 0x8b, 0xb5, 0x52, 0xfa, 0x0c, 0x44,
0xdf, 0x43, 0x45, 0x7d, 0xb7, 0x38, 0xff, 0x58, 0xc9, 0x3c, 0x85, 0x1c, 0x9b, 0xe9, 0xd8, 0xeb, 0x8b, 0xac, 0xdf, 0xc3, 0x81, 0xfa, 0x6e, 0x71, 0xfe, 0xb1, 0x2b, 0xf3, 0x14, 0xf2, 0x6c, 0xaa,
0xb2, 0xd9, 0x67, 0x33, 0x19, 0x76, 0x6b, 0x04, 0xc6, 0xfa, 0x7c, 0x34, 0x0f, 0xfc, 0x88, 0xcb, 0x73, 0xaf, 0xaf, 0xcd, 0x1e, 0x9b, 0xca, 0xb4, 0x5b, 0x1e, 0x18, 0x2b, 0xfd, 0x78, 0x16, 0x06,
0x54, 0x48, 0xe5, 0x32, 0x13, 0x32, 0x6d, 0x33, 0x79, 0x2a, 0xa5, 0x4e, 0x95, 0x63, 0xbc, 0xc5, 0x31, 0x97, 0xa5, 0x90, 0xc6, 0x65, 0x25, 0x64, 0xd9, 0xa6, 0x52, 0x2b, 0xa3, 0xb4, 0x2a, 0x09,
0x79, 0x37, 0x62, 0x02, 0xbd, 0xd4, 0x15, 0x40, 0xa7, 0xc1, 0xf0, 0x4e, 0xd6, 0x14, 0xbb, 0x8f, 0xde, 0xe2, 0xbc, 0x1b, 0x33, 0x81, 0x5e, 0xea, 0x1b, 0x40, 0x27, 0xe1, 0xf0, 0x4e, 0xde, 0x29,
0xd5, 0x97, 0x24, 0xdc, 0x09, 0x86, 0x77, 0x4d, 0x09, 0x5a, 0x7f, 0xd6, 0xb5, 0xdd, 0x0f, 0xb4, 0x76, 0x9f, 0x98, 0x2f, 0x4b, 0xb8, 0x13, 0x0e, 0xef, 0x9a, 0x12, 0xb4, 0xfe, 0xa4, 0xef, 0x76,
0xef, 0x9f, 0x1c, 0xde, 0x75, 0x08, 0xf6, 0x1e, 0x0f, 0x01, 0x85, 0xa3, 0x0d, 0xe5, 0xf1, 0x2d, 0x3f, 0xd4, 0xb1, 0xff, 0xe2, 0xf4, 0xae, 0x52, 0xb0, 0xf3, 0x78, 0x0a, 0x28, 0x1c, 0xad, 0x19,
0x92, 0x91, 0x4d, 0x6d, 0x45, 0xf6, 0x2b, 0xc8, 0xdd, 0x32, 0x6f, 0xba, 0x08, 0x97, 0x8a, 0x51, 0x4f, 0x4e, 0x91, 0xce, 0x6c, 0x66, 0x23, 0xb3, 0x5f, 0x42, 0xfe, 0x96, 0xf9, 0x93, 0x79, 0xb4,
0x22, 0x4d, 0x2d, 0x2d, 0x21, 0x4b, 0x8a, 0xf5, 0x8f, 0x1c, 0xe4, 0x62, 0x10, 0xd5, 0x20, 0x33, 0x30, 0x8c, 0x52, 0x65, 0x6a, 0x69, 0x09, 0x59, 0x50, 0xac, 0xbf, 0xe7, 0x21, 0x9f, 0x80, 0xa8,
0x0c, 0x46, 0xcb, 0xec, 0xbe, 0x78, 0x78, 0x6c, 0xf9, 0xb7, 0x11, 0x8c, 0x38, 0x51, 0x5c, 0x54, 0x06, 0xb9, 0x61, 0xe8, 0x2d, 0xaa, 0xfb, 0xe2, 0xa1, 0xda, 0xe2, 0x6f, 0x23, 0xf4, 0x38, 0x51,
0x83, 0x27, 0xb1, 0x2a, 0x1a, 0x05, 0x8b, 0x70, 0xc8, 0xe9, 0x7c, 0x71, 0x73, 0xc7, 0xef, 0xe3, 0x5c, 0xf4, 0x07, 0xa8, 0xc8, 0x1b, 0x1d, 0xf0, 0x09, 0x9d, 0xcf, 0x3c, 0xb6, 0x2c, 0xa8, 0x99,
0x84, 0x1f, 0xc5, 0xc2, 0x9e, 0x92, 0x5d, 0x2b, 0x11, 0xfa, 0x03, 0x94, 0x65, 0x17, 0xf8, 0x7c, 0xd2, 0x6e, 0x68, 0xc2, 0x40, 0xc9, 0x49, 0x79, 0x98, 0xfe, 0x44, 0xa7, 0x50, 0x18, 0x8b, 0xc9,
0x4a, 0x17, 0xf3, 0x11, 0x5b, 0x15, 0x81, 0x99, 0xb0, 0xd8, 0xd0, 0x84, 0x81, 0x92, 0x93, 0xd2, 0x50, 0x57, 0x22, 0xa7, 0x9a, 0x62, 0x5f, 0x02, 0xaa, 0x06, 0x16, 0x94, 0xc3, 0xc0, 0x0f, 0x03,
0x30, 0xf9, 0x89, 0x4e, 0x21, 0x3f, 0x11, 0xd3, 0xa1, 0xce, 0x5e, 0x46, 0x35, 0xd2, 0x81, 0x04, 0x1a, 0x8f, 0x19, 0xad, 0xbd, 0xfe, 0x5a, 0x35, 0x6b, 0x89, 0x14, 0x15, 0xd8, 0x1b, 0xb3, 0xda,
0x54, 0xde, 0x2c, 0x28, 0x05, 0xbe, 0x17, 0xf8, 0x34, 0x9a, 0x30, 0x5a, 0x7b, 0xfd, 0xb5, 0x6a, 0xeb, 0xaf, 0xd1, 0xa7, 0x50, 0x54, 0x2d, 0xc3, 0x3f, 0xcc, 0xfc, 0xe8, 0x5e, 0x75, 0x69, 0x99,
0xf0, 0x22, 0x29, 0x28, 0xb0, 0x37, 0x61, 0xb5, 0xd7, 0x5f, 0xa3, 0xcf, 0xa1, 0xa0, 0xda, 0x8c, 0xa8, 0x2e, 0xc2, 0x0a, 0x41, 0xc7, 0xb0, 0x7b, 0x3b, 0x61, 0xa3, 0x58, 0x75, 0x66, 0x99, 0xe8,
0x7f, 0x98, 0x7b, 0xe1, 0xbd, 0xea, 0xec, 0x12, 0x51, 0x9d, 0x87, 0x15, 0x82, 0x8e, 0x21, 0x7b, 0x0f, 0xf4, 0x15, 0x1c, 0x27, 0x39, 0xa0, 0x71, 0x38, 0x8f, 0x86, 0x9c, 0xfa, 0x81, 0xc7, 0x3f,
0x3b, 0x65, 0xe3, 0x48, 0x75, 0x73, 0x89, 0xe8, 0x0f, 0xeb, 0xbf, 0x19, 0x28, 0x24, 0x42, 0x80, 0xa8, 0xbe, 0x2c, 0x13, 0x94, 0xc8, 0x7a, 0x4a, 0x64, 0x4b, 0x89, 0xf5, 0x9f, 0x1c, 0x14, 0x53,
0x8a, 0x70, 0x40, 0x70, 0x0f, 0x93, 0x77, 0xb8, 0x69, 0x7c, 0x86, 0x4c, 0x38, 0x1e, 0x38, 0x6f, 0x09, 0x40, 0x25, 0xd8, 0x27, 0xb8, 0x87, 0xc9, 0x3b, 0xdc, 0x34, 0x3e, 0x41, 0x26, 0x1c, 0x0f,
0x1d, 0xf7, 0x7b, 0x87, 0x5e, 0xd7, 0xdf, 0x77, 0xb1, 0xd3, 0xa7, 0xed, 0x7a, 0xaf, 0x6d, 0xa4, 0x9c, 0xb7, 0x8e, 0xfb, 0x83, 0x43, 0xaf, 0xeb, 0xef, 0xbb, 0xd8, 0xe9, 0xd3, 0x76, 0xbd, 0xd7,
0xd0, 0x73, 0x30, 0x6d, 0xa7, 0xe1, 0x12, 0x82, 0x1b, 0xfd, 0x95, 0xac, 0xde, 0x75, 0x07, 0x4e, 0x36, 0x32, 0xe8, 0x39, 0x98, 0xb6, 0xd3, 0x70, 0x09, 0xc1, 0x8d, 0xfe, 0x52, 0x56, 0xef, 0xba,
0xdf, 0xd8, 0x43, 0x9f, 0xc3, 0x69, 0xcb, 0x76, 0xea, 0x1d, 0xba, 0xe6, 0x34, 0x3a, 0xfd, 0x77, 0x03, 0xa7, 0x6f, 0xec, 0xa0, 0x4f, 0xe1, 0xb4, 0x65, 0x3b, 0xf5, 0x0e, 0x5d, 0x71, 0x1a, 0x9d,
0x14, 0xff, 0x70, 0x6d, 0x93, 0xf7, 0x46, 0x7a, 0x17, 0xa1, 0xdd, 0xef, 0x34, 0x96, 0x1a, 0x32, 0xfe, 0x3b, 0x8a, 0x7f, 0xbc, 0xb6, 0xc9, 0x7b, 0x23, 0xbb, 0x8d, 0xd0, 0xee, 0x77, 0x1a, 0x0b,
0xe8, 0x19, 0x3c, 0xd1, 0x04, 0x7d, 0x84, 0xf6, 0x5d, 0x97, 0xf6, 0x5c, 0xd7, 0x31, 0xb2, 0xe8, 0x0b, 0x39, 0xf4, 0x0c, 0x9e, 0x68, 0x82, 0x56, 0xa1, 0x7d, 0xd7, 0xa5, 0x3d, 0xd7, 0x75, 0x8c,
0x10, 0x4a, 0xb6, 0xf3, 0xae, 0xde, 0xb1, 0x9b, 0x94, 0xe0, 0x7a, 0xa7, 0x6b, 0xec, 0xa3, 0x23, 0x5d, 0x74, 0x08, 0x65, 0xdb, 0x79, 0x57, 0xef, 0xd8, 0x4d, 0x4a, 0x70, 0xbd, 0xd3, 0x35, 0xf6,
0xa8, 0x6c, 0xf3, 0x72, 0x52, 0xc5, 0x92, 0xe7, 0x3a, 0xb6, 0xeb, 0xd0, 0x77, 0x98, 0xf4, 0x6c, 0xd0, 0x11, 0x1c, 0x6c, 0xf2, 0xf2, 0xd2, 0xc4, 0x82, 0xe7, 0x3a, 0xb6, 0xeb, 0xd0, 0x77, 0x98,
0xd7, 0x31, 0x0e, 0xd0, 0x09, 0xa0, 0x4d, 0x51, 0xbb, 0x5b, 0x6f, 0x18, 0x79, 0xf4, 0x04, 0x0e, 0xf4, 0x6c, 0xd7, 0x31, 0xf6, 0xd1, 0x09, 0xa0, 0x75, 0x51, 0xbb, 0x5b, 0x6f, 0x18, 0x05, 0xf4,
0x37, 0xf1, 0xb7, 0xf8, 0xbd, 0x01, 0x32, 0x0c, 0xda, 0x31, 0xfa, 0x06, 0x77, 0xdc, 0xef, 0x69, 0x04, 0x0e, 0xd7, 0xf1, 0xb7, 0xf8, 0xbd, 0x01, 0x32, 0x0d, 0x3a, 0x30, 0xfa, 0x06, 0x77, 0xdc,
0xd7, 0x76, 0xec, 0xee, 0xa0, 0x6b, 0x14, 0xd0, 0x31, 0x18, 0x2d, 0x8c, 0xa9, 0xed, 0xf4, 0x06, 0x1f, 0x68, 0xd7, 0x76, 0xec, 0xee, 0xa0, 0x6b, 0x14, 0xd1, 0x31, 0x18, 0x2d, 0x8c, 0xa9, 0xed,
0xad, 0x96, 0xdd, 0xb0, 0xb1, 0xd3, 0x37, 0x8a, 0xda, 0xf2, 0xae, 0x8b, 0x97, 0xe4, 0x81, 0x46, 0xf4, 0x06, 0xad, 0x96, 0xdd, 0xb0, 0xb1, 0xd3, 0x37, 0x4a, 0xda, 0xf3, 0xb6, 0x83, 0x97, 0xa5,
0xbb, 0xee, 0x38, 0xb8, 0x43, 0x9b, 0x76, 0xaf, 0xfe, 0xa6, 0x83, 0x9b, 0x46, 0x19, 0x9d, 0xc1, 0x42, 0xa3, 0x5d, 0x77, 0x1c, 0xdc, 0xa1, 0x4d, 0xbb, 0x57, 0x7f, 0xd3, 0xc1, 0x4d, 0xa3, 0x82,
0xb3, 0x3e, 0xee, 0x5e, 0xbb, 0xa4, 0x4e, 0xde, 0xd3, 0xa5, 0xbc, 0x55, 0xb7, 0x3b, 0x03, 0x82, 0xce, 0xe0, 0x59, 0x1f, 0x77, 0xaf, 0x5d, 0x52, 0x27, 0xef, 0xe9, 0x42, 0xde, 0xaa, 0xdb, 0x9d,
0x8d, 0x0a, 0xfa, 0x02, 0xce, 0x08, 0xfe, 0x6e, 0x60, 0x13, 0xdc, 0xa4, 0x8e, 0xdb, 0xc4, 0xb4, 0x01, 0xc1, 0xc6, 0x01, 0xfa, 0x0c, 0xce, 0x08, 0xfe, 0x7e, 0x60, 0x13, 0xdc, 0xa4, 0x8e, 0xdb,
0x85, 0xeb, 0xfd, 0x01, 0xc1, 0xb4, 0x6b, 0xf7, 0x7a, 0xb6, 0xf3, 0xad, 0x61, 0xa0, 0x5f, 0xc2, 0xc4, 0xb4, 0x85, 0xeb, 0xfd, 0x01, 0xc1, 0xb4, 0x6b, 0xf7, 0x7a, 0xb6, 0xf3, 0xad, 0x61, 0xa0,
0xf9, 0x8a, 0xb2, 0x52, 0xb0, 0xc5, 0x3a, 0x94, 0xf7, 0x5b, 0xe6, 0xd3, 0xc1, 0x3f, 0xf4, 0xe9, 0x5f, 0xc1, 0xf9, 0x92, 0xb2, 0x34, 0xb0, 0xc1, 0x3a, 0x94, 0xe7, 0x5b, 0xd4, 0xd3, 0xc1, 0x3f,
0x35, 0xc6, 0xc4, 0x40, 0xa8, 0x0a, 0x27, 0x6b, 0xf3, 0xda, 0x40, 0x6c, 0xfb, 0x48, 0xca, 0xae, 0xf6, 0xe9, 0x35, 0xc6, 0xc4, 0x40, 0xa8, 0x0a, 0x27, 0x2b, 0xf7, 0xda, 0x41, 0xe2, 0xfb, 0x48,
0x31, 0xe9, 0xd6, 0x1d, 0x99, 0xe0, 0x0d, 0xd9, 0xb1, 0x74, 0x7b, 0x2d, 0xdb, 0x76, 0xfb, 0x89, 0xca, 0xae, 0x31, 0xe9, 0xd6, 0x1d, 0x59, 0xe0, 0x35, 0xd9, 0xb1, 0x0c, 0x7b, 0x25, 0xdb, 0x0c,
0xf5, 0xaf, 0x34, 0x94, 0x36, 0x8a, 0x1e, 0x3d, 0x87, 0x7c, 0xe4, 0x8d, 0x7d, 0x26, 0x64, 0x2b, 0xfb, 0xc9, 0x77, 0xb9, 0xfd, 0x1d, 0x23, 0x6b, 0xfd, 0x33, 0x0b, 0xe5, 0xb5, 0x66, 0x41, 0xcf,
0xeb, 0x2e, 0x5f, 0x03, 0xea, 0xa5, 0x98, 0x30, 0xcf, 0xd7, 0xe3, 0x45, 0x77, 0x5b, 0x5e, 0x21, 0xa1, 0x10, 0xfb, 0xa3, 0x80, 0x09, 0xd9, 0xce, 0xba, 0xd3, 0x57, 0x80, 0x7a, 0x2d, 0xc6, 0xcc,
0x6a, 0xb8, 0x3c, 0x85, 0xdc, 0xf2, 0xa5, 0x49, 0xab, 0x06, 0xd9, 0x1f, 0xea, 0x17, 0xe6, 0x39, 0x0f, 0xf4, 0x88, 0xd1, 0x23, 0xb6, 0xa0, 0x10, 0x35, 0x60, 0x9e, 0x42, 0x7e, 0xf1, 0xda, 0x64,
0xe4, 0xe5, 0xfc, 0x8a, 0x04, 0x9b, 0xcd, 0x55, 0xef, 0x94, 0xc8, 0x1a, 0x40, 0xbf, 0x80, 0xd2, 0x55, 0x63, 0xed, 0x0d, 0xf5, 0x2b, 0xf3, 0x1c, 0x0a, 0x72, 0x86, 0xc5, 0x82, 0x4d, 0x67, 0xaa,
0x8c, 0x47, 0x11, 0x1b, 0x73, 0xaa, 0xeb, 0x1f, 0x14, 0xa3, 0x18, 0x83, 0x2d, 0x89, 0x49, 0xd2, 0xe7, 0xca, 0x64, 0x05, 0xa0, 0xcf, 0xa1, 0x3c, 0xe5, 0x71, 0xcc, 0x46, 0x9c, 0xea, 0xbe, 0x01,
0xb2, 0x7f, 0x35, 0x29, 0xab, 0x49, 0x31, 0xa8, 0x49, 0xdb, 0xe3, 0x53, 0xb0, 0xb8, 0xcd, 0x92, 0xc5, 0x28, 0x25, 0x60, 0x4b, 0xb5, 0xcf, 0xe7, 0xb0, 0xe8, 0xe3, 0x84, 0xb4, 0xab, 0x49, 0x09,
0xe3, 0x53, 0x30, 0xf4, 0x0a, 0x0e, 0x75, 0x2f, 0x7b, 0xbe, 0x37, 0x5b, 0xcc, 0x74, 0x4f, 0xe7, 0xa8, 0x49, 0x9b, 0x23, 0x54, 0xb0, 0xa4, 0x3d, 0xd3, 0x23, 0x54, 0x30, 0xf4, 0x0a, 0x0e, 0xf5,
0x94, 0xcb, 0x15, 0xd5, 0xd3, 0x1a, 0x57, 0xad, 0xfd, 0x0c, 0x0e, 0x6e, 0x58, 0xc4, 0xe5, 0xe4, 0x0c, 0xf0, 0x03, 0x7f, 0x3a, 0x9f, 0xea, 0x59, 0x90, 0x57, 0x21, 0x1f, 0xa8, 0x59, 0xa0, 0x71,
0x56, 0xef, 0x67, 0x89, 0xe4, 0xe4, 0x77, 0x8b, 0x73, 0x29, 0x92, 0xf3, 0x3c, 0x94, 0xd3, 0x24, 0x35, 0x12, 0x9e, 0xc1, 0xfe, 0x0d, 0x8b, 0xb9, 0x9c, 0xde, 0x49, 0xaf, 0xe6, 0xe5, 0x77, 0x8b,
0xaf, 0x45, 0xb7, 0x9c, 0x13, 0x19, 0xc7, 0x95, 0x05, 0xf6, 0x61, 0x6d, 0xa1, 0x90, 0xb0, 0xa0, 0x73, 0x29, 0x92, 0x33, 0x3d, 0x92, 0x53, 0xa8, 0xa0, 0x45, 0xb7, 0x9c, 0x13, 0x99, 0xc7, 0xa5,
0x71, 0x65, 0xe1, 0x15, 0x1c, 0xf2, 0x0f, 0x22, 0x64, 0x34, 0x98, 0xb3, 0x1f, 0x17, 0x9c, 0x8e, 0x07, 0xf6, 0x61, 0xe5, 0xa1, 0x98, 0xf2, 0xa0, 0x71, 0xe5, 0xe1, 0x15, 0x1c, 0xf2, 0x0f, 0x22,
0x98, 0x60, 0x66, 0x51, 0x05, 0xb7, 0xa2, 0x04, 0xae, 0xc2, 0x9b, 0x4c, 0x30, 0xeb, 0x39, 0x54, 0x62, 0x34, 0x9c, 0xb1, 0x9f, 0xe6, 0x9c, 0x7a, 0x4c, 0x30, 0xb3, 0xa4, 0x92, 0x7b, 0xa0, 0x04,
0x09, 0x8f, 0xb8, 0xe8, 0x7a, 0x51, 0xe4, 0x05, 0x7e, 0x23, 0xf0, 0x45, 0x18, 0x4c, 0xe3, 0x07, 0xae, 0xc2, 0x9b, 0x4c, 0x30, 0xeb, 0x39, 0x54, 0x09, 0x8f, 0xb9, 0xe8, 0xfa, 0x71, 0xec, 0x87,
0xc0, 0x3a, 0x83, 0xd3, 0x9d, 0x52, 0x3d, 0xc1, 0xe5, 0xe1, 0xef, 0x16, 0x3c, 0xbc, 0xdf, 0x7d, 0x41, 0x23, 0x0c, 0x44, 0x14, 0x4e, 0x92, 0x47, 0xc0, 0x3a, 0x83, 0xd3, 0xad, 0x52, 0x3d, 0xc5,
0xf8, 0x2d, 0x9c, 0xee, 0x94, 0xc6, 0xe3, 0xff, 0x2b, 0xc8, 0xfa, 0xc1, 0x88, 0x47, 0x66, 0x4a, 0xa5, 0xf2, 0xf7, 0x73, 0x1e, 0xdd, 0x6f, 0x57, 0x7e, 0x0b, 0xa7, 0x5b, 0xa5, 0xc9, 0x13, 0xf0,
0x2d, 0x00, 0x27, 0x89, 0xb9, 0xe9, 0x04, 0x23, 0xde, 0xf6, 0x22, 0x11, 0x84, 0xf7, 0x44, 0x93, 0x25, 0xec, 0x06, 0xa1, 0xc7, 0x63, 0x33, 0xa3, 0x96, 0x80, 0x93, 0xd4, 0xbc, 0x75, 0x42, 0x8f,
0xac, 0xff, 0xa4, 0xa0, 0x90, 0x80, 0xd1, 0x09, 0xec, 0xc7, 0x33, 0x5a, 0x17, 0x55, 0xfc, 0x85, 0xb7, 0xfd, 0x58, 0x84, 0xd1, 0x3d, 0xd1, 0x24, 0xeb, 0xdf, 0x19, 0x28, 0xa6, 0x60, 0x74, 0x02,
0x5e, 0x42, 0x79, 0xca, 0x22, 0x41, 0xe5, 0xc8, 0xa6, 0x32, 0x49, 0xf1, 0x7b, 0xb7, 0x85, 0xa2, 0x7b, 0xb3, 0xf9, 0xcd, 0x1d, 0xbf, 0x4f, 0x2e, 0x55, 0xf2, 0x85, 0x5e, 0x42, 0x65, 0xc2, 0x62,
0x6f, 0xe0, 0x69, 0x20, 0x26, 0x3c, 0xd4, 0xab, 0x4c, 0xb4, 0x18, 0x0e, 0x79, 0x14, 0xd1, 0x79, 0x41, 0xe5, 0xf8, 0xa3, 0xb2, 0x48, 0xc9, 0x9b, 0xb7, 0x81, 0xa2, 0x6f, 0xe0, 0x69, 0x28, 0xc6,
0x18, 0xdc, 0xa8, 0x52, 0xdb, 0x23, 0x8f, 0x89, 0xd1, 0x6b, 0x38, 0x88, 0x6b, 0x24, 0x32, 0x33, 0x3c, 0xd2, 0xeb, 0x4c, 0x3c, 0x1f, 0x0e, 0x79, 0x1c, 0xd3, 0x59, 0x14, 0xde, 0xa8, 0xab, 0xb6,
0xca, 0xf5, 0x67, 0x0f, 0x47, 0xfe, 0xd2, 0xfb, 0x15, 0xd5, 0xfa, 0x77, 0x0a, 0xca, 0x9b, 0x42, 0x43, 0x1e, 0x13, 0xa3, 0xd7, 0xb0, 0x9f, 0xdc, 0x91, 0xd8, 0xcc, 0xa9, 0xd0, 0x9f, 0x3d, 0x7c,
0xf4, 0x42, 0x55, 0xbf, 0x2a, 0x41, 0x6f, 0xa4, 0xee, 0x91, 0x21, 0x09, 0xe4, 0x93, 0xef, 0x52, 0x2a, 0x16, 0xd1, 0x2f, 0xa9, 0xd6, 0xbf, 0x32, 0x50, 0x59, 0x17, 0xa2, 0x17, 0xea, 0xf6, 0xab,
0x83, 0xe3, 0x99, 0xe7, 0xd3, 0x39, 0xf7, 0xd9, 0xd4, 0xfb, 0x2b, 0xa7, 0xcb, 0x45, 0x22, 0xad, 0x2b, 0xe8, 0x7b, 0xea, 0x1c, 0x39, 0x92, 0x42, 0x7e, 0xf1, 0x59, 0x6a, 0x70, 0x3c, 0xf5, 0x03,
0xd8, 0x3b, 0x65, 0xc8, 0x82, 0xe2, 0xc6, 0xa5, 0x33, 0xea, 0xd2, 0x1b, 0xd8, 0xab, 0x7f, 0xa6, 0x3a, 0xe3, 0x01, 0x9b, 0xf8, 0x7f, 0xe1, 0x74, 0xb1, 0x4c, 0x64, 0x15, 0x7b, 0xab, 0x0c, 0x59,
0xa0, 0x98, 0x5c, 0x89, 0x50, 0x09, 0xf2, 0xb6, 0x43, 0x5b, 0x1d, 0xfb, 0xdb, 0x76, 0xdf, 0xf8, 0x50, 0x5a, 0x3b, 0x74, 0x4e, 0x1d, 0x7a, 0x0d, 0x7b, 0xf5, 0x8f, 0x0c, 0x94, 0xd2, 0x6b, 0x11,
0x4c, 0x7e, 0xf6, 0x06, 0x8d, 0x06, 0xc6, 0x4d, 0xdc, 0x34, 0x52, 0x08, 0x41, 0x59, 0x4e, 0x02, 0x2a, 0x43, 0xc1, 0x76, 0x68, 0xab, 0x63, 0x7f, 0xdb, 0xee, 0x1b, 0x9f, 0xc8, 0xcf, 0xde, 0xa0,
0xdc, 0xa4, 0x7d, 0xbb, 0x8b, 0xdd, 0x81, 0x7c, 0x16, 0x8e, 0xa0, 0x12, 0x63, 0x8e, 0x4b, 0x89, 0xd1, 0xc0, 0xb8, 0x89, 0x9b, 0x46, 0x06, 0x21, 0xa8, 0xc8, 0x79, 0x80, 0x9b, 0xb4, 0x6f, 0x77,
0x3b, 0xe8, 0x63, 0x23, 0x8d, 0x0c, 0x28, 0xc6, 0x20, 0x26, 0xc4, 0x25, 0x46, 0x46, 0xce, 0xb2, 0xb1, 0x3b, 0x90, 0x8f, 0xc3, 0x11, 0x1c, 0x24, 0x98, 0xe3, 0x52, 0xe2, 0x0e, 0xfa, 0xd8, 0xc8,
0x18, 0x79, 0xf8, 0xc4, 0x34, 0x71, 0xbf, 0x6e, 0x77, 0x7a, 0x46, 0xb6, 0xf6, 0xf7, 0x0c, 0xec, 0x22, 0x03, 0x4a, 0x09, 0x88, 0x09, 0x71, 0x89, 0x91, 0x93, 0x13, 0x2d, 0x41, 0x1e, 0x3e, 0x34,
0xab, 0x15, 0x22, 0x44, 0x6d, 0x28, 0x24, 0x76, 0x71, 0x74, 0x96, 0xc8, 0xc0, 0xc3, 0x1d, 0xbd, 0x4d, 0xdc, 0xaf, 0xdb, 0x9d, 0x9e, 0xb1, 0x5b, 0xfb, 0x5b, 0x0e, 0xf6, 0xd4, 0x1a, 0x11, 0xa1,
0x6a, 0xee, 0xde, 0xf1, 0x16, 0xd1, 0x6f, 0x52, 0xe8, 0x8f, 0x50, 0x4c, 0x6e, 0x96, 0x28, 0xb9, 0x36, 0x14, 0x53, 0xfb, 0x38, 0x3a, 0x4b, 0x55, 0xe0, 0xe1, 0x9e, 0x5e, 0x35, 0xb7, 0xef, 0x79,
0x31, 0xec, 0x58, 0x39, 0x3f, 0xaa, 0xeb, 0x2d, 0x18, 0x38, 0x12, 0xde, 0x4c, 0xbe, 0xf6, 0xf1, 0xf3, 0xf8, 0xab, 0x0c, 0xfa, 0x0e, 0x4a, 0xe9, 0xed, 0x12, 0xa5, 0xb7, 0x86, 0x2d, 0x6b, 0xe7,
0xce, 0x86, 0xaa, 0x09, 0xfe, 0xd6, 0x22, 0x58, 0x3d, 0xdd, 0x29, 0x8b, 0xfb, 0xa3, 0xa3, 0xaf, 0x47, 0x6d, 0xbd, 0x05, 0x03, 0xc7, 0xc2, 0x9f, 0xca, 0x2d, 0x21, 0xd9, 0xdb, 0x50, 0x35, 0xc5,
0x18, 0x6f, 0x4d, 0x0f, 0xae, 0xb8, 0xb9, 0xaa, 0x55, 0x5f, 0x3c, 0x26, 0x8e, 0xb5, 0x8d, 0xe0, 0xdf, 0x58, 0x06, 0xab, 0xa7, 0x5b, 0x65, 0x49, 0x7f, 0x74, 0xf4, 0x11, 0x93, 0xcd, 0xe9, 0xc1,
0x68, 0x47, 0x27, 0xa3, 0x5f, 0x25, 0x3d, 0x78, 0x74, 0x0e, 0x54, 0x5f, 0xfe, 0x1c, 0x6d, 0x6d, 0x11, 0xd7, 0xd7, 0xb5, 0xea, 0x8b, 0xc7, 0xc4, 0x89, 0x35, 0x0f, 0x8e, 0xb6, 0x74, 0x32, 0xfa,
0x65, 0x47, 0xcb, 0x6f, 0x58, 0x79, 0x7c, 0x60, 0x6c, 0x58, 0xf9, 0xc8, 0xe4, 0x78, 0xf3, 0xdb, 0x75, 0x3a, 0x82, 0x47, 0xe7, 0x40, 0xf5, 0xe5, 0xff, 0xa3, 0xad, 0xbc, 0x6c, 0x69, 0xf9, 0x35,
0x3f, 0x5d, 0x8d, 0x3d, 0x31, 0x59, 0xdc, 0x5c, 0x0e, 0x83, 0xd9, 0xd5, 0xd4, 0x1b, 0x4f, 0x84, 0x2f, 0x8f, 0x0f, 0x8c, 0x35, 0x2f, 0x1f, 0x99, 0x1c, 0x6f, 0x7e, 0xfb, 0xc7, 0xab, 0x91, 0x2f,
0xef, 0xf9, 0x63, 0x9f, 0x8b, 0xbf, 0x04, 0xe1, 0xdd, 0xd5, 0xd4, 0x1f, 0x5d, 0xa9, 0x2d, 0xf4, 0xc6, 0xf3, 0x9b, 0xcb, 0x61, 0x38, 0xbd, 0x9a, 0xf8, 0xa3, 0xb1, 0x08, 0xfc, 0x60, 0x14, 0x70,
0x6a, 0xa5, 0xee, 0x66, 0x5f, 0xfd, 0x0f, 0xf7, 0xbb, 0xff, 0x07, 0x00, 0x00, 0xff, 0xff, 0xb7, 0xf1, 0xe7, 0x30, 0xba, 0xbb, 0x9a, 0x04, 0xde, 0x95, 0xda, 0x44, 0xaf, 0x96, 0xe6, 0x6e, 0xf6,
0x0c, 0x44, 0x60, 0xf3, 0x0d, 0x00, 0x00, 0xd4, 0xff, 0x71, 0xbf, 0xfb, 0x5f, 0x00, 0x00, 0x00, 0xff, 0xff, 0x2f, 0xaa, 0x9f, 0x4e, 0xf7,
0x0d, 0x00, 0x00,
} }
// Reference imports to suppress errors if they are not otherwise used. // Reference imports to suppress errors if they are not otherwise used.

@ -199,11 +199,7 @@ message Failure {
/// 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 +215,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;
} }

@ -399,7 +399,7 @@ func marshallError(sendError error) (*Failure, error) {
return nil, errors.New("unknown wire error") return nil, errors.New("unknown wire error")
} }
response.FailureSourcePubkey = fErr.ErrorSource.SerializeCompressed() response.FailureSourceIndex = uint32(fErr.FailureSourceIdx)
return response, nil return response, nil
} }

@ -1815,19 +1815,39 @@ func (r *ChannelRouter) processSendError(paySession PaymentSession,
rt *route.Route, fErr *htlcswitch.ForwardingError) ( rt *route.Route, fErr *htlcswitch.ForwardingError) (
bool, channeldb.FailureReason) { bool, channeldb.FailureReason) {
errSource := fErr.ErrorSource var (
errVertex := route.NewVertex(errSource) failureSourceIdx = fErr.FailureSourceIdx
log.Tracef("node=%x reported failure when sending htlc", errVertex) failureVertex route.Vertex
failureSource *btcec.PublicKey
err error
)
// For any non-self failure, look up the source pub key in the hops
// slice. Otherwise return the self node pubkey.
if failureSourceIdx > 0 {
failureVertex = rt.Hops[failureSourceIdx-1].PubKeyBytes
failureSource, err = btcec.ParsePubKey(failureVertex[:], btcec.S256())
if err != nil {
log.Errorf("Cannot parse pubkey %v: %v",
failureVertex, err)
return true, channeldb.FailureReasonError
}
} else {
failureVertex = r.selfNode.PubKeyBytes
failureSource, err = r.selfNode.PubKey()
if err != nil {
log.Errorf("Cannot parse self pubkey: %v", err)
return true, channeldb.FailureReasonError
}
}
log.Tracef("Node %x (index %v) reported failure when sending htlc",
failureVertex, failureSourceIdx)
// Always determine chan id ourselves, because a channel // 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 +1939,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 +1961,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 +1970,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 +1979,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 +1987,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 +1995,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 +2003,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 +2028,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 +2045,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
@ -2046,45 +2066,43 @@ func (r *ChannelRouter) processSendError(paySession PaymentSession,
} }
// 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{},
} }
@ -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,7 +750,7 @@ 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,16 +760,8 @@ 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,7 +794,7 @@ 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,7 +840,7 @@ 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{},
} }
} }
@ -2948,11 +2888,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{
@ -3030,7 +2965,7 @@ 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):
@ -3056,7 +2991,7 @@ 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{},
}, },
}: }:
@ -3224,17 +3159,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) {