htlcswitch: use distinct error for unreadable failures
This commit is contained in:
parent
0cdae56d28
commit
418fe364de
@ -965,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
|
||||||
|
}
|
||||||
|
@ -58,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
|
||||||
@ -936,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 {
|
||||||
|
|
||||||
@ -960,7 +962,8 @@ func (s *Switch) parseFailedPayment(deobfuscator ErrorDecrypter,
|
|||||||
// router.
|
// router.
|
||||||
failureMsg = lnwire.NewTemporaryChannelFailure(nil)
|
failureMsg = lnwire.NewTemporaryChannelFailure(nil)
|
||||||
}
|
}
|
||||||
failure = &ForwardingError{
|
|
||||||
|
return &ForwardingError{
|
||||||
FailureSourceIdx: 0,
|
FailureSourceIdx: 0,
|
||||||
ExtraMsg: userErr,
|
ExtraMsg: userErr,
|
||||||
FailureMessage: failureMsg,
|
FailureMessage: failureMsg,
|
||||||
@ -974,7 +977,8 @@ 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{
|
|
||||||
|
return &ForwardingError{
|
||||||
FailureSourceIdx: 0,
|
FailureSourceIdx: 0,
|
||||||
ExtraMsg: userErr,
|
ExtraMsg: userErr,
|
||||||
FailureMessage: lnwire.FailPermanentChannelFailure{},
|
FailureMessage: lnwire.FailPermanentChannelFailure{},
|
||||||
@ -983,24 +987,19 @@ func (s *Switch) parseFailedPayment(deobfuscator ErrorDecrypter,
|
|||||||
// 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{
|
|
||||||
FailureSourceIdx: 0,
|
|
||||||
ExtraMsg: userErr,
|
|
||||||
FailureMessage: lnwire.NewTemporaryChannelFailure(nil),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return failure
|
return ErrUnreadableFailureMessage
|
||||||
|
}
|
||||||
|
|
||||||
|
return failure
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// handlePacketForward is used in cases when we need forward the htlc update
|
// handlePacketForward is used in cases when we need forward the htlc update
|
||||||
|
@ -2241,3 +2241,107 @@ 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")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -103,31 +103,36 @@ 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
|
||||||
|
//*
|
||||||
|
//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",
|
||||||
|
999: "UNREADABLE_FAILURE",
|
||||||
}
|
}
|
||||||
|
|
||||||
var Failure_FailureCode_value = map[string]int32{
|
var Failure_FailureCode_value = map[string]int32{
|
||||||
@ -153,6 +158,7 @@ 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,
|
||||||
|
"UNREADABLE_FAILURE": 999,
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x Failure_FailureCode) String() string {
|
func (x Failure_FailureCode) String() string {
|
||||||
@ -1167,110 +1173,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{
|
||||||
// 1635 bytes of a gzipped FileDescriptorProto
|
// 1648 bytes of a gzipped FileDescriptorProto
|
||||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x57, 0x5f, 0x73, 0x22, 0xc7,
|
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x57, 0x4f, 0x73, 0x22, 0xc7,
|
||||||
0x11, 0x37, 0x02, 0x09, 0xd1, 0xfc, 0xd1, 0x6a, 0xa4, 0xd3, 0xed, 0xa1, 0xd3, 0x59, 0x5e, 0x27,
|
0x15, 0x37, 0x02, 0x84, 0x78, 0xfc, 0xd1, 0xa8, 0xa5, 0x95, 0x58, 0xb4, 0x5a, 0xcb, 0xe3, 0x64,
|
||||||
0x67, 0xd5, 0x95, 0x23, 0x39, 0xa4, 0xce, 0xe5, 0xa7, 0xa4, 0x38, 0x18, 0xcc, 0xfa, 0x60, 0x57,
|
0xad, 0xda, 0x72, 0x24, 0x87, 0xd4, 0xba, 0x7c, 0x4a, 0x8a, 0x85, 0xc6, 0x8c, 0x17, 0x66, 0xe4,
|
||||||
0x1e, 0xe0, 0xec, 0x4b, 0x1e, 0xa6, 0x46, 0xec, 0x08, 0xb6, 0x04, 0xbb, 0x78, 0x77, 0x70, 0x4e,
|
0x06, 0xd6, 0xde, 0xe4, 0xd0, 0xd5, 0x62, 0x5a, 0x30, 0x25, 0x98, 0xc1, 0x33, 0x8d, 0xb3, 0xca,
|
||||||
0x79, 0xc8, 0x5b, 0x5e, 0x52, 0x95, 0xcf, 0x92, 0x7c, 0x82, 0x7c, 0x8f, 0x7c, 0x95, 0x3c, 0xa5,
|
0x21, 0xb7, 0x1c, 0xf3, 0x29, 0xf2, 0x01, 0x92, 0x4f, 0x90, 0xaf, 0x93, 0x7b, 0x8e, 0x39, 0xa5,
|
||||||
0x66, 0x66, 0x81, 0x05, 0xa1, 0x8b, 0x9f, 0xc4, 0xfe, 0xfa, 0xd7, 0x7f, 0xa6, 0x7b, 0xba, 0xa7,
|
0xba, 0x7b, 0x80, 0x01, 0xa1, 0xf5, 0x9e, 0xc4, 0xfc, 0xde, 0xaf, 0xdf, 0xdf, 0x7e, 0xaf, 0x9f,
|
||||||
0x05, 0x27, 0x51, 0x38, 0x17, 0x3c, 0x8a, 0x66, 0xc3, 0x2b, 0xfd, 0xeb, 0x72, 0x16, 0x85, 0x22,
|
0xe0, 0x38, 0x0c, 0xe6, 0x82, 0x87, 0xe1, 0x6c, 0x78, 0xa5, 0x7f, 0x5d, 0xce, 0xc2, 0x40, 0x04,
|
||||||
0x44, 0x85, 0x25, 0x5e, 0x2d, 0x44, 0xb3, 0xa1, 0x46, 0xad, 0xff, 0xee, 0x00, 0xea, 0xf1, 0xc0,
|
0x28, 0xbf, 0xc4, 0xab, 0xf9, 0x70, 0x36, 0xd4, 0xa8, 0xf9, 0xbf, 0x1d, 0x40, 0x3d, 0xee, 0xbb,
|
||||||
0xbb, 0x66, 0xf7, 0x53, 0x1e, 0x08, 0xc2, 0x7f, 0x9a, 0xf3, 0x58, 0x20, 0x04, 0x39, 0x8f, 0xc7,
|
0xd7, 0xec, 0x7e, 0xca, 0x7d, 0x41, 0xf8, 0x4f, 0x73, 0x1e, 0x09, 0x84, 0x20, 0xe3, 0xf2, 0x48,
|
||||||
0xc2, 0xcc, 0x9c, 0x67, 0x2e, 0x4a, 0x44, 0xfd, 0x46, 0x06, 0x64, 0xd9, 0x54, 0x98, 0x3b, 0xe7,
|
0x54, 0x52, 0xe7, 0xa9, 0x8b, 0x22, 0x51, 0xbf, 0x91, 0x01, 0x69, 0x36, 0x15, 0x95, 0x9d, 0xf3,
|
||||||
0x99, 0x8b, 0x2c, 0x91, 0x3f, 0xd1, 0x67, 0x50, 0x9a, 0x69, 0x3d, 0x3a, 0x66, 0xf1, 0xd8, 0xcc,
|
0xd4, 0x45, 0x9a, 0xc8, 0x9f, 0xe8, 0x33, 0x28, 0xce, 0xf4, 0x39, 0x3a, 0x66, 0xd1, 0xb8, 0x92,
|
||||||
0x2a, 0x76, 0x31, 0xc1, 0xda, 0x2c, 0x1e, 0xa3, 0x0b, 0x30, 0x6e, 0xfd, 0x80, 0x4d, 0xe8, 0x70,
|
0x56, 0xec, 0x42, 0x8c, 0xb5, 0x59, 0x34, 0x46, 0x17, 0x60, 0xdc, 0x7a, 0x3e, 0x9b, 0xd0, 0xe1,
|
||||||
0x22, 0x7e, 0xa6, 0x1e, 0x9f, 0x08, 0x66, 0xe6, 0xce, 0x33, 0x17, 0xbb, 0xa4, 0xa2, 0xf0, 0xc6,
|
0x44, 0xfc, 0x4c, 0x5d, 0x3e, 0x11, 0xac, 0x92, 0x39, 0x4f, 0x5d, 0x64, 0x49, 0x59, 0xe1, 0x8d,
|
||||||
0x44, 0xfc, 0xdc, 0x94, 0x28, 0xfa, 0x02, 0x0e, 0x16, 0xc6, 0x22, 0x1d, 0x85, 0xb9, 0x7b, 0x9e,
|
0x89, 0xf8, 0xb9, 0x29, 0x51, 0xf4, 0x05, 0xec, 0x2f, 0x94, 0x85, 0xda, 0x8b, 0x4a, 0xf6, 0x3c,
|
||||||
0xb9, 0x28, 0x90, 0xca, 0x6c, 0x3d, 0xb6, 0x2f, 0xe0, 0x40, 0xf8, 0x53, 0x1e, 0xce, 0x05, 0x8d,
|
0x75, 0x91, 0x27, 0xe5, 0xd9, 0xba, 0x6f, 0x5f, 0xc0, 0xbe, 0xf0, 0xa6, 0x3c, 0x98, 0x0b, 0x1a,
|
||||||
0xf9, 0x30, 0x0c, 0xbc, 0xd8, 0xdc, 0xd3, 0x16, 0x13, 0xb8, 0xa7, 0x51, 0x64, 0x41, 0xf9, 0x96,
|
0xf1, 0x61, 0xe0, 0xbb, 0x51, 0x65, 0x57, 0x6b, 0x8c, 0xe1, 0x9e, 0x46, 0x91, 0x09, 0xa5, 0x5b,
|
||||||
0x73, 0x3a, 0xf1, 0xa7, 0xbe, 0xa0, 0x31, 0x13, 0x66, 0x5e, 0x85, 0x5e, 0xbc, 0xe5, 0xbc, 0x23,
|
0xce, 0xe9, 0xc4, 0x9b, 0x7a, 0x82, 0x46, 0x4c, 0x54, 0x72, 0xca, 0xf5, 0xc2, 0x2d, 0xe7, 0x1d,
|
||||||
0xb1, 0x1e, 0x13, 0x32, 0xbe, 0x70, 0x2e, 0x46, 0xa1, 0x1f, 0x8c, 0xe8, 0x70, 0xcc, 0x02, 0xea,
|
0x89, 0xf5, 0x98, 0x90, 0xfe, 0x05, 0x73, 0x31, 0x0a, 0x3c, 0x7f, 0x44, 0x87, 0x63, 0xe6, 0x53,
|
||||||
0x7b, 0xe6, 0xfe, 0x79, 0xe6, 0x22, 0x47, 0x2a, 0x0b, 0xbc, 0x31, 0x66, 0x81, 0xed, 0xa1, 0x33,
|
0xcf, 0xad, 0xec, 0x9d, 0xa7, 0x2e, 0x32, 0xa4, 0xbc, 0xc0, 0x1b, 0x63, 0xe6, 0x5b, 0x2e, 0x3a,
|
||||||
0x00, 0x75, 0x06, 0x65, 0xce, 0x2c, 0x28, 0x8f, 0x05, 0x89, 0x28, 0x5b, 0xa8, 0x06, 0x45, 0x95,
|
0x03, 0x50, 0x31, 0x28, 0x75, 0x95, 0xbc, 0xb2, 0x98, 0x97, 0x88, 0xd2, 0x85, 0x6a, 0x50, 0x50,
|
||||||
0x60, 0x3a, 0xf6, 0x03, 0x11, 0x9b, 0x70, 0x9e, 0xbd, 0x28, 0xd6, 0x8c, 0xcb, 0x49, 0x20, 0x73,
|
0x09, 0xa6, 0x63, 0xcf, 0x17, 0x51, 0x05, 0xce, 0xd3, 0x17, 0x85, 0x9a, 0x71, 0x39, 0xf1, 0x65,
|
||||||
0x4d, 0xa4, 0xa4, 0xed, 0x07, 0x82, 0xa4, 0x49, 0xd6, 0x37, 0x70, 0xd4, 0x8f, 0xd8, 0xf0, 0x6e,
|
0xae, 0x89, 0x94, 0xb4, 0x3d, 0x5f, 0x90, 0x24, 0xc9, 0xfc, 0x06, 0x0e, 0xfb, 0x21, 0x1b, 0xde,
|
||||||
0x23, 0xf9, 0x9b, 0x69, 0xcd, 0x3c, 0x48, 0xab, 0xf5, 0x57, 0x28, 0x27, 0x4a, 0x3d, 0xc1, 0xc4,
|
0x6d, 0x24, 0x7f, 0x33, 0xad, 0xa9, 0x07, 0x69, 0x35, 0xff, 0x0a, 0xa5, 0xf8, 0x50, 0x4f, 0x30,
|
||||||
0x3c, 0x46, 0xbf, 0x81, 0xdd, 0x58, 0x30, 0xc1, 0x15, 0xb9, 0x52, 0x7b, 0x7a, 0xb9, 0xac, 0xf6,
|
0x31, 0x8f, 0xd0, 0x6f, 0x20, 0x1b, 0x09, 0x26, 0xb8, 0x22, 0x97, 0x6b, 0x27, 0x97, 0xcb, 0x6a,
|
||||||
0x65, 0x8a, 0xc8, 0x89, 0x66, 0xa1, 0x2a, 0xec, 0xcf, 0x22, 0xee, 0x4f, 0xd9, 0x88, 0xab, 0x82,
|
0x5f, 0x26, 0x88, 0x9c, 0x68, 0x16, 0xaa, 0xc2, 0xde, 0x2c, 0xe4, 0xde, 0x94, 0x8d, 0xb8, 0x2a,
|
||||||
0x96, 0xc8, 0xf2, 0x1b, 0x59, 0xb0, 0xab, 0x94, 0x55, 0x39, 0x8b, 0xb5, 0x52, 0xfa, 0x0c, 0x44,
|
0x68, 0x91, 0x2c, 0xbf, 0x91, 0x09, 0x59, 0x75, 0x58, 0x95, 0xb3, 0x50, 0x2b, 0x26, 0x63, 0x20,
|
||||||
0x8b, 0xac, 0xdf, 0xc3, 0x81, 0xfa, 0x6e, 0x71, 0xfe, 0xb1, 0x2b, 0xf3, 0x14, 0xf2, 0x6c, 0xaa,
|
0x5a, 0x64, 0xfe, 0x1e, 0xf6, 0xd5, 0x77, 0x8b, 0xf3, 0x0f, 0x5d, 0x99, 0x13, 0xc8, 0xb1, 0xa9,
|
||||||
0x73, 0xaf, 0xaf, 0xcd, 0x1e, 0x9b, 0xca, 0xb4, 0x5b, 0x1e, 0x18, 0x2b, 0xfd, 0x78, 0x16, 0x06,
|
0xce, 0xbd, 0xbe, 0x36, 0xbb, 0x6c, 0x2a, 0xd3, 0x6e, 0xba, 0x60, 0xac, 0xce, 0x47, 0xb3, 0xc0,
|
||||||
0x31, 0x97, 0xa5, 0x90, 0xc6, 0x65, 0x25, 0x64, 0xd9, 0xa6, 0x52, 0x2b, 0xa3, 0xb4, 0x2a, 0x09,
|
0x8f, 0xb8, 0x2c, 0x85, 0x54, 0x2e, 0x2b, 0x21, 0xcb, 0x36, 0x95, 0xa7, 0x52, 0xea, 0x54, 0x39,
|
||||||
0xde, 0xe2, 0xbc, 0x1b, 0x33, 0x81, 0x5e, 0xea, 0x1b, 0x40, 0x27, 0xe1, 0xf0, 0x4e, 0xde, 0x29,
|
0xc6, 0x5b, 0x9c, 0x77, 0x23, 0x26, 0xd0, 0x0b, 0x7d, 0x03, 0xe8, 0x24, 0x18, 0xde, 0xc9, 0x3b,
|
||||||
0x76, 0x9f, 0x98, 0x2f, 0x4b, 0xb8, 0x13, 0x0e, 0xef, 0x9a, 0x12, 0xb4, 0xfe, 0xa4, 0xef, 0x76,
|
0xc5, 0xee, 0x63, 0xf5, 0x25, 0x09, 0x77, 0x82, 0xe1, 0x5d, 0x53, 0x82, 0xe6, 0x9f, 0xf4, 0xdd,
|
||||||
0x3f, 0xd4, 0xb1, 0xff, 0xe2, 0xf4, 0xae, 0x52, 0xb0, 0xf3, 0x78, 0x0a, 0x28, 0x1c, 0xad, 0x19,
|
0xee, 0x07, 0xda, 0xf7, 0x8f, 0x4e, 0xef, 0x2a, 0x05, 0x3b, 0x8f, 0xa7, 0x80, 0xc2, 0xe1, 0x9a,
|
||||||
0x4f, 0x4e, 0x91, 0xce, 0x6c, 0x66, 0x23, 0xb3, 0x5f, 0x42, 0xfe, 0x96, 0xf9, 0x93, 0x79, 0xb4,
|
0xf2, 0x38, 0x8a, 0x64, 0x66, 0x53, 0x1b, 0x99, 0xfd, 0x12, 0x72, 0xb7, 0xcc, 0x9b, 0xcc, 0xc3,
|
||||||
0x30, 0x8c, 0x52, 0x65, 0x6a, 0x69, 0x09, 0x59, 0x50, 0xac, 0xbf, 0xe7, 0x21, 0x9f, 0x80, 0xa8,
|
0x85, 0x62, 0x94, 0x28, 0x53, 0x4b, 0x4b, 0xc8, 0x82, 0x62, 0xfe, 0x23, 0x07, 0xb9, 0x18, 0x44,
|
||||||
0x06, 0xb9, 0x61, 0xe8, 0x2d, 0xaa, 0xfb, 0xe2, 0xa1, 0xda, 0xe2, 0x6f, 0x23, 0xf4, 0x38, 0x51,
|
0x35, 0xc8, 0x0c, 0x03, 0x77, 0x51, 0xdd, 0xe7, 0x0f, 0x8f, 0x2d, 0xfe, 0x36, 0x02, 0x97, 0x13,
|
||||||
0x5c, 0xf4, 0x07, 0xa8, 0xc8, 0x1b, 0x1d, 0xf0, 0x09, 0x9d, 0xcf, 0x3c, 0xb6, 0x2c, 0xa8, 0x99,
|
0xc5, 0x45, 0x7f, 0x80, 0xb2, 0xbc, 0xd1, 0x3e, 0x9f, 0xd0, 0xf9, 0xcc, 0x65, 0xcb, 0x82, 0x56,
|
||||||
0xd2, 0x6e, 0x68, 0xc2, 0x40, 0xc9, 0x49, 0x79, 0x98, 0xfe, 0x44, 0xa7, 0x50, 0x18, 0x8b, 0xc9,
|
0x12, 0xa7, 0x1b, 0x9a, 0x30, 0x50, 0x72, 0x52, 0x1a, 0x26, 0x3f, 0xd1, 0x29, 0xe4, 0xc7, 0x62,
|
||||||
0x50, 0x57, 0x22, 0xa7, 0x9a, 0x62, 0x5f, 0x02, 0xaa, 0x06, 0x16, 0x94, 0xc3, 0xc0, 0x0f, 0x03,
|
0x32, 0xd4, 0x95, 0xc8, 0xa8, 0xa6, 0xd8, 0x93, 0x80, 0xaa, 0x81, 0x09, 0xa5, 0xc0, 0xf7, 0x02,
|
||||||
0x1a, 0x8f, 0x19, 0xad, 0xbd, 0xfe, 0x5a, 0x35, 0x6b, 0x89, 0x14, 0x15, 0xd8, 0x1b, 0xb3, 0xda,
|
0x9f, 0x46, 0x63, 0x46, 0x6b, 0xaf, 0xbe, 0x56, 0xcd, 0x5a, 0x24, 0x05, 0x05, 0xf6, 0xc6, 0xac,
|
||||||
0xeb, 0xaf, 0xd1, 0xa7, 0x50, 0x54, 0x2d, 0xc3, 0x3f, 0xcc, 0xfc, 0xe8, 0x5e, 0x75, 0x69, 0x99,
|
0xf6, 0xea, 0x6b, 0xf4, 0x29, 0x14, 0x54, 0xcb, 0xf0, 0xf7, 0x33, 0x2f, 0xbc, 0x57, 0x5d, 0x5a,
|
||||||
0xa8, 0x2e, 0xc2, 0x0a, 0x41, 0xc7, 0xb0, 0x7b, 0x3b, 0x61, 0xa3, 0x58, 0x75, 0x66, 0x99, 0xe8,
|
0x22, 0xaa, 0x8b, 0xb0, 0x42, 0xd0, 0x11, 0x64, 0x6f, 0x27, 0x6c, 0x14, 0xa9, 0xce, 0x2c, 0x11,
|
||||||
0x0f, 0xf4, 0x15, 0x1c, 0x27, 0x39, 0xa0, 0x71, 0x38, 0x8f, 0x86, 0x9c, 0xfa, 0x81, 0xc7, 0x3f,
|
0xfd, 0x81, 0xbe, 0x82, 0xa3, 0x38, 0x07, 0x34, 0x0a, 0xe6, 0xe1, 0x90, 0x53, 0xcf, 0x77, 0xf9,
|
||||||
0xa8, 0xbe, 0x2c, 0x13, 0x94, 0xc8, 0x7a, 0x4a, 0x64, 0x4b, 0x89, 0xf5, 0x9f, 0x1c, 0x14, 0x53,
|
0x7b, 0xd5, 0x97, 0x25, 0x82, 0x62, 0x59, 0x4f, 0x89, 0x2c, 0x29, 0x31, 0xff, 0x9b, 0x81, 0x42,
|
||||||
0x09, 0x40, 0x25, 0xd8, 0x27, 0xb8, 0x87, 0xc9, 0x3b, 0xdc, 0x34, 0x3e, 0x41, 0x26, 0x1c, 0x0f,
|
0x22, 0x01, 0xa8, 0x08, 0x7b, 0x04, 0xf7, 0x30, 0x79, 0x8b, 0x9b, 0xc6, 0x27, 0xa8, 0x02, 0x47,
|
||||||
0x9c, 0xb7, 0x8e, 0xfb, 0x83, 0x43, 0xaf, 0xeb, 0xef, 0xbb, 0xd8, 0xe9, 0xd3, 0x76, 0xbd, 0xd7,
|
0x03, 0xfb, 0x8d, 0xed, 0xfc, 0x60, 0xd3, 0xeb, 0xfa, 0xbb, 0x2e, 0xb6, 0xfb, 0xb4, 0x5d, 0xef,
|
||||||
0x36, 0x32, 0xe8, 0x39, 0x98, 0xb6, 0xd3, 0x70, 0x09, 0xc1, 0x8d, 0xfe, 0x52, 0x56, 0xef, 0xba,
|
0xb5, 0x8d, 0x14, 0x7a, 0x06, 0x15, 0xcb, 0x6e, 0x38, 0x84, 0xe0, 0x46, 0x7f, 0x29, 0xab, 0x77,
|
||||||
0x03, 0xa7, 0x6f, 0xec, 0xa0, 0x4f, 0xe1, 0xb4, 0x65, 0x3b, 0xf5, 0x0e, 0x5d, 0x71, 0x1a, 0x9d,
|
0x9d, 0x81, 0xdd, 0x37, 0x76, 0xd0, 0xa7, 0x70, 0xda, 0xb2, 0xec, 0x7a, 0x87, 0xae, 0x38, 0x8d,
|
||||||
0xfe, 0x3b, 0x8a, 0x7f, 0xbc, 0xb6, 0xc9, 0x7b, 0x23, 0xbb, 0x8d, 0xd0, 0xee, 0x77, 0x1a, 0x0b,
|
0x4e, 0xff, 0x2d, 0xc5, 0x3f, 0x5e, 0x5b, 0xe4, 0x9d, 0x91, 0xde, 0x46, 0x68, 0xf7, 0x3b, 0x8d,
|
||||||
0x0b, 0x39, 0xf4, 0x0c, 0x9e, 0x68, 0x82, 0x56, 0xa1, 0x7d, 0xd7, 0xa5, 0x3d, 0xd7, 0x75, 0x8c,
|
0x85, 0x86, 0x0c, 0x7a, 0x0a, 0x4f, 0x34, 0x41, 0x1f, 0xa1, 0x7d, 0xc7, 0xa1, 0x3d, 0xc7, 0xb1,
|
||||||
0x5d, 0x74, 0x08, 0x65, 0xdb, 0x79, 0x57, 0xef, 0xd8, 0x4d, 0x4a, 0x70, 0xbd, 0xd3, 0x35, 0xf6,
|
0x8d, 0x2c, 0x3a, 0x80, 0x92, 0x65, 0xbf, 0xad, 0x77, 0xac, 0x26, 0x25, 0xb8, 0xde, 0xe9, 0x1a,
|
||||||
0xd0, 0x11, 0x1c, 0x6c, 0xf2, 0xf2, 0xd2, 0xc4, 0x82, 0xe7, 0x3a, 0xb6, 0xeb, 0xd0, 0x77, 0x98,
|
0xbb, 0xe8, 0x10, 0xf6, 0x37, 0x79, 0x39, 0xa9, 0x62, 0xc1, 0x73, 0x6c, 0xcb, 0xb1, 0xe9, 0x5b,
|
||||||
0xf4, 0x6c, 0xd7, 0x31, 0xf6, 0xd1, 0x09, 0xa0, 0x75, 0x51, 0xbb, 0x5b, 0x6f, 0x18, 0x05, 0xf4,
|
0x4c, 0x7a, 0x96, 0x63, 0x1b, 0x7b, 0xe8, 0x18, 0xd0, 0xba, 0xa8, 0xdd, 0xad, 0x37, 0x8c, 0x3c,
|
||||||
0x04, 0x0e, 0xd7, 0xf1, 0xb7, 0xf8, 0xbd, 0x01, 0x32, 0x0d, 0x3a, 0x30, 0xfa, 0x06, 0x77, 0xdc,
|
0x7a, 0x02, 0x07, 0xeb, 0xf8, 0x1b, 0xfc, 0xce, 0x00, 0x99, 0x06, 0xed, 0x18, 0x7d, 0x8d, 0x3b,
|
||||||
0x1f, 0x68, 0xd7, 0x76, 0xec, 0xee, 0xa0, 0x6b, 0x14, 0xd1, 0x31, 0x18, 0x2d, 0x8c, 0xa9, 0xed,
|
0xce, 0x0f, 0xb4, 0x6b, 0xd9, 0x56, 0x77, 0xd0, 0x35, 0x0a, 0xe8, 0x08, 0x8c, 0x16, 0xc6, 0xd4,
|
||||||
0xf4, 0x06, 0xad, 0x96, 0xdd, 0xb0, 0xb1, 0xd3, 0x37, 0x4a, 0xda, 0xf3, 0xb6, 0x83, 0x97, 0xa5,
|
0xb2, 0x7b, 0x83, 0x56, 0xcb, 0x6a, 0x58, 0xd8, 0xee, 0x1b, 0x45, 0x6d, 0x79, 0x5b, 0xe0, 0x25,
|
||||||
0x42, 0xa3, 0x5d, 0x77, 0x1c, 0xdc, 0xa1, 0x4d, 0xbb, 0x57, 0x7f, 0xd3, 0xc1, 0x4d, 0xa3, 0x82,
|
0x79, 0xa0, 0xd1, 0xae, 0xdb, 0x36, 0xee, 0xd0, 0xa6, 0xd5, 0xab, 0xbf, 0xee, 0xe0, 0xa6, 0x51,
|
||||||
0xce, 0xe0, 0x59, 0x1f, 0x77, 0xaf, 0x5d, 0x52, 0x27, 0xef, 0xe9, 0x42, 0xde, 0xaa, 0xdb, 0x9d,
|
0x46, 0x67, 0xf0, 0xb4, 0x8f, 0xbb, 0xd7, 0x0e, 0xa9, 0x93, 0x77, 0x74, 0x21, 0x6f, 0xd5, 0xad,
|
||||||
0x01, 0xc1, 0xc6, 0x01, 0xfa, 0x0c, 0xce, 0x08, 0xfe, 0x7e, 0x60, 0x13, 0xdc, 0xa4, 0x8e, 0xdb,
|
0xce, 0x80, 0x60, 0x63, 0x1f, 0x7d, 0x06, 0x67, 0x04, 0x7f, 0x3f, 0xb0, 0x08, 0x6e, 0x52, 0xdb,
|
||||||
0xc4, 0xb4, 0x85, 0xeb, 0xfd, 0x01, 0xc1, 0xb4, 0x6b, 0xf7, 0x7a, 0xb6, 0xf3, 0xad, 0x61, 0xa0,
|
0x69, 0x62, 0xda, 0xc2, 0xf5, 0xfe, 0x80, 0x60, 0xda, 0xb5, 0x7a, 0x3d, 0xcb, 0xfe, 0xd6, 0x30,
|
||||||
0x5f, 0xc1, 0xf9, 0x92, 0xb2, 0x34, 0xb0, 0xc1, 0x3a, 0x94, 0xe7, 0x5b, 0xd4, 0xd3, 0xc1, 0x3f,
|
0xd0, 0xaf, 0xe0, 0x7c, 0x49, 0x59, 0x2a, 0xd8, 0x60, 0x1d, 0xc8, 0xf8, 0x16, 0xf5, 0xb4, 0xf1,
|
||||||
0xf6, 0xe9, 0x35, 0xc6, 0xc4, 0x40, 0xa8, 0x0a, 0x27, 0x2b, 0xf7, 0xda, 0x41, 0xe2, 0xfb, 0x48,
|
0x8f, 0x7d, 0x7a, 0x8d, 0x31, 0x31, 0x10, 0xaa, 0xc2, 0xf1, 0xca, 0xbc, 0x36, 0x10, 0xdb, 0x3e,
|
||||||
0xca, 0xae, 0x31, 0xe9, 0xd6, 0x1d, 0x59, 0xe0, 0x35, 0xd9, 0xb1, 0x0c, 0x7b, 0x25, 0xdb, 0x0c,
|
0x94, 0xb2, 0x6b, 0x4c, 0xba, 0x75, 0x5b, 0x16, 0x78, 0x4d, 0x76, 0x24, 0xdd, 0x5e, 0xc9, 0x36,
|
||||||
0xfb, 0xc9, 0x77, 0xb9, 0xfd, 0x1d, 0x23, 0x6b, 0xfd, 0x33, 0x0b, 0xe5, 0xb5, 0x66, 0x41, 0xcf,
|
0xdd, 0x7e, 0x82, 0x4e, 0x00, 0x0d, 0x6c, 0x82, 0xeb, 0x4d, 0x19, 0xe6, 0x12, 0xff, 0x4f, 0xee,
|
||||||
0xa1, 0x10, 0xfb, 0xa3, 0x80, 0x09, 0xd9, 0xce, 0xba, 0xd3, 0x57, 0x80, 0x7a, 0x2d, 0xc6, 0xcc,
|
0xbb, 0xcc, 0xde, 0x8e, 0x91, 0x36, 0xff, 0x99, 0x86, 0xd2, 0x5a, 0x17, 0xa1, 0x67, 0x90, 0x8f,
|
||||||
0x0f, 0xf4, 0x88, 0xd1, 0x23, 0xb6, 0xa0, 0x10, 0x35, 0x60, 0x9e, 0x42, 0x7e, 0xf1, 0xda, 0x64,
|
0xbc, 0x91, 0xcf, 0x84, 0xec, 0x73, 0x3d, 0x02, 0x56, 0x80, 0x7a, 0x46, 0xc6, 0xcc, 0xf3, 0xf5,
|
||||||
0x55, 0x63, 0xed, 0x0d, 0xf5, 0x2b, 0xf3, 0x1c, 0x0a, 0x72, 0x86, 0xc5, 0x82, 0x4d, 0x67, 0xaa,
|
0xec, 0xd1, 0xb3, 0x37, 0xaf, 0x10, 0x35, 0x79, 0x4e, 0x20, 0xb7, 0x78, 0x86, 0xd2, 0xaa, 0xe3,
|
||||||
0xe7, 0xca, 0x64, 0x05, 0xa0, 0xcf, 0xa1, 0x3c, 0xe5, 0x71, 0xcc, 0x46, 0x9c, 0xea, 0xbe, 0x01,
|
0x76, 0x87, 0xfa, 0xf9, 0x79, 0x06, 0x79, 0x39, 0xdc, 0x22, 0xc1, 0xa6, 0x33, 0xd5, 0x8c, 0x25,
|
||||||
0xc5, 0x28, 0x25, 0x60, 0x4b, 0xb5, 0xcf, 0xe7, 0xb0, 0xe8, 0xe3, 0x84, 0xb4, 0xab, 0x49, 0x09,
|
0xb2, 0x02, 0xd0, 0xe7, 0x50, 0x9a, 0xf2, 0x28, 0x62, 0x23, 0x4e, 0x75, 0x43, 0x81, 0x62, 0x14,
|
||||||
0xa8, 0x49, 0x9b, 0x23, 0x54, 0xb0, 0xa4, 0x3d, 0xd3, 0x23, 0x54, 0x30, 0xf4, 0x0a, 0x0e, 0xf5,
|
0x63, 0xb0, 0xa5, 0xfa, 0xea, 0x73, 0x58, 0x34, 0x78, 0x4c, 0xca, 0x6a, 0x52, 0x0c, 0x6a, 0xd2,
|
||||||
0x0c, 0xf0, 0x03, 0x7f, 0x3a, 0x9f, 0xea, 0x59, 0x90, 0x57, 0x21, 0x1f, 0xa8, 0x59, 0xa0, 0x71,
|
0xe6, 0x6c, 0x15, 0x2c, 0xee, 0xdb, 0xe4, 0x6c, 0x15, 0x0c, 0xbd, 0x84, 0x03, 0x3d, 0x1c, 0x3c,
|
||||||
0x35, 0x12, 0x9e, 0xc1, 0xfe, 0x0d, 0x8b, 0xb9, 0x9c, 0xde, 0x49, 0xaf, 0xe6, 0xe5, 0x77, 0x8b,
|
0xdf, 0x9b, 0xce, 0xa7, 0x7a, 0x48, 0xe4, 0x94, 0xcb, 0xfb, 0x6a, 0x48, 0x68, 0x5c, 0xcd, 0x8a,
|
||||||
0x73, 0x29, 0x92, 0x33, 0x3d, 0x92, 0x53, 0xa8, 0xa0, 0x45, 0xb7, 0x9c, 0x13, 0x99, 0xc7, 0xa5,
|
0xa7, 0xb0, 0x77, 0xc3, 0x22, 0x2e, 0xc7, 0x7a, 0xdc, 0xc4, 0x39, 0xf9, 0xdd, 0xe2, 0x5c, 0x8a,
|
||||||
0x07, 0xf6, 0x61, 0xe5, 0xa1, 0x98, 0xf2, 0xa0, 0x71, 0xe5, 0xe1, 0x15, 0x1c, 0xf2, 0x0f, 0x22,
|
0xe4, 0xb0, 0x0f, 0xe5, 0x78, 0xca, 0x6b, 0xd1, 0x2d, 0xe7, 0x44, 0xe6, 0x71, 0x69, 0x81, 0xbd,
|
||||||
0x62, 0x34, 0x9c, 0xb1, 0x9f, 0xe6, 0x9c, 0x7a, 0x4c, 0x30, 0xb3, 0xa4, 0x92, 0x7b, 0xa0, 0x04,
|
0x5f, 0x59, 0x28, 0x24, 0x2c, 0x68, 0x5c, 0x59, 0x78, 0x09, 0x07, 0xfc, 0xbd, 0x08, 0x19, 0x0d,
|
||||||
0xae, 0xc2, 0x9b, 0x4c, 0x30, 0xeb, 0x39, 0x54, 0x09, 0x8f, 0xb9, 0xe8, 0xfa, 0x71, 0xec, 0x87,
|
0x66, 0xec, 0xa7, 0x39, 0xa7, 0x2e, 0x13, 0xac, 0x52, 0x54, 0xc9, 0xdd, 0x57, 0x02, 0x47, 0xe1,
|
||||||
0x41, 0x23, 0x0c, 0x44, 0x14, 0x4e, 0x92, 0x47, 0xc0, 0x3a, 0x83, 0xd3, 0xad, 0x52, 0x3d, 0xc5,
|
0x4d, 0x26, 0x98, 0xf9, 0x0c, 0xaa, 0x84, 0x47, 0x5c, 0x74, 0xbd, 0x28, 0xf2, 0x02, 0xbf, 0x11,
|
||||||
0xa5, 0xf2, 0xf7, 0x73, 0x1e, 0xdd, 0x6f, 0x57, 0x7e, 0x0b, 0xa7, 0x5b, 0xa5, 0xc9, 0x13, 0xf0,
|
0xf8, 0x22, 0x0c, 0x26, 0xf1, 0xeb, 0x60, 0x9e, 0xc1, 0xe9, 0x56, 0xa9, 0x1e, 0xef, 0xf2, 0xf0,
|
||||||
0x25, 0xec, 0x06, 0xa1, 0xc7, 0x63, 0x33, 0xa3, 0x96, 0x80, 0x93, 0xd4, 0xbc, 0x75, 0x42, 0x8f,
|
0xf7, 0x73, 0x1e, 0xde, 0x6f, 0x3f, 0xfc, 0x06, 0x4e, 0xb7, 0x4a, 0xe3, 0xb7, 0xe1, 0x4b, 0xc8,
|
||||||
0xb7, 0xfd, 0x58, 0x84, 0xd1, 0x3d, 0xd1, 0x24, 0xeb, 0xdf, 0x19, 0x28, 0xa6, 0x60, 0x74, 0x02,
|
0xfa, 0x81, 0xcb, 0xa3, 0x4a, 0x4a, 0x6d, 0x07, 0xc7, 0x89, 0x41, 0x6c, 0x07, 0x2e, 0x6f, 0x7b,
|
||||||
0x7b, 0xb3, 0xf9, 0xcd, 0x1d, 0xbf, 0x4f, 0x2e, 0x55, 0xf2, 0x85, 0x5e, 0x42, 0x65, 0xc2, 0x62,
|
0x91, 0x08, 0xc2, 0x7b, 0xa2, 0x49, 0xe6, 0xbf, 0x53, 0x50, 0x48, 0xc0, 0xe8, 0x18, 0x76, 0x67,
|
||||||
0x41, 0xe5, 0xf8, 0xa3, 0xb2, 0x48, 0xc9, 0x9b, 0xb7, 0x81, 0xa2, 0x6f, 0xe0, 0x69, 0x28, 0xc6,
|
0xf3, 0x9b, 0x3b, 0x7e, 0x1f, 0x5f, 0xaa, 0xf8, 0x0b, 0xbd, 0x80, 0xf2, 0x84, 0x45, 0x82, 0xca,
|
||||||
0x3c, 0xd2, 0xeb, 0x4c, 0x3c, 0x1f, 0x0e, 0x79, 0x1c, 0xd3, 0x59, 0x14, 0xde, 0xa8, 0xab, 0xb6,
|
0xb9, 0x48, 0x65, 0x91, 0xe2, 0xc7, 0x70, 0x03, 0x45, 0xdf, 0xc0, 0x49, 0x20, 0xc6, 0x3c, 0xd4,
|
||||||
0x43, 0x1e, 0x13, 0xa3, 0xd7, 0xb0, 0x9f, 0xdc, 0x91, 0xd8, 0xcc, 0xa9, 0xd0, 0x9f, 0x3d, 0x7c,
|
0x7b, 0x4e, 0x34, 0x1f, 0x0e, 0x79, 0x14, 0xd1, 0x59, 0x18, 0xdc, 0xa8, 0xab, 0xb6, 0x43, 0x1e,
|
||||||
0x2a, 0x16, 0xd1, 0x2f, 0xa9, 0xd6, 0xbf, 0x32, 0x50, 0x59, 0x17, 0xa2, 0x17, 0xea, 0xf6, 0xab,
|
0x13, 0xa3, 0x57, 0xb0, 0x17, 0xdf, 0x91, 0xa8, 0x92, 0x51, 0xae, 0x3f, 0x7d, 0xf8, 0x86, 0x2c,
|
||||||
0x2b, 0xe8, 0x7b, 0xea, 0x1c, 0x39, 0x92, 0x42, 0x7e, 0xf1, 0x59, 0x6a, 0x70, 0x3c, 0xf5, 0x03,
|
0xbc, 0x5f, 0x52, 0xcd, 0x7f, 0xa5, 0xa0, 0xbc, 0x2e, 0x44, 0xcf, 0xd5, 0xed, 0x57, 0x57, 0xd0,
|
||||||
0x3a, 0xe3, 0x01, 0x9b, 0xf8, 0x7f, 0xe1, 0x74, 0xb1, 0x4c, 0x64, 0x15, 0x7b, 0xab, 0x0c, 0x59,
|
0x73, 0x55, 0x1c, 0x19, 0x92, 0x40, 0x3e, 0x3a, 0x96, 0x1a, 0x1c, 0x4d, 0x3d, 0x9f, 0xce, 0xb8,
|
||||||
0x50, 0x5a, 0x3b, 0x74, 0x4e, 0x1d, 0x7a, 0x0d, 0x7b, 0xf5, 0x8f, 0x0c, 0x94, 0xd2, 0x6b, 0x11,
|
0xcf, 0x26, 0xde, 0x5f, 0x38, 0x5d, 0x6c, 0x19, 0x69, 0xc5, 0xde, 0x2a, 0x43, 0x26, 0x14, 0xd7,
|
||||||
0x2a, 0x43, 0xc1, 0x76, 0x68, 0xab, 0x63, 0x7f, 0xdb, 0xee, 0x1b, 0x9f, 0xc8, 0xcf, 0xde, 0xa0,
|
0x82, 0xce, 0xa8, 0xa0, 0xd7, 0xb0, 0x97, 0x7f, 0x4f, 0x41, 0x31, 0xb9, 0x2f, 0xa1, 0x12, 0xe4,
|
||||||
0xd1, 0xc0, 0xb8, 0x89, 0x9b, 0x46, 0x06, 0x21, 0xa8, 0xc8, 0x79, 0x80, 0x9b, 0xb4, 0x6f, 0x77,
|
0x2d, 0x9b, 0xb6, 0x3a, 0xd6, 0xb7, 0xed, 0xbe, 0xf1, 0x89, 0xfc, 0xec, 0x0d, 0x1a, 0x0d, 0x8c,
|
||||||
0xb1, 0x3b, 0x90, 0x8f, 0xc3, 0x11, 0x1c, 0x24, 0x98, 0xe3, 0x52, 0xe2, 0x0e, 0xfa, 0xd8, 0xc8,
|
0x9b, 0xb8, 0x69, 0xa4, 0x10, 0x82, 0xb2, 0x1c, 0x08, 0xb8, 0x49, 0xfb, 0x56, 0x17, 0x3b, 0x03,
|
||||||
0x22, 0x03, 0x4a, 0x09, 0x88, 0x09, 0x71, 0x89, 0x91, 0x93, 0x13, 0x2d, 0x41, 0x1e, 0x3e, 0x34,
|
0xf9, 0x6a, 0x1c, 0xc2, 0x7e, 0x8c, 0xd9, 0x0e, 0x25, 0xce, 0xa0, 0x8f, 0x8d, 0x34, 0x32, 0xa0,
|
||||||
0x4d, 0xdc, 0xaf, 0xdb, 0x9d, 0x9e, 0xb1, 0x5b, 0xfb, 0x5b, 0x0e, 0xf6, 0xd4, 0x1a, 0x11, 0xa1,
|
0x18, 0x83, 0x98, 0x10, 0x87, 0x18, 0x19, 0x39, 0xea, 0x62, 0xe4, 0xe1, 0x0b, 0xd4, 0xc4, 0xfd,
|
||||||
0x36, 0x14, 0x53, 0xfb, 0x38, 0x3a, 0x4b, 0x55, 0xe0, 0xe1, 0x9e, 0x5e, 0x35, 0xb7, 0xef, 0x79,
|
0xba, 0xd5, 0xe9, 0x19, 0xd9, 0xda, 0xdf, 0x32, 0xb0, 0xab, 0xf6, 0x8b, 0x10, 0xb5, 0xa1, 0x90,
|
||||||
0xf3, 0xf8, 0xab, 0x0c, 0xfa, 0x0e, 0x4a, 0xe9, 0xed, 0x12, 0xa5, 0xb7, 0x86, 0x2d, 0x6b, 0xe7,
|
0x58, 0xd4, 0xd1, 0x59, 0xa2, 0x02, 0x0f, 0x17, 0xf8, 0x6a, 0x65, 0xfb, 0x02, 0x38, 0x8f, 0xbe,
|
||||||
0x47, 0x6d, 0xbd, 0x05, 0x03, 0xc7, 0xc2, 0x9f, 0xca, 0x2d, 0x21, 0xd9, 0xdb, 0x50, 0x35, 0xc5,
|
0x4a, 0xa1, 0xef, 0xa0, 0x98, 0x5c, 0x3b, 0x51, 0x72, 0x9d, 0xd8, 0xb2, 0x8f, 0x7e, 0x50, 0xd7,
|
||||||
0xdf, 0x58, 0x06, 0xab, 0xa7, 0x5b, 0x65, 0x49, 0x7f, 0x74, 0xf4, 0x11, 0x93, 0xcd, 0xe9, 0xc1,
|
0x1b, 0x30, 0x70, 0x24, 0xbc, 0xa9, 0x5c, 0x1f, 0xe2, 0x85, 0x0e, 0x55, 0x13, 0xfc, 0x8d, 0x2d,
|
||||||
0x11, 0xd7, 0xd7, 0xb5, 0xea, 0x8b, 0xc7, 0xc4, 0x89, 0x35, 0x0f, 0x8e, 0xb6, 0x74, 0x32, 0xfa,
|
0xb1, 0x7a, 0xba, 0x55, 0x16, 0xf7, 0x47, 0x47, 0x87, 0x18, 0xaf, 0x54, 0x0f, 0x42, 0x5c, 0xdf,
|
||||||
0x75, 0x3a, 0x82, 0x47, 0xe7, 0x40, 0xf5, 0xe5, 0xff, 0xa3, 0xad, 0xbc, 0x6c, 0x69, 0xf9, 0x35,
|
0xe3, 0xaa, 0xcf, 0x1f, 0x13, 0xc7, 0xda, 0x5c, 0x38, 0xdc, 0xd2, 0xc9, 0xe8, 0xd7, 0x49, 0x0f,
|
||||||
0x2f, 0x8f, 0x0f, 0x8c, 0x35, 0x2f, 0x1f, 0x99, 0x1c, 0x6f, 0x7e, 0xfb, 0xc7, 0xab, 0x91, 0x2f,
|
0x1e, 0x9d, 0x03, 0xd5, 0x17, 0xbf, 0x44, 0x5b, 0x59, 0xd9, 0xd2, 0xf2, 0x6b, 0x56, 0x1e, 0x1f,
|
||||||
0xc6, 0xf3, 0x9b, 0xcb, 0x61, 0x38, 0xbd, 0x9a, 0xf8, 0xa3, 0xb1, 0x08, 0xfc, 0x60, 0x14, 0x70,
|
0x18, 0x6b, 0x56, 0x3e, 0x30, 0x39, 0x5e, 0xff, 0xf6, 0x8f, 0x57, 0x23, 0x4f, 0x8c, 0xe7, 0x37,
|
||||||
0xf1, 0xe7, 0x30, 0xba, 0xbb, 0x9a, 0x04, 0xde, 0x95, 0xda, 0x44, 0xaf, 0x96, 0xe6, 0x6e, 0xf6,
|
0x97, 0xc3, 0x60, 0x7a, 0x35, 0xf1, 0x46, 0x63, 0xe1, 0x7b, 0xfe, 0xc8, 0xe7, 0xe2, 0xcf, 0x41,
|
||||||
0xd4, 0xff, 0x71, 0xbf, 0xfb, 0x5f, 0x00, 0x00, 0x00, 0xff, 0xff, 0x2f, 0xaa, 0x9f, 0x4e, 0xf7,
|
0x78, 0x77, 0x35, 0xf1, 0xdd, 0x2b, 0xb5, 0xa2, 0x5e, 0x2d, 0xd5, 0xdd, 0xec, 0xaa, 0x7f, 0xf0,
|
||||||
0x0d, 0x00, 0x00,
|
0x7e, 0xf7, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x7c, 0x28, 0x1a, 0x3a, 0x10, 0x0e, 0x00, 0x00,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reference imports to suppress errors if they are not otherwise used.
|
// Reference imports to suppress errors if they are not otherwise used.
|
||||||
|
@ -194,6 +194,12 @@ 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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
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
|
||||||
|
@ -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
|
||||||
|
@ -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,8 +1812,24 @@ 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) {
|
|
||||||
|
// If the failure message could not be decrypted, attribute the failure
|
||||||
|
// to our own outgoing channel.
|
||||||
|
//
|
||||||
|
// TODO(joostager): Penalize all channels in the route.
|
||||||
|
if sendErr == htlcswitch.ErrUnreadableFailureMessage {
|
||||||
|
sendErr = &htlcswitch.ForwardingError{
|
||||||
|
FailureSourceIdx: 0,
|
||||||
|
FailureMessage: lnwire.NewTemporaryChannelFailure(nil),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If an internal, non-forwarding error occurred, we can stop trying.
|
||||||
|
fErr, ok := sendErr.(*htlcswitch.ForwardingError)
|
||||||
|
if !ok {
|
||||||
|
return true, channeldb.FailureReasonError
|
||||||
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
failureSourceIdx = fErr.FailureSourceIdx
|
failureSourceIdx = fErr.FailureSourceIdx
|
||||||
|
@ -2425,6 +2425,95 @@ func TestEmptyRoutesGenerateSphinxPacket(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestUnknownErrorSource tests that if the source of an error is unknown, all
|
||||||
|
// edges along the route will be pruned.
|
||||||
|
func TestUnknownErrorSource(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
// Setup a network. It contains two paths to c: a->b->c and an
|
||||||
|
// alternative a->d->c.
|
||||||
|
chanCapSat := btcutil.Amount(100000)
|
||||||
|
testChannels := []*testChannel{
|
||||||
|
symmetricTestChannel("a", "b", chanCapSat, &testChannelPolicy{
|
||||||
|
Expiry: 144,
|
||||||
|
FeeRate: 400,
|
||||||
|
MinHTLC: 1,
|
||||||
|
MaxHTLC: lnwire.NewMSatFromSatoshis(chanCapSat),
|
||||||
|
}, 1),
|
||||||
|
symmetricTestChannel("b", "c", chanCapSat, &testChannelPolicy{
|
||||||
|
Expiry: 144,
|
||||||
|
FeeRate: 400,
|
||||||
|
MinHTLC: 1,
|
||||||
|
MaxHTLC: lnwire.NewMSatFromSatoshis(chanCapSat),
|
||||||
|
}, 3),
|
||||||
|
symmetricTestChannel("a", "d", chanCapSat, &testChannelPolicy{
|
||||||
|
Expiry: 144,
|
||||||
|
FeeRate: 400,
|
||||||
|
FeeBaseMsat: 100000,
|
||||||
|
MinHTLC: 1,
|
||||||
|
MaxHTLC: lnwire.NewMSatFromSatoshis(chanCapSat),
|
||||||
|
}, 2),
|
||||||
|
symmetricTestChannel("d", "c", chanCapSat, &testChannelPolicy{
|
||||||
|
Expiry: 144,
|
||||||
|
FeeRate: 400,
|
||||||
|
FeeBaseMsat: 100000,
|
||||||
|
MinHTLC: 1,
|
||||||
|
MaxHTLC: lnwire.NewMSatFromSatoshis(chanCapSat),
|
||||||
|
}, 4),
|
||||||
|
}
|
||||||
|
|
||||||
|
testGraph, err := createTestGraphFromChannels(testChannels, "a")
|
||||||
|
defer testGraph.cleanUp()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unable to create graph: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
const startingBlockHeight = 101
|
||||||
|
|
||||||
|
ctx, cleanUp, err := createTestCtxFromGraphInstance(startingBlockHeight,
|
||||||
|
testGraph)
|
||||||
|
|
||||||
|
defer cleanUp()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unable to create router: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a payment to node c.
|
||||||
|
payment := LightningPayment{
|
||||||
|
Target: ctx.aliases["c"],
|
||||||
|
Amount: lnwire.NewMSatFromSatoshis(1000),
|
||||||
|
FeeLimit: noFeeLimit,
|
||||||
|
PaymentHash: lntypes.Hash{},
|
||||||
|
}
|
||||||
|
|
||||||
|
// We'll modify the SendToSwitch method so that it simulates hop b as a
|
||||||
|
// node that returns an unparsable failure if approached via the a->b
|
||||||
|
// channel.
|
||||||
|
ctx.router.cfg.Payer.(*mockPaymentAttemptDispatcher).setPaymentResult(
|
||||||
|
func(firstHop lnwire.ShortChannelID) ([32]byte, error) {
|
||||||
|
|
||||||
|
// If channel a->b is used, return an error without
|
||||||
|
// source and message. The sender won't know the origin
|
||||||
|
// of the error.
|
||||||
|
if firstHop.ToUint64() == 1 {
|
||||||
|
return [32]byte{},
|
||||||
|
htlcswitch.ErrUnreadableFailureMessage
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise the payment succeeds.
|
||||||
|
return lntypes.Preimage{}, nil
|
||||||
|
})
|
||||||
|
|
||||||
|
// Send off the payment request to the router. The expectation is that
|
||||||
|
// the route a->b->c is tried first. An unreadable faiure is returned
|
||||||
|
// which should pruning the channel a->b. We expect the payment to
|
||||||
|
// succeed via a->d.
|
||||||
|
_, _, err = ctx.router.SendPayment(&payment)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("expected payment to succeed, but got: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// assertChannelsPruned ensures that only the given channels are pruned from the
|
// 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,
|
||||||
|
Loading…
Reference in New Issue
Block a user