mirror of
https://github.com/lightningnetwork/lnd.git
synced 2025-02-22 06:21:40 +01:00
Merge pull request #3499 from cfromknecht/mpp-payments-rpc
channeldb+rpcserver: expose legacy payments as multi-path payments
This commit is contained in:
commit
6f9fcfaccc
16 changed files with 1377 additions and 831 deletions
105
channeldb/mp_payment.go
Normal file
105
channeldb/mp_payment.go
Normal file
|
@ -0,0 +1,105 @@
|
|||
package channeldb
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/btcsuite/btcd/btcec"
|
||||
"github.com/lightningnetwork/lnd/lntypes"
|
||||
"github.com/lightningnetwork/lnd/lnwire"
|
||||
"github.com/lightningnetwork/lnd/routing/route"
|
||||
)
|
||||
|
||||
// MPPaymentCreationInfo is the information necessary to have ready when
|
||||
// initiating a payment, moving it into state InFlight.
|
||||
type MPPaymentCreationInfo struct {
|
||||
// PaymentHash is the hash this payment is paying to.
|
||||
PaymentHash lntypes.Hash
|
||||
|
||||
// Value is the amount we are paying.
|
||||
Value lnwire.MilliSatoshi
|
||||
|
||||
// CreatingTime is the time at which this payment was started.
|
||||
CreationTime time.Time
|
||||
|
||||
// PaymentRequest is the full payment request, if any.
|
||||
PaymentRequest []byte
|
||||
}
|
||||
|
||||
// HTLCAttempt contains information about a specific HTLC attempt for a given
|
||||
// payment. This information is used by the router to handle any errors coming
|
||||
// back after an attempt is made, and to query the switch about the status of a
|
||||
// payment. For settled payment this will be the information for the succeeding
|
||||
// payment attempt.
|
||||
type HTLCAttempt struct {
|
||||
// PaymentID is the unique ID used for this attempt.
|
||||
PaymentID uint64
|
||||
|
||||
// SessionKey is the ephemeral key used for this payment attempt.
|
||||
SessionKey *btcec.PrivateKey
|
||||
|
||||
// Route is the route attempted to send the HTLC.
|
||||
Route route.Route
|
||||
|
||||
// AttemptTime is the time at which this HTLC was attempted.
|
||||
AttemptTime time.Time
|
||||
|
||||
// Settle is the preimage of a successful payment. This serves as a
|
||||
// proof of payment. It will only be non-nil for settled payments.
|
||||
//
|
||||
// NOTE: Can be nil if payment is not settled.
|
||||
Settle *HTLCSettleInfo
|
||||
|
||||
// Fail is a failure reason code indicating the reason the payment
|
||||
// failed. It is only non-nil for failed payments.
|
||||
//
|
||||
// NOTE: Can be nil if payment is not failed.
|
||||
Failure *HTLCFailInfo
|
||||
}
|
||||
|
||||
// HTLCSettleInfo encapsulates the information that augments an HTLCAttempt in
|
||||
// the event that the HTLC is successful.
|
||||
type HTLCSettleInfo struct {
|
||||
// Preimage is the preimage of a successful HTLC. This serves as a proof
|
||||
// of payment.
|
||||
Preimage lntypes.Preimage
|
||||
|
||||
// SettleTime is the time at which this HTLC was settled.
|
||||
SettleTime time.Time
|
||||
}
|
||||
|
||||
// HTLCFailInfo encapsulates the information that augments an HTLCAttempt in the
|
||||
// event that the HTLC fails.
|
||||
type HTLCFailInfo struct {
|
||||
// FailTime is the time at which this HTLC was failed.
|
||||
FailTime time.Time
|
||||
}
|
||||
|
||||
// MPPayment is a wrapper around a payment's MPPaymentCreationInfo and
|
||||
// HTLCAttempts. All payments will have the MPPPaymentCreationInfo set, any
|
||||
// HTLCs made in attempts to be completed will populated in the HTLCs slice.
|
||||
// Each populated HTLCAttempt represents an attempted HTLC, each of which may
|
||||
// have the associated Settle or Fail struct populated if the HTLC is no longer
|
||||
// in-flight.
|
||||
type MPPayment struct {
|
||||
// sequenceNum is a unique identifier used to sort the payments in
|
||||
// order of creation.
|
||||
sequenceNum uint64
|
||||
|
||||
// Info holds all static information about this payment, and is
|
||||
// populated when the payment is initiated.
|
||||
Info *MPPaymentCreationInfo
|
||||
|
||||
// HTLCs holds the information about individual HTLCs that we send in
|
||||
// order to make the payment.
|
||||
HTLCs []HTLCAttempt
|
||||
|
||||
// FailureReason is the failure reason code indicating the reason the
|
||||
// payment failed.
|
||||
//
|
||||
// NOTE: Will only be set once the daemon has given up on the payment
|
||||
// altogether.
|
||||
FailureReason *FailureReason
|
||||
|
||||
// Status is the current PaymentStatus of this payment.
|
||||
Status PaymentStatus
|
||||
}
|
|
@ -8,7 +8,6 @@ import (
|
|||
|
||||
"github.com/coreos/bbolt"
|
||||
"github.com/lightningnetwork/lnd/lntypes"
|
||||
"github.com/lightningnetwork/lnd/routing/route"
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -192,16 +191,17 @@ func (p *PaymentControl) RegisterAttempt(paymentHash lntypes.Hash,
|
|||
// duplicate payments to the same payment hash. The provided preimage is
|
||||
// atomically saved to the DB for record keeping.
|
||||
func (p *PaymentControl) Success(paymentHash lntypes.Hash,
|
||||
preimage lntypes.Preimage) (*route.Route, error) {
|
||||
preimage lntypes.Preimage) (*MPPayment, error) {
|
||||
|
||||
var (
|
||||
updateErr error
|
||||
route *route.Route
|
||||
payment *MPPayment
|
||||
)
|
||||
err := p.db.Batch(func(tx *bbolt.Tx) error {
|
||||
// Reset the update error, to avoid carrying over an error
|
||||
// from a previous execution of the batched db transaction.
|
||||
updateErr = nil
|
||||
payment = nil
|
||||
|
||||
bucket, err := fetchPaymentBucket(tx, paymentHash)
|
||||
if err == ErrPaymentNotInitiated {
|
||||
|
@ -225,20 +225,14 @@ func (p *PaymentControl) Success(paymentHash lntypes.Hash,
|
|||
}
|
||||
|
||||
// Retrieve attempt info for the notification.
|
||||
attempt, err := fetchPaymentAttempt(bucket)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
route = &attempt.Route
|
||||
|
||||
return nil
|
||||
payment, err = fetchPayment(bucket)
|
||||
return err
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return route, updateErr
|
||||
return payment, updateErr
|
||||
}
|
||||
|
||||
// Fail transitions a payment into the Failed state, and records the reason the
|
||||
|
@ -246,16 +240,17 @@ func (p *PaymentControl) Success(paymentHash lntypes.Hash,
|
|||
// its next call for this payment hash, allowing the switch to make a
|
||||
// subsequent payment.
|
||||
func (p *PaymentControl) Fail(paymentHash lntypes.Hash,
|
||||
reason FailureReason) (*route.Route, error) {
|
||||
reason FailureReason) (*MPPayment, error) {
|
||||
|
||||
var (
|
||||
updateErr error
|
||||
route *route.Route
|
||||
payment *MPPayment
|
||||
)
|
||||
err := p.db.Batch(func(tx *bbolt.Tx) error {
|
||||
// Reset the update error, to avoid carrying over an error
|
||||
// from a previous execution of the batched db transaction.
|
||||
updateErr = nil
|
||||
payment = nil
|
||||
|
||||
bucket, err := fetchPaymentBucket(tx, paymentHash)
|
||||
if err == ErrPaymentNotInitiated {
|
||||
|
@ -279,28 +274,21 @@ func (p *PaymentControl) Fail(paymentHash lntypes.Hash,
|
|||
}
|
||||
|
||||
// Retrieve attempt info for the notification, if available.
|
||||
attempt, err := fetchPaymentAttempt(bucket)
|
||||
if err != nil && err != errNoAttemptInfo {
|
||||
return err
|
||||
}
|
||||
if err != errNoAttemptInfo {
|
||||
route = &attempt.Route
|
||||
}
|
||||
|
||||
return nil
|
||||
payment, err = fetchPayment(bucket)
|
||||
return err
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return route, updateErr
|
||||
return payment, updateErr
|
||||
}
|
||||
|
||||
// FetchPayment returns information about a payment from the database.
|
||||
func (p *PaymentControl) FetchPayment(paymentHash lntypes.Hash) (
|
||||
*Payment, error) {
|
||||
*MPPayment, error) {
|
||||
|
||||
var payment *Payment
|
||||
var payment *MPPayment
|
||||
err := p.db.View(func(tx *bbolt.Tx) error {
|
||||
bucket, err := fetchPaymentBucket(tx, paymentHash)
|
||||
if err != nil {
|
||||
|
|
|
@ -14,7 +14,6 @@ import (
|
|||
"github.com/coreos/bbolt"
|
||||
"github.com/davecgh/go-spew/spew"
|
||||
"github.com/lightningnetwork/lnd/lntypes"
|
||||
"github.com/lightningnetwork/lnd/routing/route"
|
||||
)
|
||||
|
||||
func initDB() (*DB, error) {
|
||||
|
@ -132,16 +131,22 @@ func TestPaymentControlSwitchFail(t *testing.T) {
|
|||
)
|
||||
|
||||
// Verifies that status was changed to StatusSucceeded.
|
||||
var route *route.Route
|
||||
route, err = pControl.Success(info.PaymentHash, preimg)
|
||||
var payment *MPPayment
|
||||
payment, err = pControl.Success(info.PaymentHash, preimg)
|
||||
if err != nil {
|
||||
t.Fatalf("error shouldn't have been received, got: %v", err)
|
||||
}
|
||||
|
||||
err = assertRouteEqual(route, &attempt.Route)
|
||||
if len(payment.HTLCs) != 1 {
|
||||
t.Fatalf("payment should have one htlc, got: %d",
|
||||
len(payment.HTLCs))
|
||||
}
|
||||
|
||||
err = assertRouteEqual(&payment.HTLCs[0].Route, &attempt.Route)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected route returned: %v vs %v: %v",
|
||||
spew.Sdump(attempt.Route), spew.Sdump(*route), err)
|
||||
spew.Sdump(attempt.Route),
|
||||
spew.Sdump(payment.HTLCs[0].Route), err)
|
||||
}
|
||||
|
||||
assertPaymentStatus(t, db, info.PaymentHash, StatusSucceeded)
|
||||
|
|
|
@ -101,9 +101,9 @@ const (
|
|||
// payment.
|
||||
FailureReasonError FailureReason = 2
|
||||
|
||||
// FailureReasonIncorrectPaymentDetails indicates that either the hash
|
||||
// is unknown or the final cltv delta or amount is incorrect.
|
||||
FailureReasonIncorrectPaymentDetails FailureReason = 3
|
||||
// FailureReasonPaymentDetails indicates that either the hash is unknown
|
||||
// or the final cltv delta or amount is incorrect.
|
||||
FailureReasonPaymentDetails FailureReason = 3
|
||||
|
||||
// TODO(halseth): cancel state.
|
||||
|
||||
|
@ -120,7 +120,7 @@ func (r FailureReason) String() string {
|
|||
return "no_route"
|
||||
case FailureReasonError:
|
||||
return "error"
|
||||
case FailureReasonIncorrectPaymentDetails:
|
||||
case FailureReasonPaymentDetails:
|
||||
return "incorrect_payment_details"
|
||||
}
|
||||
|
||||
|
@ -239,11 +239,11 @@ type Payment struct {
|
|||
// NOTE: Can be nil if no attempt is yet made.
|
||||
Attempt *PaymentAttemptInfo
|
||||
|
||||
// PaymentPreimage is the preimage of a successful payment. This serves
|
||||
// as a proof of payment. It will only be non-nil for settled payments.
|
||||
// Preimage is the preimage of a successful payment. This serves as a
|
||||
// proof of payment. It will only be non-nil for settled payments.
|
||||
//
|
||||
// NOTE: Can be nil if payment is not settled.
|
||||
PaymentPreimage *lntypes.Preimage
|
||||
Preimage *lntypes.Preimage
|
||||
|
||||
// Failure is a failure reason code indicating the reason the payment
|
||||
// failed. It is only non-nil for failed payments.
|
||||
|
@ -252,9 +252,70 @@ type Payment struct {
|
|||
Failure *FailureReason
|
||||
}
|
||||
|
||||
// ToMPPayment converts a legacy payment into an MPPayment.
|
||||
func (p *Payment) ToMPPayment() *MPPayment {
|
||||
var (
|
||||
htlcs []HTLCAttempt
|
||||
reason *FailureReason
|
||||
settle *HTLCSettleInfo
|
||||
failure *HTLCFailInfo
|
||||
)
|
||||
|
||||
// Promote the payment failure to a proper fail struct, if it exists.
|
||||
if p.Failure != nil {
|
||||
// NOTE: FailTime is not set for legacy payments.
|
||||
failure = &HTLCFailInfo{}
|
||||
reason = p.Failure
|
||||
}
|
||||
|
||||
// Promote the payment preimage to proper settle struct, if it exists.
|
||||
if p.Preimage != nil {
|
||||
// NOTE: SettleTime is not set for legacy payments.
|
||||
settle = &HTLCSettleInfo{
|
||||
Preimage: *p.Preimage,
|
||||
}
|
||||
}
|
||||
|
||||
// Either a settle or a failure may be set, but not both.
|
||||
if settle != nil && failure != nil {
|
||||
panic("htlc attempt has both settle and failure info")
|
||||
}
|
||||
|
||||
// Populate a single HTLC on the MPPayment if an attempt exists on the
|
||||
// legacy payment. If none exists we will leave the attempt info blank
|
||||
// since we cannot recover it.
|
||||
if p.Attempt != nil {
|
||||
// NOTE: AttemptTime is not set for legacy payments.
|
||||
htlcs = []HTLCAttempt{
|
||||
{
|
||||
PaymentID: p.Attempt.PaymentID,
|
||||
SessionKey: p.Attempt.SessionKey,
|
||||
Route: p.Attempt.Route,
|
||||
Settle: settle,
|
||||
Failure: failure,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
return &MPPayment{
|
||||
sequenceNum: p.sequenceNum,
|
||||
Info: &MPPaymentCreationInfo{
|
||||
PaymentHash: p.Info.PaymentHash,
|
||||
Value: p.Info.Value,
|
||||
CreationTime: p.Info.CreationDate,
|
||||
PaymentRequest: p.Info.PaymentRequest,
|
||||
},
|
||||
HTLCs: htlcs,
|
||||
FailureReason: reason,
|
||||
Status: p.Status,
|
||||
}
|
||||
}
|
||||
|
||||
// FetchPayments returns all sent payments found in the DB.
|
||||
func (db *DB) FetchPayments() ([]*Payment, error) {
|
||||
var payments []*Payment
|
||||
//
|
||||
// nolint: dupl
|
||||
func (db *DB) FetchPayments() ([]*MPPayment, error) {
|
||||
var payments []*MPPayment
|
||||
|
||||
err := db.View(func(tx *bbolt.Tx) error {
|
||||
paymentsBucket := tx.Bucket(paymentsRootBucket)
|
||||
|
@ -318,7 +379,7 @@ func (db *DB) FetchPayments() ([]*Payment, error) {
|
|||
return payments, nil
|
||||
}
|
||||
|
||||
func fetchPayment(bucket *bbolt.Bucket) (*Payment, error) {
|
||||
func fetchPayment(bucket *bbolt.Bucket) (*MPPayment, error) {
|
||||
var (
|
||||
err error
|
||||
p = &Payment{}
|
||||
|
@ -363,7 +424,7 @@ func fetchPayment(bucket *bbolt.Bucket) (*Payment, error) {
|
|||
if b != nil {
|
||||
var preimg lntypes.Preimage
|
||||
copy(preimg[:], b[:])
|
||||
p.PaymentPreimage = &preimg
|
||||
p.Preimage = &preimg
|
||||
}
|
||||
|
||||
// Get failure reason if available.
|
||||
|
@ -373,7 +434,7 @@ func fetchPayment(bucket *bbolt.Bucket) (*Payment, error) {
|
|||
p.Failure = &reason
|
||||
}
|
||||
|
||||
return p, nil
|
||||
return p.ToMPPayment(), nil
|
||||
}
|
||||
|
||||
// DeletePayments deletes all completed and failed payments from the DB.
|
||||
|
@ -473,7 +534,7 @@ func deserializePaymentCreationInfo(r io.Reader) (*PaymentCreationInfo, error) {
|
|||
reqLen := uint32(byteOrder.Uint32(scratch[:4]))
|
||||
payReq := make([]byte, reqLen)
|
||||
if reqLen > 0 {
|
||||
if _, err := io.ReadFull(r, payReq[:]); err != nil {
|
||||
if _, err := io.ReadFull(r, payReq); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
|
|
@ -422,10 +422,13 @@ type PaymentStatus struct {
|
|||
Preimage []byte `protobuf:"bytes,2,opt,name=preimage,proto3" json:"preimage,omitempty"`
|
||||
//*
|
||||
//The taken route when state is SUCCEEDED.
|
||||
Route *lnrpc.Route `protobuf:"bytes,3,opt,name=route,proto3" json:"route,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
Route *lnrpc.Route `protobuf:"bytes,3,opt,name=route,proto3" json:"route,omitempty"`
|
||||
//*
|
||||
//The HTLCs made in attempt to settle the payment [EXPERIMENTAL].
|
||||
Htlcs []*lnrpc.HTLCAttempt `protobuf:"bytes,4,rep,name=htlcs,proto3" json:"htlcs,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
}
|
||||
|
||||
func (m *PaymentStatus) Reset() { *m = PaymentStatus{} }
|
||||
|
@ -474,6 +477,13 @@ func (m *PaymentStatus) GetRoute() *lnrpc.Route {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (m *PaymentStatus) GetHtlcs() []*lnrpc.HTLCAttempt {
|
||||
if m != nil {
|
||||
return m.Htlcs
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type RouteFeeRequest struct {
|
||||
//*
|
||||
//The destination once wishes to obtain a routing fee quote to.
|
||||
|
@ -1455,131 +1465,132 @@ func init() {
|
|||
func init() { proto.RegisterFile("routerrpc/router.proto", fileDescriptor_7a0613f69d37b0a5) }
|
||||
|
||||
var fileDescriptor_7a0613f69d37b0a5 = []byte{
|
||||
// 1975 bytes of a gzipped FileDescriptorProto
|
||||
// 1993 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x58, 0x4f, 0x73, 0x1a, 0xc9,
|
||||
0x15, 0xdf, 0x11, 0x20, 0xe0, 0x01, 0xd2, 0xa8, 0xa5, 0x95, 0xc7, 0x48, 0x5a, 0x6b, 0xc7, 0x8e,
|
||||
0x57, 0xe5, 0xf2, 0x4a, 0x8e, 0x52, 0xbb, 0xe5, 0xda, 0x43, 0x52, 0x18, 0x86, 0xd5, 0xc8, 0x30,
|
||||
0xc8, 0x0d, 0x78, 0xd7, 0xd9, 0x43, 0x57, 0x0b, 0x5a, 0x62, 0x4a, 0xc3, 0x0c, 0x3b, 0xd3, 0x38,
|
||||
0x56, 0x0e, 0xf9, 0x04, 0xc9, 0x35, 0x5f, 0x21, 0x97, 0xe4, 0x94, 0xef, 0x94, 0x54, 0xe5, 0x9e,
|
||||
0x7b, 0xaa, 0xbb, 0x67, 0x60, 0x40, 0x48, 0x9b, 0x93, 0xe8, 0xdf, 0x7b, 0xfd, 0x5e, 0xcf, 0xfb,
|
||||
0xf3, 0xeb, 0xd7, 0x82, 0xdd, 0x30, 0x98, 0x72, 0x16, 0x86, 0x93, 0xc1, 0x89, 0xfa, 0x75, 0x3c,
|
||||
0x09, 0x03, 0x1e, 0xa0, 0xe2, 0x0c, 0xaf, 0x16, 0xc3, 0xc9, 0x40, 0xa1, 0xe6, 0x7f, 0xb2, 0x80,
|
||||
0xba, 0xcc, 0x1f, 0x5e, 0xd0, 0xdb, 0x31, 0xf3, 0x39, 0x66, 0x3f, 0x4f, 0x59, 0xc4, 0x11, 0x82,
|
||||
0xec, 0x90, 0x45, 0xdc, 0xd0, 0x0e, 0xb5, 0xa3, 0x32, 0x96, 0xbf, 0x91, 0x0e, 0x19, 0x3a, 0xe6,
|
||||
0xc6, 0xda, 0xa1, 0x76, 0x94, 0xc1, 0xe2, 0x27, 0x7a, 0x0c, 0x05, 0x3a, 0xe6, 0x64, 0x1c, 0x51,
|
||||
0x6e, 0x94, 0x25, 0x9c, 0xa7, 0x63, 0xde, 0x8e, 0x28, 0x47, 0x5f, 0x42, 0x79, 0xa2, 0x4c, 0x92,
|
||||
0x11, 0x8d, 0x46, 0x46, 0x46, 0x1a, 0x2a, 0xc5, 0xd8, 0x19, 0x8d, 0x46, 0xe8, 0x08, 0xf4, 0x2b,
|
||||
0xd7, 0xa7, 0x1e, 0x19, 0x78, 0xfc, 0x23, 0x19, 0x32, 0x8f, 0x53, 0x23, 0x7b, 0xa8, 0x1d, 0xe5,
|
||||
0xf0, 0x86, 0xc4, 0xeb, 0x1e, 0xff, 0xd8, 0x10, 0x28, 0xfa, 0x0a, 0x36, 0x13, 0x63, 0xa1, 0x3a,
|
||||
0xa0, 0x91, 0x3b, 0xd4, 0x8e, 0x8a, 0x78, 0x63, 0xb2, 0x78, 0xec, 0xaf, 0x60, 0x93, 0xbb, 0x63,
|
||||
0x16, 0x4c, 0x39, 0x89, 0xd8, 0x20, 0xf0, 0x87, 0x91, 0xb1, 0xae, 0x2c, 0xc6, 0x70, 0x57, 0xa1,
|
||||
0xc8, 0x84, 0xca, 0x15, 0x63, 0xc4, 0x73, 0xc7, 0x2e, 0x27, 0xe2, 0xf8, 0x79, 0x79, 0xfc, 0xd2,
|
||||
0x15, 0x63, 0x2d, 0x81, 0x75, 0x29, 0x47, 0xcf, 0x60, 0x63, 0xae, 0x23, 0xbf, 0xb1, 0x22, 0x95,
|
||||
0xca, 0x89, 0x92, 0xfc, 0xd0, 0x97, 0xa0, 0x07, 0x53, 0x7e, 0x1d, 0xb8, 0xfe, 0x35, 0x19, 0x8c,
|
||||
0xa8, 0x4f, 0xdc, 0xa1, 0x51, 0x38, 0xd4, 0x8e, 0xb2, 0x6f, 0xd6, 0x5e, 0x69, 0x78, 0x23, 0x91,
|
||||
0xd5, 0x47, 0xd4, 0xb7, 0x87, 0xe8, 0x39, 0x6c, 0x7a, 0x34, 0xe2, 0x64, 0x14, 0x4c, 0xc8, 0x64,
|
||||
0x7a, 0x79, 0xc3, 0x6e, 0x8d, 0x0d, 0x19, 0x99, 0x8a, 0x80, 0xcf, 0x82, 0xc9, 0x85, 0x04, 0xd1,
|
||||
0x01, 0x80, 0x8c, 0x8a, 0x74, 0x6e, 0x14, 0xe5, 0x37, 0x14, 0x05, 0x22, 0x1d, 0xa3, 0x53, 0x28,
|
||||
0xc9, 0x6c, 0x92, 0x91, 0xeb, 0xf3, 0xc8, 0x80, 0xc3, 0xcc, 0x51, 0xe9, 0x54, 0x3f, 0xf6, 0x7c,
|
||||
0x91, 0x58, 0x2c, 0x24, 0x67, 0xae, 0xcf, 0x71, 0x5a, 0x09, 0x59, 0x50, 0x10, 0x69, 0x24, 0xdc,
|
||||
0xfb, 0x68, 0x94, 0xe4, 0x86, 0x17, 0xc7, 0xb3, 0x92, 0x38, 0xbe, 0x5b, 0x03, 0xc7, 0x0d, 0x16,
|
||||
0xf1, 0x9e, 0xf7, 0xd1, 0xf2, 0x79, 0x78, 0x8b, 0xf3, 0x43, 0xb5, 0xaa, 0x7e, 0x07, 0xe5, 0xb4,
|
||||
0x40, 0x54, 0x85, 0xf8, 0x0a, 0x51, 0x28, 0x59, 0x2c, 0x7e, 0xa2, 0x1d, 0xc8, 0x7d, 0xa4, 0xde,
|
||||
0x94, 0xc9, 0x4a, 0x29, 0x63, 0xb5, 0xf8, 0x6e, 0xed, 0xb5, 0x66, 0xbe, 0x86, 0xed, 0x5e, 0x48,
|
||||
0x07, 0x37, 0x4b, 0xc5, 0xb6, 0x5c, 0x2b, 0xda, 0x9d, 0x5a, 0x31, 0xff, 0x04, 0x95, 0x78, 0x53,
|
||||
0x97, 0x53, 0x3e, 0x8d, 0xd0, 0xd7, 0x90, 0x8b, 0x38, 0xe5, 0x4c, 0x2a, 0x6f, 0x9c, 0x3e, 0x4a,
|
||||
0x7d, 0x4a, 0x4a, 0x91, 0x61, 0xa5, 0x85, 0xaa, 0x50, 0x98, 0x84, 0xcc, 0x1d, 0xd3, 0xeb, 0xe4,
|
||||
0x58, 0xb3, 0x35, 0x32, 0x21, 0x27, 0x37, 0xcb, 0x1a, 0x2d, 0x9d, 0x96, 0xd3, 0x61, 0xc4, 0x4a,
|
||||
0x64, 0xfe, 0x16, 0x36, 0xe5, 0xba, 0xc9, 0xd8, 0x43, 0x2d, 0xf2, 0x08, 0x44, 0x03, 0xc8, 0x82,
|
||||
0x52, 0x6d, 0xb2, 0x4e, 0xc7, 0xa2, 0x96, 0xcc, 0x21, 0xe8, 0xf3, 0xfd, 0xd1, 0x24, 0xf0, 0x23,
|
||||
0x26, 0xea, 0x5f, 0x18, 0x17, 0x85, 0x23, 0xea, 0x4c, 0x56, 0x98, 0x26, 0x77, 0x6d, 0xc4, 0x78,
|
||||
0x93, 0x31, 0x59, 0x63, 0xcf, 0x55, 0x59, 0x13, 0x2f, 0x18, 0xdc, 0x88, 0x46, 0xa1, 0xb7, 0xb1,
|
||||
0xf9, 0x8a, 0x80, 0x5b, 0xc1, 0xe0, 0xa6, 0x21, 0x40, 0xf3, 0x27, 0xd5, 0xcb, 0xbd, 0x40, 0x9d,
|
||||
0xfd, 0xff, 0x0e, 0xef, 0x3c, 0x04, 0x6b, 0xf7, 0x87, 0x80, 0xc0, 0xf6, 0x82, 0xf1, 0xf8, 0x2b,
|
||||
0xd2, 0x91, 0xd5, 0x96, 0x22, 0xfb, 0x12, 0xf2, 0x57, 0xd4, 0xf5, 0xa6, 0x61, 0x62, 0x18, 0xa5,
|
||||
0xd2, 0xd4, 0x54, 0x12, 0x9c, 0xa8, 0x98, 0xff, 0xcd, 0x43, 0x3e, 0x06, 0xd1, 0x29, 0x64, 0x07,
|
||||
0xc1, 0x30, 0xc9, 0xee, 0x17, 0x77, 0xb7, 0x25, 0x7f, 0xeb, 0xc1, 0x90, 0x61, 0xa9, 0x8b, 0x7e,
|
||||
0x07, 0x1b, 0xa2, 0x01, 0x7d, 0xe6, 0x91, 0xe9, 0x64, 0x48, 0x67, 0x09, 0x35, 0x52, 0xbb, 0xeb,
|
||||
0x4a, 0xa1, 0x2f, 0xe5, 0xb8, 0x32, 0x48, 0x2f, 0xd1, 0x1e, 0x14, 0x47, 0xdc, 0x1b, 0xa8, 0x4c,
|
||||
0x64, 0x65, 0x41, 0x17, 0x04, 0x20, 0x73, 0x60, 0x42, 0x25, 0xf0, 0xdd, 0xc0, 0x27, 0xd1, 0x88,
|
||||
0x92, 0xd3, 0x6f, 0xbe, 0x95, 0x0c, 0x54, 0xc6, 0x25, 0x09, 0x76, 0x47, 0xf4, 0xf4, 0x9b, 0x6f,
|
||||
0xd1, 0x13, 0x28, 0xc9, 0xae, 0x65, 0x9f, 0x26, 0x6e, 0x78, 0x2b, 0xa9, 0xa7, 0x82, 0x65, 0x23,
|
||||
0x5b, 0x12, 0x11, 0xad, 0x71, 0xe5, 0xd1, 0xeb, 0x48, 0xd2, 0x4d, 0x05, 0xab, 0x05, 0x7a, 0x05,
|
||||
0x3b, 0x71, 0x0c, 0x48, 0x14, 0x4c, 0xc3, 0x01, 0x23, 0xae, 0x3f, 0x64, 0x9f, 0x24, 0x8d, 0x54,
|
||||
0x30, 0x8a, 0x65, 0x5d, 0x29, 0xb2, 0x85, 0x04, 0xed, 0xc2, 0xfa, 0x88, 0xb9, 0xd7, 0x23, 0x45,
|
||||
0x0d, 0x15, 0x1c, 0xaf, 0xcc, 0xbf, 0xe7, 0xa0, 0x94, 0x0a, 0x0c, 0x2a, 0x43, 0x01, 0x5b, 0x5d,
|
||||
0x0b, 0xbf, 0xb7, 0x1a, 0xfa, 0x67, 0xe8, 0x08, 0x9e, 0xd9, 0x4e, 0xbd, 0x83, 0xb1, 0x55, 0xef,
|
||||
0x91, 0x0e, 0x26, 0x7d, 0xe7, 0xad, 0xd3, 0xf9, 0xc1, 0x21, 0x17, 0xb5, 0x0f, 0x6d, 0xcb, 0xe9,
|
||||
0x91, 0x86, 0xd5, 0xab, 0xd9, 0xad, 0xae, 0xae, 0xa1, 0x7d, 0x30, 0xe6, 0x9a, 0x89, 0xb8, 0xd6,
|
||||
0xee, 0xf4, 0x9d, 0x9e, 0xbe, 0x86, 0x9e, 0xc0, 0x5e, 0xd3, 0x76, 0x6a, 0x2d, 0x32, 0xd7, 0xa9,
|
||||
0xb7, 0x7a, 0xef, 0x89, 0xf5, 0xe3, 0x85, 0x8d, 0x3f, 0xe8, 0x99, 0x55, 0x0a, 0x67, 0xbd, 0x56,
|
||||
0x3d, 0xb1, 0x90, 0x45, 0x8f, 0xe1, 0x73, 0xa5, 0xa0, 0xb6, 0x90, 0x5e, 0xa7, 0x43, 0xba, 0x9d,
|
||||
0x8e, 0xa3, 0xe7, 0xd0, 0x16, 0x54, 0x6c, 0xe7, 0x7d, 0xad, 0x65, 0x37, 0x08, 0xb6, 0x6a, 0xad,
|
||||
0xb6, 0xbe, 0x8e, 0xb6, 0x61, 0x73, 0x59, 0x2f, 0x2f, 0x4c, 0x24, 0x7a, 0x1d, 0xc7, 0xee, 0x38,
|
||||
0xe4, 0xbd, 0x85, 0xbb, 0x76, 0xc7, 0xd1, 0x0b, 0x68, 0x17, 0xd0, 0xa2, 0xe8, 0xac, 0x5d, 0xab,
|
||||
0xeb, 0x45, 0xf4, 0x39, 0x6c, 0x2d, 0xe2, 0x6f, 0xad, 0x0f, 0x3a, 0x20, 0x03, 0x76, 0xd4, 0xc1,
|
||||
0xc8, 0x1b, 0xab, 0xd5, 0xf9, 0x81, 0xb4, 0x6d, 0xc7, 0x6e, 0xf7, 0xdb, 0x7a, 0x09, 0xed, 0x80,
|
||||
0xde, 0xb4, 0x2c, 0x62, 0x3b, 0xdd, 0x7e, 0xb3, 0x69, 0xd7, 0x6d, 0xcb, 0xe9, 0xe9, 0x65, 0xe5,
|
||||
0x79, 0xd5, 0x87, 0x57, 0xc4, 0x86, 0xfa, 0x59, 0xcd, 0x71, 0xac, 0x16, 0x69, 0xd8, 0xdd, 0xda,
|
||||
0x9b, 0x96, 0xd5, 0xd0, 0x37, 0xd0, 0x01, 0x3c, 0xee, 0x59, 0xed, 0x8b, 0x0e, 0xae, 0xe1, 0x0f,
|
||||
0x24, 0x91, 0x37, 0x6b, 0x76, 0xab, 0x8f, 0x2d, 0x7d, 0x13, 0x7d, 0x09, 0x07, 0xd8, 0x7a, 0xd7,
|
||||
0xb7, 0xb1, 0xd5, 0x20, 0x4e, 0xa7, 0x61, 0x91, 0xa6, 0x55, 0xeb, 0xf5, 0xb1, 0x45, 0xda, 0x76,
|
||||
0xb7, 0x6b, 0x3b, 0xdf, 0xeb, 0x3a, 0x7a, 0x06, 0x87, 0x33, 0x95, 0x99, 0x81, 0x25, 0xad, 0x2d,
|
||||
0xf1, 0x7d, 0x49, 0x4a, 0x1d, 0xeb, 0xc7, 0x1e, 0xb9, 0xb0, 0x2c, 0xac, 0x23, 0x54, 0x85, 0xdd,
|
||||
0xb9, 0x7b, 0xe5, 0x20, 0xf6, 0xbd, 0x2d, 0x64, 0x17, 0x16, 0x6e, 0xd7, 0x1c, 0x91, 0xe0, 0x05,
|
||||
0xd9, 0x8e, 0x38, 0xf6, 0x5c, 0xb6, 0x7c, 0xec, 0xcf, 0x11, 0x82, 0x8d, 0x54, 0x56, 0x9a, 0x35,
|
||||
0xac, 0xef, 0xa2, 0x1d, 0xd8, 0x4c, 0x4e, 0x90, 0x28, 0xfe, 0x2b, 0x8f, 0x1e, 0x01, 0xea, 0x3b,
|
||||
0xd8, 0xaa, 0x35, 0x44, 0x40, 0x66, 0x82, 0x7f, 0xe7, 0xcf, 0xb3, 0x85, 0x35, 0x3d, 0x63, 0xfe,
|
||||
0x33, 0x03, 0x95, 0x85, 0xbe, 0x44, 0xfb, 0x50, 0x8c, 0xdc, 0x6b, 0x9f, 0x72, 0xc1, 0x1c, 0x8a,
|
||||
0x54, 0xe6, 0x80, 0xbc, 0x1b, 0x47, 0xd4, 0xf5, 0x15, 0x9b, 0x29, 0x36, 0x2f, 0x4a, 0x44, 0x72,
|
||||
0xd9, 0x1e, 0xe4, 0x93, 0x7b, 0x38, 0x33, 0xbb, 0x87, 0xd7, 0x07, 0xea, 0xfe, 0xdd, 0x87, 0xa2,
|
||||
0xa0, 0xcc, 0x88, 0xd3, 0xf1, 0x44, 0xb6, 0x78, 0x05, 0xcf, 0x01, 0xf4, 0x14, 0x2a, 0x63, 0x16,
|
||||
0x45, 0xf4, 0x9a, 0x11, 0xd5, 0xa6, 0x20, 0x35, 0xca, 0x31, 0xd8, 0x94, 0xdd, 0xfa, 0x14, 0x12,
|
||||
0xda, 0x88, 0x95, 0x72, 0x4a, 0x29, 0x06, 0x95, 0xd2, 0x32, 0x63, 0x73, 0x1a, 0xb3, 0x41, 0x9a,
|
||||
0xb1, 0x39, 0x45, 0x2f, 0x60, 0x4b, 0x51, 0x8e, 0xeb, 0xbb, 0xe3, 0xe9, 0x58, 0x51, 0x4f, 0x5e,
|
||||
0x52, 0xcf, 0xa6, 0xa4, 0x1e, 0x85, 0x4b, 0x06, 0x7a, 0x0c, 0x85, 0x4b, 0x1a, 0x31, 0x71, 0x59,
|
||||
0xc4, 0xd4, 0x90, 0x17, 0xeb, 0x26, 0x63, 0x42, 0x24, 0xae, 0x90, 0x50, 0x90, 0x9e, 0x62, 0x84,
|
||||
0xfc, 0x15, 0x63, 0x58, 0xc4, 0x72, 0xe6, 0x81, 0x7e, 0x9a, 0x7b, 0x28, 0xa5, 0x3c, 0x28, 0x5c,
|
||||
0x7a, 0x78, 0x01, 0x5b, 0xec, 0x13, 0x0f, 0x29, 0x09, 0x26, 0xf4, 0xe7, 0x29, 0x23, 0x43, 0xca,
|
||||
0xa9, 0x1c, 0xec, 0xca, 0x78, 0x53, 0x0a, 0x3a, 0x12, 0x6f, 0x50, 0x4e, 0xcd, 0x7d, 0xa8, 0x62,
|
||||
0x16, 0x31, 0xde, 0x76, 0xa3, 0xc8, 0x0d, 0xfc, 0x7a, 0xe0, 0xf3, 0x30, 0xf0, 0xe2, 0x3b, 0xc7,
|
||||
0x3c, 0x80, 0xbd, 0x95, 0x52, 0x75, 0x69, 0x88, 0xcd, 0xef, 0xa6, 0x2c, 0xbc, 0x5d, 0xbd, 0xf9,
|
||||
0x1d, 0xec, 0xad, 0x94, 0xc6, 0x37, 0xce, 0x4b, 0xc8, 0x4d, 0xa8, 0x1b, 0x46, 0xc6, 0x9a, 0x9c,
|
||||
0x62, 0x76, 0x17, 0xae, 0x7e, 0x37, 0x3c, 0x73, 0x23, 0x1e, 0x84, 0xb7, 0x58, 0x29, 0x9d, 0x67,
|
||||
0x0b, 0x9a, 0xbe, 0x66, 0xfe, 0x59, 0x83, 0x52, 0x4a, 0x28, 0xea, 0xc0, 0x0f, 0x86, 0x8c, 0x5c,
|
||||
0x85, 0xc1, 0x38, 0xa9, 0xb0, 0x19, 0x80, 0x0c, 0xc8, 0xcb, 0x05, 0x0f, 0xe2, 0xf2, 0x4a, 0x96,
|
||||
0xe8, 0x6b, 0xc8, 0x8f, 0x94, 0x09, 0x99, 0xa5, 0xd2, 0xe9, 0xf6, 0x92, 0x77, 0x11, 0x1b, 0x9c,
|
||||
0xe8, 0x9c, 0x67, 0x0b, 0x19, 0x3d, 0x7b, 0x9e, 0x2d, 0x64, 0xf5, 0xdc, 0x79, 0xb6, 0x90, 0xd3,
|
||||
0xd7, 0xcf, 0xb3, 0x85, 0x75, 0x3d, 0x6f, 0xfe, 0x55, 0x83, 0x42, 0xa2, 0xbd, 0x58, 0x93, 0x6a,
|
||||
0x00, 0x48, 0xd5, 0xe4, 0x29, 0xec, 0x8c, 0x5d, 0x9f, 0x4c, 0x98, 0x4f, 0x3d, 0xf7, 0x8f, 0x8c,
|
||||
0x2c, 0xce, 0x17, 0x2b, 0x65, 0xe8, 0x35, 0x3c, 0x92, 0x53, 0x26, 0xe5, 0x9c, 0x8d, 0x27, 0x9c,
|
||||
0x44, 0xd3, 0xc1, 0x80, 0x45, 0xd1, 0xd5, 0xd4, 0x93, 0x2d, 0x51, 0xc0, 0xf7, 0x89, 0xcd, 0x31,
|
||||
0x3c, 0x92, 0xa1, 0xbf, 0x08, 0x83, 0x4b, 0x7a, 0xe9, 0x7a, 0x2e, 0xbf, 0x4d, 0xc6, 0x88, 0x7d,
|
||||
0x28, 0x8a, 0xe0, 0x10, 0x3f, 0xb9, 0x97, 0xcb, 0x78, 0x0e, 0x88, 0x90, 0xf1, 0x40, 0xc9, 0xe2,
|
||||
0x90, 0xc5, 0x4b, 0x31, 0x20, 0xcc, 0x1e, 0x09, 0x19, 0x79, 0xe8, 0xd9, 0xda, 0xbc, 0x01, 0xe3,
|
||||
0xae, 0xbb, 0x38, 0xcd, 0x87, 0x50, 0x9a, 0xcc, 0x61, 0xe9, 0x51, 0xc3, 0x69, 0x28, 0x9d, 0x8c,
|
||||
0xb5, 0x5f, 0x4e, 0x86, 0xf9, 0x37, 0x0d, 0xb6, 0xde, 0x4c, 0x5d, 0x6f, 0xb8, 0x30, 0x1d, 0xa5,
|
||||
0xdf, 0x30, 0xda, 0xe2, 0x1b, 0x66, 0xd5, 0x03, 0x65, 0x6d, 0xe5, 0x03, 0x65, 0xd5, 0x23, 0x20,
|
||||
0x73, 0xef, 0x23, 0xe0, 0x09, 0x94, 0xe6, 0xf3, 0x7f, 0x64, 0x64, 0x0f, 0x33, 0x47, 0x65, 0x0c,
|
||||
0xa3, 0x64, 0xf8, 0x8f, 0xcc, 0xd7, 0x80, 0xd2, 0x07, 0x8d, 0x03, 0x32, 0x1b, 0xd2, 0xb4, 0x7b,
|
||||
0x87, 0xb4, 0x17, 0x7f, 0xd1, 0xa0, 0x9c, 0x9e, 0x7f, 0x51, 0x05, 0x8a, 0xb6, 0x43, 0x9a, 0x2d,
|
||||
0xfb, 0xfb, 0xb3, 0x9e, 0xfe, 0x99, 0x58, 0x76, 0xfb, 0xf5, 0xba, 0x65, 0x35, 0xac, 0x86, 0xae,
|
||||
0x09, 0x0e, 0x17, 0x74, 0x6c, 0x35, 0x48, 0xcf, 0x6e, 0x5b, 0x9d, 0xbe, 0xb8, 0xdd, 0xb7, 0x61,
|
||||
0x33, 0xc6, 0x9c, 0x0e, 0xc1, 0x9d, 0x7e, 0xcf, 0xd2, 0x33, 0x48, 0x87, 0x72, 0x0c, 0x5a, 0x18,
|
||||
0x77, 0xb0, 0x9e, 0x15, 0x57, 0x52, 0x8c, 0xdc, 0x9d, 0x14, 0x92, 0x41, 0x22, 0x77, 0xfa, 0x8f,
|
||||
0x1c, 0xac, 0xcb, 0x03, 0x86, 0xe8, 0x0c, 0x4a, 0xa9, 0x47, 0x06, 0x3a, 0x78, 0xf0, 0xf1, 0x51,
|
||||
0x35, 0x56, 0x0f, 0xf4, 0xd3, 0xe8, 0x95, 0x86, 0xce, 0xa1, 0x9c, 0x7e, 0x46, 0xa0, 0xf4, 0x78,
|
||||
0xb8, 0xe2, 0x7d, 0xf1, 0xa0, 0xad, 0xb7, 0xa0, 0x5b, 0x11, 0x77, 0xc7, 0x62, 0x1c, 0x8c, 0x07,
|
||||
0x74, 0x54, 0x4d, 0xe9, 0x2f, 0x4d, 0xfd, 0xd5, 0xbd, 0x95, 0xb2, 0x38, 0x43, 0x2d, 0xf5, 0x89,
|
||||
0xf1, 0x88, 0x7c, 0xe7, 0x13, 0x17, 0xe7, 0xf2, 0xea, 0x17, 0xf7, 0x89, 0x63, 0x6b, 0x43, 0xd8,
|
||||
0x5e, 0xc1, 0xa1, 0xe8, 0x57, 0xe9, 0x13, 0xdc, 0xcb, 0xc0, 0xd5, 0xe7, 0xbf, 0xa4, 0x36, 0xf7,
|
||||
0xb2, 0x82, 0x6c, 0x17, 0xbc, 0xdc, 0x4f, 0xd5, 0x0b, 0x5e, 0x1e, 0xe2, 0xec, 0x9f, 0x40, 0x5f,
|
||||
0x6e, 0x74, 0x64, 0x2e, 0xef, 0xbd, 0x4b, 0x3a, 0xd5, 0xa7, 0x0f, 0xea, 0xc4, 0xc6, 0x6d, 0x80,
|
||||
0x79, 0xbb, 0xa0, 0xfd, 0xd4, 0x96, 0x3b, 0xed, 0x5e, 0x3d, 0xb8, 0x47, 0xaa, 0x4c, 0xbd, 0xf9,
|
||||
0xf5, 0xef, 0x4f, 0xae, 0x5d, 0x3e, 0x9a, 0x5e, 0x1e, 0x0f, 0x82, 0xf1, 0x89, 0x27, 0x86, 0x6a,
|
||||
0xdf, 0xf5, 0xaf, 0x7d, 0xc6, 0xff, 0x10, 0x84, 0x37, 0x27, 0x9e, 0x3f, 0x3c, 0x91, 0x5d, 0x77,
|
||||
0x32, 0xb3, 0x72, 0xb9, 0x2e, 0xff, 0x91, 0xf2, 0x9b, 0xff, 0x05, 0x00, 0x00, 0xff, 0xff, 0x1f,
|
||||
0xb3, 0x05, 0x24, 0x78, 0x11, 0x00, 0x00,
|
||||
0x56, 0x3e, 0x43, 0x72, 0xcd, 0x57, 0x48, 0x0e, 0xc9, 0x29, 0xdf, 0x29, 0xa9, 0xca, 0x3d, 0xf7,
|
||||
0x54, 0x77, 0xcf, 0xc0, 0x80, 0x90, 0x36, 0x27, 0xa6, 0x7f, 0xef, 0x75, 0xbf, 0xee, 0xf7, 0xe7,
|
||||
0xd7, 0xaf, 0x81, 0xdd, 0x30, 0x98, 0x72, 0x16, 0x86, 0x93, 0xc1, 0x89, 0xfa, 0x3a, 0x9e, 0x84,
|
||||
0x01, 0x0f, 0x50, 0x71, 0x86, 0x57, 0x8b, 0xe1, 0x64, 0xa0, 0x50, 0xf3, 0x3f, 0x59, 0x40, 0x5d,
|
||||
0xe6, 0x0f, 0x2f, 0xe8, 0xed, 0x98, 0xf9, 0x1c, 0xb3, 0x9f, 0xa7, 0x2c, 0xe2, 0x08, 0x41, 0x76,
|
||||
0xc8, 0x22, 0x6e, 0x68, 0x87, 0xda, 0x51, 0x19, 0xcb, 0x6f, 0xa4, 0x43, 0x86, 0x8e, 0xb9, 0xb1,
|
||||
0x76, 0xa8, 0x1d, 0x65, 0xb0, 0xf8, 0x44, 0x8f, 0xa1, 0x40, 0xc7, 0x9c, 0x8c, 0x23, 0xca, 0x8d,
|
||||
0xb2, 0x84, 0xf3, 0x74, 0xcc, 0xdb, 0x11, 0xe5, 0xe8, 0x4b, 0x28, 0x4f, 0xd4, 0x92, 0x64, 0x44,
|
||||
0xa3, 0x91, 0x91, 0x91, 0x0b, 0x95, 0x62, 0xec, 0x8c, 0x46, 0x23, 0x74, 0x04, 0xfa, 0x95, 0xeb,
|
||||
0x53, 0x8f, 0x0c, 0x3c, 0xfe, 0x91, 0x0c, 0x99, 0xc7, 0xa9, 0x91, 0x3d, 0xd4, 0x8e, 0x72, 0x78,
|
||||
0x43, 0xe2, 0x75, 0x8f, 0x7f, 0x6c, 0x08, 0x14, 0x7d, 0x05, 0x9b, 0xc9, 0x62, 0xa1, 0xda, 0xa0,
|
||||
0x91, 0x3b, 0xd4, 0x8e, 0x8a, 0x78, 0x63, 0xb2, 0xb8, 0xed, 0xaf, 0x60, 0x93, 0xbb, 0x63, 0x16,
|
||||
0x4c, 0x39, 0x89, 0xd8, 0x20, 0xf0, 0x87, 0x91, 0xb1, 0xae, 0x56, 0x8c, 0xe1, 0xae, 0x42, 0x91,
|
||||
0x09, 0x95, 0x2b, 0xc6, 0x88, 0xe7, 0x8e, 0x5d, 0x4e, 0xc4, 0xf6, 0xf3, 0x72, 0xfb, 0xa5, 0x2b,
|
||||
0xc6, 0x5a, 0x02, 0xeb, 0x52, 0x8e, 0x9e, 0xc1, 0xc6, 0x5c, 0x47, 0x9e, 0xb1, 0x22, 0x95, 0xca,
|
||||
0x89, 0x92, 0x3c, 0xe8, 0x4b, 0xd0, 0x83, 0x29, 0xbf, 0x0e, 0x5c, 0xff, 0x9a, 0x0c, 0x46, 0xd4,
|
||||
0x27, 0xee, 0xd0, 0x28, 0x1c, 0x6a, 0x47, 0xd9, 0x37, 0x6b, 0xaf, 0x34, 0xbc, 0x91, 0xc8, 0xea,
|
||||
0x23, 0xea, 0xdb, 0x43, 0xf4, 0x1c, 0x36, 0x3d, 0x1a, 0x71, 0x32, 0x0a, 0x26, 0x64, 0x32, 0xbd,
|
||||
0xbc, 0x61, 0xb7, 0xc6, 0x86, 0xf4, 0x4c, 0x45, 0xc0, 0x67, 0xc1, 0xe4, 0x42, 0x82, 0xe8, 0x00,
|
||||
0x40, 0x7a, 0x45, 0x1a, 0x37, 0x8a, 0xf2, 0x0c, 0x45, 0x81, 0x48, 0xc3, 0xe8, 0x14, 0x4a, 0x32,
|
||||
0x9a, 0x64, 0xe4, 0xfa, 0x3c, 0x32, 0xe0, 0x30, 0x73, 0x54, 0x3a, 0xd5, 0x8f, 0x3d, 0x5f, 0x04,
|
||||
0x16, 0x0b, 0xc9, 0x99, 0xeb, 0x73, 0x9c, 0x56, 0x42, 0x16, 0x14, 0x44, 0x18, 0x09, 0xf7, 0x3e,
|
||||
0x1a, 0x25, 0x39, 0xe1, 0xc5, 0xf1, 0x2c, 0x25, 0x8e, 0xef, 0xe6, 0xc0, 0x71, 0x83, 0x45, 0xbc,
|
||||
0xe7, 0x7d, 0xb4, 0x7c, 0x1e, 0xde, 0xe2, 0xfc, 0x50, 0x8d, 0xaa, 0xdf, 0x41, 0x39, 0x2d, 0x10,
|
||||
0x59, 0x21, 0x4e, 0x21, 0x12, 0x25, 0x8b, 0xc5, 0x27, 0xda, 0x81, 0xdc, 0x47, 0xea, 0x4d, 0x99,
|
||||
0xcc, 0x94, 0x32, 0x56, 0x83, 0xef, 0xd6, 0x5e, 0x6b, 0xe6, 0x6b, 0xd8, 0xee, 0x85, 0x74, 0x70,
|
||||
0xb3, 0x94, 0x6c, 0xcb, 0xb9, 0xa2, 0xdd, 0xc9, 0x15, 0xf3, 0x6f, 0x1a, 0x54, 0xe2, 0x59, 0x5d,
|
||||
0x4e, 0xf9, 0x34, 0x42, 0x5f, 0x43, 0x2e, 0xe2, 0x94, 0x33, 0xa9, 0xbd, 0x71, 0xfa, 0x28, 0x75,
|
||||
0x96, 0x94, 0x22, 0xc3, 0x4a, 0x0b, 0x55, 0xa1, 0x30, 0x09, 0x99, 0x3b, 0xa6, 0xd7, 0xc9, 0xbe,
|
||||
0x66, 0x63, 0x64, 0x42, 0x4e, 0x4e, 0x96, 0x49, 0x5a, 0x3a, 0x2d, 0xa7, 0xfd, 0x88, 0x95, 0x08,
|
||||
0x1d, 0x41, 0x6e, 0xc4, 0xbd, 0x41, 0x64, 0x64, 0xa5, 0xeb, 0x50, 0xac, 0x73, 0xd6, 0x6b, 0xd5,
|
||||
0x6b, 0x9c, 0xb3, 0xf1, 0x84, 0x63, 0xa5, 0x60, 0xfe, 0x16, 0x36, 0xe5, 0xcc, 0x26, 0x63, 0x0f,
|
||||
0x55, 0xd3, 0x23, 0x10, 0xb5, 0x22, 0x73, 0x4f, 0x55, 0xd4, 0x3a, 0x1d, 0x8b, 0xb4, 0x33, 0x87,
|
||||
0xa0, 0xcf, 0xe7, 0x47, 0x93, 0xc0, 0x8f, 0x84, 0x75, 0x5d, 0x6c, 0x43, 0xe4, 0x98, 0x48, 0x49,
|
||||
0x99, 0x8c, 0x9a, 0x9c, 0xb5, 0x11, 0xe3, 0x4d, 0xc6, 0x64, 0x3a, 0x3e, 0x57, 0x15, 0x40, 0xbc,
|
||||
0x60, 0x70, 0x23, 0x6a, 0x8a, 0xde, 0xc6, 0xcb, 0x57, 0x04, 0xdc, 0x0a, 0x06, 0x37, 0x0d, 0x01,
|
||||
0x9a, 0x3f, 0xa9, 0xb2, 0xef, 0x05, 0xea, 0x94, 0xff, 0x77, 0x24, 0xe6, 0xce, 0x5a, 0xbb, 0xd7,
|
||||
0x59, 0x26, 0x81, 0xed, 0x85, 0xc5, 0xe3, 0x53, 0xa4, 0x63, 0xa0, 0x2d, 0xc5, 0xe0, 0x25, 0xe4,
|
||||
0xaf, 0xa8, 0xeb, 0x4d, 0xc3, 0x64, 0x61, 0x94, 0x0a, 0x68, 0x53, 0x49, 0x70, 0xa2, 0x62, 0xfe,
|
||||
0x37, 0x0f, 0xf9, 0x18, 0x44, 0xa7, 0x90, 0x1d, 0x04, 0xc3, 0x24, 0x0f, 0xbe, 0xb8, 0x3b, 0x2d,
|
||||
0xf9, 0xad, 0x07, 0x43, 0x86, 0xa5, 0x2e, 0xfa, 0x1d, 0x6c, 0x88, 0x5a, 0xf5, 0x99, 0x47, 0xa6,
|
||||
0x93, 0x21, 0x9d, 0x85, 0xde, 0x48, 0xcd, 0xae, 0x2b, 0x85, 0xbe, 0x94, 0xe3, 0xca, 0x20, 0x3d,
|
||||
0x44, 0x7b, 0x50, 0x14, 0xd1, 0x56, 0x91, 0xc8, 0xca, 0xdc, 0x2f, 0x08, 0x40, 0xc6, 0xc0, 0x84,
|
||||
0x4a, 0xe0, 0xbb, 0x81, 0x4f, 0xa2, 0x11, 0x25, 0xa7, 0xdf, 0x7c, 0x2b, 0xc9, 0xaa, 0x8c, 0x4b,
|
||||
0x12, 0xec, 0x8e, 0xe8, 0xe9, 0x37, 0xdf, 0xa2, 0x27, 0x50, 0x92, 0x05, 0xce, 0x3e, 0x4d, 0xdc,
|
||||
0xf0, 0x56, 0xb2, 0x54, 0x05, 0xcb, 0x9a, 0xb7, 0x24, 0x22, 0xaa, 0xe8, 0xca, 0xa3, 0xd7, 0x91,
|
||||
0x64, 0xa6, 0x0a, 0x56, 0x03, 0xf4, 0x0a, 0x76, 0x62, 0x1f, 0x90, 0x28, 0x98, 0x86, 0x03, 0x46,
|
||||
0x5c, 0x7f, 0xc8, 0x3e, 0x49, 0xc6, 0xa9, 0x60, 0x14, 0xcb, 0xba, 0x52, 0x64, 0x0b, 0x09, 0xda,
|
||||
0x85, 0xf5, 0x11, 0x73, 0xaf, 0x47, 0x8a, 0x45, 0x2a, 0x38, 0x1e, 0x99, 0x7f, 0xcf, 0x41, 0x29,
|
||||
0xe5, 0x18, 0x54, 0x86, 0x02, 0xb6, 0xba, 0x16, 0x7e, 0x6f, 0x35, 0xf4, 0xcf, 0xd0, 0x11, 0x3c,
|
||||
0xb3, 0x9d, 0x7a, 0x07, 0x63, 0xab, 0xde, 0x23, 0x1d, 0x4c, 0xfa, 0xce, 0x5b, 0xa7, 0xf3, 0x83,
|
||||
0x43, 0x2e, 0x6a, 0x1f, 0xda, 0x96, 0xd3, 0x23, 0x0d, 0xab, 0x57, 0xb3, 0x5b, 0x5d, 0x5d, 0x43,
|
||||
0xfb, 0x60, 0xcc, 0x35, 0x13, 0x71, 0xad, 0xdd, 0xe9, 0x3b, 0x3d, 0x7d, 0x0d, 0x3d, 0x81, 0xbd,
|
||||
0xa6, 0xed, 0xd4, 0x5a, 0x64, 0xae, 0x53, 0x6f, 0xf5, 0xde, 0x13, 0xeb, 0xc7, 0x0b, 0x1b, 0x7f,
|
||||
0xd0, 0x33, 0xab, 0x14, 0x44, 0x4d, 0x25, 0x2b, 0x64, 0xd1, 0x63, 0xf8, 0x5c, 0x29, 0xa8, 0x29,
|
||||
0xa4, 0xd7, 0xe9, 0x90, 0x6e, 0xa7, 0xe3, 0xe8, 0x39, 0xb4, 0x05, 0x15, 0xdb, 0x79, 0x5f, 0x6b,
|
||||
0xd9, 0x0d, 0x82, 0xad, 0x5a, 0xab, 0xad, 0xaf, 0xa3, 0x6d, 0xd8, 0x5c, 0xd6, 0xcb, 0x8b, 0x25,
|
||||
0x12, 0xbd, 0x8e, 0x63, 0x77, 0x1c, 0xf2, 0xde, 0xc2, 0x5d, 0xbb, 0xe3, 0xe8, 0x05, 0xb4, 0x0b,
|
||||
0x68, 0x51, 0x74, 0xd6, 0xae, 0xd5, 0xf5, 0x22, 0xfa, 0x1c, 0xb6, 0x16, 0xf1, 0xb7, 0xd6, 0x07,
|
||||
0x1d, 0x90, 0x01, 0x3b, 0x6a, 0x63, 0xe4, 0x8d, 0xd5, 0xea, 0xfc, 0x40, 0xda, 0xb6, 0x63, 0xb7,
|
||||
0xfb, 0x6d, 0xbd, 0x84, 0x76, 0x40, 0x6f, 0x5a, 0x16, 0xb1, 0x9d, 0x6e, 0xbf, 0xd9, 0xb4, 0xeb,
|
||||
0xb6, 0xe5, 0xf4, 0xf4, 0xb2, 0xb2, 0xbc, 0xea, 0xe0, 0x15, 0x31, 0xa1, 0x7e, 0x56, 0x73, 0x1c,
|
||||
0xab, 0x45, 0x1a, 0x76, 0xb7, 0xf6, 0xa6, 0x65, 0x35, 0xf4, 0x0d, 0x74, 0x00, 0x8f, 0x7b, 0x56,
|
||||
0xfb, 0xa2, 0x83, 0x6b, 0xf8, 0x03, 0x49, 0xe4, 0xcd, 0x9a, 0xdd, 0xea, 0x63, 0x4b, 0xdf, 0x44,
|
||||
0x5f, 0xc2, 0x01, 0xb6, 0xde, 0xf5, 0x6d, 0x6c, 0x35, 0x88, 0xd3, 0x69, 0x58, 0xa4, 0x69, 0xd5,
|
||||
0x7a, 0x7d, 0x6c, 0x91, 0xb6, 0xdd, 0xed, 0xda, 0xce, 0xf7, 0xba, 0x8e, 0x9e, 0xc1, 0xe1, 0x4c,
|
||||
0x65, 0xb6, 0xc0, 0x92, 0xd6, 0x96, 0x38, 0x5f, 0x12, 0x52, 0xc7, 0xfa, 0xb1, 0x47, 0x2e, 0x2c,
|
||||
0x0b, 0xeb, 0x08, 0x55, 0x61, 0x77, 0x6e, 0x5e, 0x19, 0x88, 0x6d, 0x6f, 0x0b, 0xd9, 0x85, 0x85,
|
||||
0xdb, 0x35, 0x47, 0x04, 0x78, 0x41, 0xb6, 0x23, 0xb6, 0x3d, 0x97, 0x2d, 0x6f, 0xfb, 0x73, 0x84,
|
||||
0x60, 0x23, 0x15, 0x95, 0x66, 0x0d, 0xeb, 0xbb, 0x68, 0x07, 0x36, 0x93, 0x1d, 0x24, 0x8a, 0xff,
|
||||
0xca, 0xa3, 0x47, 0x80, 0xfa, 0x0e, 0xb6, 0x6a, 0x0d, 0xe1, 0x90, 0x99, 0xe0, 0xdf, 0xf9, 0xf3,
|
||||
0x6c, 0x61, 0x4d, 0xcf, 0x98, 0xff, 0xcc, 0x40, 0x65, 0xa1, 0x2e, 0xd1, 0x3e, 0x14, 0x23, 0xf7,
|
||||
0xda, 0xa7, 0x5c, 0x30, 0x87, 0x22, 0x95, 0x39, 0x20, 0xaf, 0xd1, 0x11, 0x75, 0x7d, 0xc5, 0x66,
|
||||
0x8a, 0xf7, 0x8b, 0x12, 0x91, 0x5c, 0xb6, 0x07, 0xf9, 0xe4, 0xca, 0xce, 0xcc, 0xae, 0xec, 0xf5,
|
||||
0x81, 0xba, 0xaa, 0xf7, 0xa1, 0x28, 0x28, 0x33, 0xe2, 0x74, 0x3c, 0x91, 0x25, 0x5e, 0xc1, 0x73,
|
||||
0x00, 0x3d, 0x85, 0xca, 0x98, 0x45, 0x11, 0xbd, 0x66, 0x44, 0x95, 0x29, 0x48, 0x8d, 0x72, 0x0c,
|
||||
0x36, 0x65, 0xb5, 0x3e, 0x85, 0x84, 0x36, 0x62, 0xa5, 0x9c, 0x52, 0x8a, 0x41, 0xa5, 0xb4, 0xcc,
|
||||
0xd8, 0x9c, 0xc6, 0x6c, 0x90, 0x66, 0x6c, 0x4e, 0xd1, 0x0b, 0xd8, 0x52, 0x94, 0xe3, 0xfa, 0xee,
|
||||
0x78, 0x3a, 0x56, 0xd4, 0x93, 0x97, 0xd4, 0xb3, 0x29, 0xa9, 0x47, 0xe1, 0x92, 0x81, 0x1e, 0x43,
|
||||
0xe1, 0x92, 0x46, 0x4c, 0x5c, 0x16, 0x31, 0x35, 0xe4, 0xc5, 0xb8, 0xc9, 0x98, 0x10, 0x89, 0x2b,
|
||||
0x24, 0x14, 0xa4, 0xa7, 0x18, 0x21, 0x7f, 0xc5, 0x18, 0x16, 0xbe, 0x9c, 0x59, 0xa0, 0x9f, 0xe6,
|
||||
0x16, 0x4a, 0x29, 0x0b, 0x0a, 0x97, 0x16, 0x5e, 0xc0, 0x16, 0xfb, 0xc4, 0x43, 0x4a, 0x82, 0x09,
|
||||
0xfd, 0x79, 0xca, 0xc8, 0x90, 0x72, 0x2a, 0x7b, 0xc0, 0x32, 0xde, 0x94, 0x82, 0x8e, 0xc4, 0x1b,
|
||||
0x94, 0x53, 0x73, 0x1f, 0xaa, 0x98, 0x45, 0x8c, 0xb7, 0xdd, 0x28, 0x72, 0x03, 0xbf, 0x1e, 0xf8,
|
||||
0x3c, 0x0c, 0xbc, 0xf8, 0xce, 0x31, 0x0f, 0x60, 0x6f, 0xa5, 0x54, 0x5d, 0x1a, 0x62, 0xf2, 0xbb,
|
||||
0x29, 0x0b, 0x6f, 0x57, 0x4f, 0x7e, 0x07, 0x7b, 0x2b, 0xa5, 0xf1, 0x8d, 0xf3, 0x12, 0x72, 0x13,
|
||||
0xea, 0x86, 0x91, 0xb1, 0x26, 0x6f, 0xed, 0xdd, 0x85, 0x26, 0xc1, 0x0d, 0xcf, 0xdc, 0x88, 0x07,
|
||||
0xe1, 0x2d, 0x56, 0x4a, 0xe7, 0xd9, 0x82, 0xa6, 0xaf, 0x99, 0x7f, 0xd2, 0xa0, 0x94, 0x12, 0x8a,
|
||||
0x3c, 0xf0, 0x83, 0x21, 0x23, 0x57, 0x61, 0x30, 0x4e, 0x32, 0x6c, 0x06, 0x20, 0x03, 0xf2, 0x72,
|
||||
0xc0, 0x83, 0x38, 0xbd, 0x92, 0x21, 0xfa, 0x1a, 0xf2, 0x23, 0xb5, 0x84, 0x8c, 0x52, 0xe9, 0x74,
|
||||
0x7b, 0xc9, 0xba, 0xf0, 0x0d, 0x4e, 0x74, 0xce, 0xb3, 0x85, 0x8c, 0x9e, 0x3d, 0xcf, 0x16, 0xb2,
|
||||
0x7a, 0xee, 0x3c, 0x5b, 0xc8, 0xe9, 0xeb, 0xe7, 0xd9, 0xc2, 0xba, 0x9e, 0x37, 0xff, 0xa2, 0x41,
|
||||
0x21, 0xd1, 0x5e, 0xcc, 0x49, 0xd5, 0x00, 0xa4, 0x72, 0xf2, 0x14, 0x76, 0xc6, 0xae, 0x4f, 0x26,
|
||||
0xcc, 0xa7, 0x9e, 0xfb, 0x47, 0x46, 0x16, 0xfb, 0x8b, 0x95, 0x32, 0xf4, 0x1a, 0x1e, 0xc9, 0x86,
|
||||
0x94, 0xaa, 0x26, 0x86, 0x44, 0xd3, 0xc1, 0x80, 0x45, 0xd1, 0xd5, 0xd4, 0x93, 0x25, 0x51, 0xc0,
|
||||
0xf7, 0x89, 0xcd, 0x31, 0x3c, 0x92, 0xae, 0xbf, 0x08, 0x83, 0x4b, 0x7a, 0xe9, 0x7a, 0x2e, 0xbf,
|
||||
0x4d, 0xda, 0x88, 0x7d, 0x28, 0x0a, 0xe7, 0x10, 0x3f, 0xb9, 0x97, 0xcb, 0x78, 0x0e, 0x08, 0x97,
|
||||
0xf1, 0x40, 0xc9, 0x62, 0x97, 0xc5, 0x43, 0xd1, 0x20, 0xcc, 0xde, 0x13, 0x19, 0xb9, 0xe9, 0xd9,
|
||||
0xd8, 0xbc, 0x01, 0xe3, 0xae, 0xb9, 0x38, 0xcc, 0x87, 0x50, 0x9a, 0xcc, 0x61, 0x69, 0x51, 0xc3,
|
||||
0x69, 0x28, 0x1d, 0x8c, 0xb5, 0x5f, 0x0e, 0x86, 0xf9, 0x57, 0x0d, 0xb6, 0xde, 0x4c, 0x5d, 0x6f,
|
||||
0xb8, 0xd0, 0x1d, 0xa5, 0x9f, 0x3b, 0xda, 0xe2, 0x73, 0x67, 0xd5, 0x5b, 0x66, 0x6d, 0xe5, 0x5b,
|
||||
0x66, 0xd5, 0x7b, 0x21, 0x73, 0xef, 0x7b, 0xe1, 0x09, 0x94, 0xe6, 0x4f, 0x05, 0xd5, 0x7c, 0x96,
|
||||
0x31, 0x8c, 0x92, 0x77, 0x42, 0x64, 0xbe, 0x06, 0x94, 0xde, 0x68, 0xec, 0x90, 0x59, 0x93, 0xa6,
|
||||
0xdd, 0xdb, 0xa4, 0xbd, 0xf8, 0xb3, 0x06, 0xe5, 0x74, 0xa7, 0x8c, 0x2a, 0x50, 0xb4, 0x1d, 0xd2,
|
||||
0x6c, 0xd9, 0xdf, 0x9f, 0xf5, 0xf4, 0xcf, 0xc4, 0xb0, 0xdb, 0xaf, 0xd7, 0x2d, 0xab, 0x61, 0x35,
|
||||
0x74, 0x4d, 0x70, 0xb8, 0xa0, 0x63, 0xab, 0x41, 0x7a, 0x76, 0xdb, 0xea, 0xf4, 0xc5, 0xed, 0xbe,
|
||||
0x0d, 0x9b, 0x31, 0xe6, 0x74, 0x08, 0xee, 0xf4, 0x7b, 0x96, 0x9e, 0x41, 0x3a, 0x94, 0x63, 0xd0,
|
||||
0xc2, 0xb8, 0x83, 0xf5, 0xac, 0xb8, 0x92, 0x62, 0xe4, 0x6e, 0xa7, 0x90, 0x34, 0x12, 0xb9, 0xd3,
|
||||
0x7f, 0xe4, 0x60, 0x5d, 0x6e, 0x30, 0x44, 0x67, 0x50, 0x4a, 0xbd, 0x47, 0xd0, 0xc1, 0x83, 0xef,
|
||||
0x94, 0xaa, 0xb1, 0xba, 0xf5, 0x9f, 0x46, 0xaf, 0x34, 0x74, 0x0e, 0xe5, 0xf4, 0x8b, 0x03, 0xa5,
|
||||
0xdb, 0xc3, 0x15, 0x4f, 0x91, 0x07, 0xd7, 0x7a, 0x0b, 0xba, 0x15, 0x71, 0x77, 0x2c, 0xda, 0xc1,
|
||||
0xb8, 0x41, 0x47, 0xd5, 0x94, 0xfe, 0x52, 0xd7, 0x5f, 0xdd, 0x5b, 0x29, 0x8b, 0x23, 0xd4, 0x52,
|
||||
0x47, 0x8c, 0x5b, 0xe4, 0x3b, 0x47, 0x5c, 0xec, 0xcb, 0xab, 0x5f, 0xdc, 0x27, 0x8e, 0x57, 0x1b,
|
||||
0xc2, 0xf6, 0x0a, 0x0e, 0x45, 0xbf, 0x4a, 0xef, 0xe0, 0x5e, 0x06, 0xae, 0x3e, 0xff, 0x25, 0xb5,
|
||||
0xb9, 0x95, 0x15, 0x64, 0xbb, 0x60, 0xe5, 0x7e, 0xaa, 0x5e, 0xb0, 0xf2, 0x10, 0x67, 0xff, 0x04,
|
||||
0xfa, 0x72, 0xa1, 0x23, 0x73, 0x79, 0xee, 0x5d, 0xd2, 0xa9, 0x3e, 0x7d, 0x50, 0x27, 0x5e, 0xdc,
|
||||
0x06, 0x98, 0x97, 0x0b, 0xda, 0x4f, 0x4d, 0xb9, 0x53, 0xee, 0xd5, 0x83, 0x7b, 0xa4, 0x6a, 0xa9,
|
||||
0x37, 0xbf, 0xfe, 0xfd, 0xc9, 0xb5, 0xcb, 0x47, 0xd3, 0xcb, 0xe3, 0x41, 0x30, 0x3e, 0xf1, 0x44,
|
||||
0x53, 0xed, 0xbb, 0xfe, 0xb5, 0xcf, 0xf8, 0x1f, 0x82, 0xf0, 0xe6, 0xc4, 0xf3, 0x87, 0x27, 0xb2,
|
||||
0xea, 0x4e, 0x66, 0xab, 0x5c, 0xae, 0xcb, 0xff, 0x5c, 0x7e, 0xf3, 0xbf, 0x00, 0x00, 0x00, 0xff,
|
||||
0xff, 0x78, 0xac, 0xbb, 0x04, 0xa3, 0x11, 0x00, 0x00,
|
||||
}
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
|
|
|
@ -155,6 +155,11 @@ message PaymentStatus {
|
|||
The taken route when state is SUCCEEDED.
|
||||
*/
|
||||
lnrpc.Route route = 3;
|
||||
|
||||
/**
|
||||
The HTLCs made in attempt to settle the payment [EXPERIMENTAL].
|
||||
*/
|
||||
repeated lnrpc.HTLCAttempt htlcs = 4;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -12,6 +12,7 @@ import (
|
|||
|
||||
"github.com/btcsuite/btcd/chaincfg"
|
||||
"github.com/btcsuite/btcutil"
|
||||
"github.com/lightningnetwork/lnd/channeldb"
|
||||
"github.com/lightningnetwork/lnd/lnrpc"
|
||||
"github.com/lightningnetwork/lnd/lntypes"
|
||||
"github.com/lightningnetwork/lnd/lnwire"
|
||||
|
@ -779,3 +780,48 @@ func UnmarshalMPP(reqMPP *lnrpc.MPPRecord) (*record.MPP, error) {
|
|||
|
||||
return record.NewMPP(total, addr), nil
|
||||
}
|
||||
|
||||
// MarshalHTLCAttempt constructs an RPC HTLCAttempt from the db representation.
|
||||
func (r *RouterBackend) MarshalHTLCAttempt(
|
||||
htlc channeldb.HTLCAttempt) (*lnrpc.HTLCAttempt, error) {
|
||||
|
||||
var (
|
||||
status lnrpc.HTLCAttempt_HTLCStatus
|
||||
resolveTime int64
|
||||
)
|
||||
|
||||
switch {
|
||||
case htlc.Settle != nil:
|
||||
status = lnrpc.HTLCAttempt_SUCCEEDED
|
||||
resolveTime = MarshalTimeNano(htlc.Settle.SettleTime)
|
||||
|
||||
case htlc.Failure != nil:
|
||||
status = lnrpc.HTLCAttempt_FAILED
|
||||
resolveTime = MarshalTimeNano(htlc.Failure.FailTime)
|
||||
|
||||
default:
|
||||
status = lnrpc.HTLCAttempt_IN_FLIGHT
|
||||
}
|
||||
|
||||
route, err := r.MarshallRoute(&htlc.Route)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &lnrpc.HTLCAttempt{
|
||||
Status: status,
|
||||
Route: route,
|
||||
AttemptTimeNs: MarshalTimeNano(htlc.AttemptTime),
|
||||
ResolveTimeNs: resolveTime,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// MarshalTimeNano converts a time.Time into its nanosecond representation. If
|
||||
// the time is zero, this method simply returns 0, since calling UnixNano() on a
|
||||
// zero-valued time is undefined.
|
||||
func MarshalTimeNano(t time.Time) int64 {
|
||||
if t.IsZero() {
|
||||
return 0
|
||||
}
|
||||
return t.UnixNano()
|
||||
}
|
||||
|
|
|
@ -584,19 +584,12 @@ func (s *Server) trackPayment(paymentHash lntypes.Hash,
|
|||
case result := <-resultChan:
|
||||
// Marshall result to rpc type.
|
||||
var status PaymentStatus
|
||||
|
||||
if result.Success {
|
||||
log.Debugf("Payment %v successfully completed",
|
||||
paymentHash)
|
||||
|
||||
status.State = PaymentState_SUCCEEDED
|
||||
status.Preimage = result.Preimage[:]
|
||||
status.Route, err = router.MarshallRoute(
|
||||
result.Route,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
state, err := marshallFailureReason(
|
||||
result.FailureReason,
|
||||
|
@ -605,15 +598,49 @@ func (s *Server) trackPayment(paymentHash lntypes.Hash,
|
|||
return err
|
||||
}
|
||||
status.State = state
|
||||
if result.Route != nil {
|
||||
status.Route, err = router.MarshallRoute(
|
||||
result.Route,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Extract the last route from the given list of HTLCs. This
|
||||
// will populate the legacy route field for backwards
|
||||
// compatibility.
|
||||
//
|
||||
// NOTE: For now there will be at most one HTLC, this code
|
||||
// should be revisted or the field removed when multiple HTLCs
|
||||
// are permitted.
|
||||
var legacyRoute *route.Route
|
||||
for _, htlc := range result.HTLCs {
|
||||
switch {
|
||||
case htlc.Settle != nil:
|
||||
legacyRoute = &htlc.Route
|
||||
|
||||
// Only display the route for failed payments if we got
|
||||
// an incorrect payment details error, so that it can be
|
||||
// used for probing or fee estimation.
|
||||
case htlc.Failure != nil && result.FailureReason ==
|
||||
channeldb.FailureReasonPaymentDetails:
|
||||
|
||||
legacyRoute = &htlc.Route
|
||||
}
|
||||
}
|
||||
if legacyRoute != nil {
|
||||
status.Route, err = router.MarshallRoute(legacyRoute)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Marshal our list of HTLCs that have been tried for this
|
||||
// payment.
|
||||
htlcs := make([]*lnrpc.HTLCAttempt, 0, len(result.HTLCs))
|
||||
for _, dbHtlc := range result.HTLCs {
|
||||
htlc, err := router.MarshalHTLCAttempt(dbHtlc)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
htlcs = append(htlcs, htlc)
|
||||
}
|
||||
status.Htlcs = htlcs
|
||||
|
||||
// Send event to the client.
|
||||
err = stream.Send(&status)
|
||||
|
@ -645,7 +672,7 @@ func marshallFailureReason(reason channeldb.FailureReason) (
|
|||
case channeldb.FailureReasonError:
|
||||
return PaymentState_FAILED_ERROR, nil
|
||||
|
||||
case channeldb.FailureReasonIncorrectPaymentDetails:
|
||||
case channeldb.FailureReasonPaymentDetails:
|
||||
return PaymentState_FAILED_INCORRECT_PAYMENT_DETAILS, nil
|
||||
}
|
||||
|
||||
|
|
1298
lnrpc/rpc.pb.go
1298
lnrpc/rpc.pb.go
File diff suppressed because it is too large
Load diff
|
@ -2478,11 +2478,11 @@ message Payment {
|
|||
/// Deprecated, use value_sat or value_msat.
|
||||
int64 value = 2 [json_name = "value", deprecated = true];
|
||||
|
||||
/// The date of this payment
|
||||
int64 creation_date = 3 [json_name = "creation_date"];
|
||||
/// Deprecated, use creation_time_ns
|
||||
int64 creation_date = 3 [json_name = "creation_date", deprecated = true];
|
||||
|
||||
/// The path this payment took
|
||||
repeated string path = 4 [ json_name = "path" ];
|
||||
/// The path this payment took.
|
||||
repeated string path = 4 [json_name = "path", deprecated = true];
|
||||
|
||||
/// Deprecated, use fee_sat or fee_msat.
|
||||
int64 fee = 5 [json_name = "fee", deprecated = true];
|
||||
|
@ -2514,6 +2514,35 @@ message Payment {
|
|||
|
||||
/// The fee paid for this payment in milli-satoshis
|
||||
int64 fee_msat = 12 [json_name = "fee_msat"];
|
||||
|
||||
/// The time in UNIX nanoseconds at which the payment was created.
|
||||
int64 creation_time_ns = 13 [json_name = "creation_time_ns"];
|
||||
|
||||
/// The HTLCs made in attempt to settle the payment [EXPERIMENTAL].
|
||||
repeated HTLCAttempt htlcs = 14 [json_name = "htlcs"];
|
||||
}
|
||||
|
||||
message HTLCAttempt {
|
||||
enum HTLCStatus {
|
||||
IN_FLIGHT = 0;
|
||||
SUCCEEDED = 1;
|
||||
FAILED = 2;
|
||||
}
|
||||
|
||||
/// The status of the HTLC.
|
||||
HTLCStatus status = 1 [json_name = "status"];
|
||||
|
||||
/// The route taken by this HTLC.
|
||||
Route route = 2 [json_name = "route"];
|
||||
|
||||
/// The time in UNIX nanonseconds at which this HTLC was sent.
|
||||
int64 attempt_time_ns = 3 [json_name = "attempt_time_ns"];
|
||||
|
||||
/**
|
||||
The time in UNIX nanonseconds at which this HTLC was settled or failed.
|
||||
This value will not be set if the HTLC is still IN_FLIGHT.
|
||||
*/
|
||||
int64 resolve_time_ns = 4 [json_name = "resolve_time_ns"];
|
||||
}
|
||||
|
||||
message ListPaymentsRequest {
|
||||
|
|
|
@ -1418,6 +1418,15 @@
|
|||
],
|
||||
"default": "OPEN_CHANNEL"
|
||||
},
|
||||
"HTLCAttemptHTLCStatus": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"IN_FLIGHT",
|
||||
"SUCCEEDED",
|
||||
"FAILED"
|
||||
],
|
||||
"default": "IN_FLIGHT"
|
||||
},
|
||||
"InvoiceInvoiceState": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
|
@ -2511,6 +2520,29 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"lnrpcHTLCAttempt": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"status": {
|
||||
"$ref": "#/definitions/HTLCAttemptHTLCStatus",
|
||||
"description": "/ The status of the HTLC."
|
||||
},
|
||||
"route": {
|
||||
"$ref": "#/definitions/lnrpcRoute",
|
||||
"description": "/ The route taken by this HTLC."
|
||||
},
|
||||
"attempt_time_ns": {
|
||||
"type": "string",
|
||||
"format": "int64",
|
||||
"description": "/ The time in UNIX nanonseconds at which this HTLC was sent."
|
||||
},
|
||||
"resolve_time_ns": {
|
||||
"type": "string",
|
||||
"format": "int64",
|
||||
"description": "*\nThe time in UNIX nanonseconds at which this HTLC was settled or failed.\nThis value will not be set if the HTLC is still IN_FLIGHT."
|
||||
}
|
||||
}
|
||||
},
|
||||
"lnrpcHop": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
@ -3231,14 +3263,14 @@
|
|||
"creation_date": {
|
||||
"type": "string",
|
||||
"format": "int64",
|
||||
"title": "/ The date of this payment"
|
||||
"title": "/ Deprecated, use creation_time_ns"
|
||||
},
|
||||
"path": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"title": "/ The path this payment took"
|
||||
"description": "/ The path this payment took."
|
||||
},
|
||||
"fee": {
|
||||
"type": "string",
|
||||
|
@ -3276,6 +3308,18 @@
|
|||
"type": "string",
|
||||
"format": "int64",
|
||||
"title": "/ The fee paid for this payment in milli-satoshis"
|
||||
},
|
||||
"creation_time_ns": {
|
||||
"type": "string",
|
||||
"format": "int64",
|
||||
"description": "/ The time in UNIX nanoseconds at which the payment was created."
|
||||
},
|
||||
"htlcs": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/lnrpcHTLCAttempt"
|
||||
},
|
||||
"description": "/ The HTLCs made in attempt to settle the payment [EXPERIMENTAL]."
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
@ -4737,6 +4737,50 @@ func testSingleHopSendToRouteCase(net *lntest.NetworkHarness, t *harnessTest,
|
|||
"want: %d, got: %d",
|
||||
i, paymentAmtSat, p.ValueSat)
|
||||
}
|
||||
|
||||
// Assert exactly one htlc was made.
|
||||
if len(p.Htlcs) != 1 {
|
||||
t.Fatalf("expected 1 htlc for payment %d, got: %d",
|
||||
i, len(p.Htlcs))
|
||||
}
|
||||
|
||||
// Assert the htlc's route is populated.
|
||||
htlc := p.Htlcs[0]
|
||||
if htlc.Route == nil {
|
||||
t.Fatalf("expected route for payment %d", i)
|
||||
}
|
||||
|
||||
// Assert the hop has exactly one hop.
|
||||
if len(htlc.Route.Hops) != 1 {
|
||||
t.Fatalf("expected 1 hop for payment %d, got: %d",
|
||||
i, len(htlc.Route.Hops))
|
||||
}
|
||||
|
||||
// If this is an MPP test, assert the MPP record's fields are
|
||||
// properly populated. Otherwise the hop should not have an MPP
|
||||
// record.
|
||||
hop := htlc.Route.Hops[0]
|
||||
if test.mpp {
|
||||
if hop.MppRecord == nil {
|
||||
t.Fatalf("expected mpp record for mpp payment")
|
||||
}
|
||||
|
||||
if hop.MppRecord.TotalAmtMsat != paymentAmtSat*1000 {
|
||||
t.Fatalf("incorrect mpp total msat for payment %d "+
|
||||
"want: %d, got: %d",
|
||||
i, paymentAmtSat*1000,
|
||||
hop.MppRecord.TotalAmtMsat)
|
||||
}
|
||||
|
||||
expAddr := [32]byte{byte(i)}
|
||||
if !bytes.Equal(hop.MppRecord.PaymentAddr, expAddr[:]) {
|
||||
t.Fatalf("incorrect mpp payment addr for payment %d "+
|
||||
"want: %x, got: %x",
|
||||
i, expAddr, hop.MppRecord.PaymentAddr)
|
||||
}
|
||||
} else if hop.MppRecord != nil {
|
||||
t.Fatalf("unexpected mpp record for non-mpp payment")
|
||||
}
|
||||
}
|
||||
|
||||
// Verify that the invoices's from Dave's PoV have the correct payment
|
||||
|
|
|
@ -6,7 +6,6 @@ import (
|
|||
|
||||
"github.com/lightningnetwork/lnd/channeldb"
|
||||
"github.com/lightningnetwork/lnd/lntypes"
|
||||
"github.com/lightningnetwork/lnd/routing/route"
|
||||
)
|
||||
|
||||
// ControlTower tracks all outgoing payments made, whose primary purpose is to
|
||||
|
@ -52,17 +51,17 @@ type PaymentResult struct {
|
|||
// Success indicates whether the payment was successful.
|
||||
Success bool
|
||||
|
||||
// Route is the (last) route attempted to send the HTLC. It is only set
|
||||
// for successful payments.
|
||||
Route *route.Route
|
||||
|
||||
// PaymentPreimage is the preimage of a successful payment. This serves
|
||||
// as a proof of payment. It is only set for successful payments.
|
||||
// Preimage is the preimage of a successful payment. This serves as a
|
||||
// proof of payment. It is only set for successful payments.
|
||||
Preimage lntypes.Preimage
|
||||
|
||||
// Failure is a failure reason code indicating the reason the payment
|
||||
// failed. It is only set for failed payments.
|
||||
// FailureReason is a failure reason code indicating the reason the
|
||||
// payment failed. It is only set for failed payments.
|
||||
FailureReason channeldb.FailureReason
|
||||
|
||||
// HTLCs is a list of HTLCs that have been attempted in order to settle
|
||||
// the payment.
|
||||
HTLCs []channeldb.HTLCAttempt
|
||||
}
|
||||
|
||||
// controlTower is persistent implementation of ControlTower to restrict
|
||||
|
@ -107,46 +106,46 @@ func (p *controlTower) RegisterAttempt(paymentHash lntypes.Hash,
|
|||
func (p *controlTower) Success(paymentHash lntypes.Hash,
|
||||
preimage lntypes.Preimage) error {
|
||||
|
||||
route, err := p.db.Success(paymentHash, preimage)
|
||||
payment, err := p.db.Success(paymentHash, preimage)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Notify subscribers of success event.
|
||||
p.notifyFinalEvent(
|
||||
paymentHash, createSuccessResult(route, preimage),
|
||||
paymentHash, createSuccessResult(payment.HTLCs),
|
||||
)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// createSuccessResult creates a success result to send to subscribers.
|
||||
func createSuccessResult(rt *route.Route,
|
||||
preimage lntypes.Preimage) *PaymentResult {
|
||||
func createSuccessResult(htlcs []channeldb.HTLCAttempt) *PaymentResult {
|
||||
// Extract any preimage from the list of HTLCs.
|
||||
var preimage lntypes.Preimage
|
||||
for _, htlc := range htlcs {
|
||||
if htlc.Settle != nil {
|
||||
preimage = htlc.Settle.Preimage
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return &PaymentResult{
|
||||
Success: true,
|
||||
Preimage: preimage,
|
||||
Route: rt,
|
||||
HTLCs: htlcs,
|
||||
}
|
||||
}
|
||||
|
||||
// createFailResult creates a failed result to send to subscribers.
|
||||
func createFailedResult(rt *route.Route,
|
||||
func createFailedResult(htlcs []channeldb.HTLCAttempt,
|
||||
reason channeldb.FailureReason) *PaymentResult {
|
||||
|
||||
result := &PaymentResult{
|
||||
return &PaymentResult{
|
||||
Success: false,
|
||||
FailureReason: reason,
|
||||
HTLCs: htlcs,
|
||||
}
|
||||
|
||||
// In case of incorrect payment details, set the route. This can be used
|
||||
// for probing and to extract a fee estimate from the route.
|
||||
if reason == channeldb.FailureReasonIncorrectPaymentDetails {
|
||||
result.Route = rt
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// Fail transitions a payment into the Failed state, and records the reason the
|
||||
|
@ -156,14 +155,16 @@ func createFailedResult(rt *route.Route,
|
|||
func (p *controlTower) Fail(paymentHash lntypes.Hash,
|
||||
reason channeldb.FailureReason) error {
|
||||
|
||||
route, err := p.db.Fail(paymentHash, reason)
|
||||
payment, err := p.db.Fail(paymentHash, reason)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Notify subscribers of fail event.
|
||||
p.notifyFinalEvent(
|
||||
paymentHash, createFailedResult(route, reason),
|
||||
paymentHash, createFailedResult(
|
||||
payment.HTLCs, reason,
|
||||
),
|
||||
)
|
||||
|
||||
return nil
|
||||
|
@ -213,20 +214,14 @@ func (p *controlTower) SubscribePayment(paymentHash lntypes.Hash) (
|
|||
// a subscriber, because we can send the result on the channel
|
||||
// immediately.
|
||||
case channeldb.StatusSucceeded:
|
||||
event = *createSuccessResult(
|
||||
&payment.Attempt.Route, *payment.PaymentPreimage,
|
||||
)
|
||||
event = *createSuccessResult(payment.HTLCs)
|
||||
|
||||
// Payment already failed. It is not necessary to register as a
|
||||
// subscriber, because we can send the result on the channel
|
||||
// immediately.
|
||||
case channeldb.StatusFailed:
|
||||
var route *route.Route
|
||||
if payment.Attempt != nil {
|
||||
route = &payment.Attempt.Route
|
||||
}
|
||||
event = *createFailedResult(
|
||||
route, *payment.Failure,
|
||||
payment.HTLCs, *payment.FailureReason,
|
||||
)
|
||||
|
||||
default:
|
||||
|
|
|
@ -144,10 +144,13 @@ func TestControlTowerSubscribeSuccess(t *testing.T) {
|
|||
if result.Preimage != preimg {
|
||||
t.Fatal("unexpected preimage")
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(result.Route, &attempt.Route) {
|
||||
t.Fatalf("unexpected route: %v vs %v",
|
||||
spew.Sdump(result.Route),
|
||||
if len(result.HTLCs) != 1 {
|
||||
t.Fatalf("expected one htlc, got %d", len(result.HTLCs))
|
||||
}
|
||||
htlc := result.HTLCs[0]
|
||||
if !reflect.DeepEqual(htlc.Route, attempt.Route) {
|
||||
t.Fatalf("unexpected htlc route: %v vs %v",
|
||||
spew.Sdump(htlc.Route),
|
||||
spew.Sdump(attempt.Route))
|
||||
}
|
||||
|
||||
|
@ -168,6 +171,15 @@ func TestControlTowerSubscribeSuccess(t *testing.T) {
|
|||
func TestPaymentControlSubscribeFail(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
t.Run("register attempt", func(t *testing.T) {
|
||||
testPaymentControlSubscribeFail(t, true)
|
||||
})
|
||||
t.Run("no register attempt", func(t *testing.T) {
|
||||
testPaymentControlSubscribeFail(t, false)
|
||||
})
|
||||
}
|
||||
|
||||
func testPaymentControlSubscribeFail(t *testing.T, registerAttempt bool) {
|
||||
db, err := initDB()
|
||||
if err != nil {
|
||||
t.Fatalf("unable to init db: %v", err)
|
||||
|
@ -176,7 +188,7 @@ func TestPaymentControlSubscribeFail(t *testing.T) {
|
|||
pControl := NewControlTower(channeldb.NewPaymentControl(db))
|
||||
|
||||
// Initiate a payment.
|
||||
info, _, _, err := genInfo()
|
||||
info, attempt, _, err := genInfo()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -192,6 +204,17 @@ func TestPaymentControlSubscribeFail(t *testing.T) {
|
|||
t.Fatalf("expected subscribe to succeed, but got: %v", err)
|
||||
}
|
||||
|
||||
// Conditionally register the attempt based on the test type. This
|
||||
// allows us to simulate failing after attempting with an htlc or before
|
||||
// making any attempts at all.
|
||||
if registerAttempt {
|
||||
// Register an attempt.
|
||||
err = pControl.RegisterAttempt(info.PaymentHash, attempt)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
// Mark the payment as failed.
|
||||
if err := pControl.Fail(info.PaymentHash, channeldb.FailureReasonTimeout); err != nil {
|
||||
t.Fatal(err)
|
||||
|
@ -223,9 +246,28 @@ func TestPaymentControlSubscribeFail(t *testing.T) {
|
|||
if result.Success {
|
||||
t.Fatal("unexpected payment state")
|
||||
}
|
||||
if result.Route != nil {
|
||||
t.Fatal("expected no route")
|
||||
|
||||
// There will either be one or zero htlcs depending on whether
|
||||
// or not the attempt was registered. Assert the correct number
|
||||
// is present, and the route taken if the attempt was
|
||||
// registered.
|
||||
if registerAttempt {
|
||||
if len(result.HTLCs) != 1 {
|
||||
t.Fatalf("expected 1 htlc, got: %d",
|
||||
len(result.HTLCs))
|
||||
}
|
||||
|
||||
htlc := result.HTLCs[0]
|
||||
if !reflect.DeepEqual(htlc.Route, testRoute) {
|
||||
t.Fatalf("unexpected htlc route: %v vs %v",
|
||||
spew.Sdump(htlc.Route),
|
||||
spew.Sdump(testRoute))
|
||||
}
|
||||
} else if len(result.HTLCs) != 0 {
|
||||
t.Fatalf("expected 0 htlcs, got: %d",
|
||||
len(result.HTLCs))
|
||||
}
|
||||
|
||||
if result.FailureReason != channeldb.FailureReasonTimeout {
|
||||
t.Fatal("unexpected failure reason")
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@ import (
|
|||
// Instantiate variables to allow taking a reference from the failure reason.
|
||||
var (
|
||||
reasonError = channeldb.FailureReasonError
|
||||
reasonIncorrectDetails = channeldb.FailureReasonIncorrectPaymentDetails
|
||||
reasonIncorrectDetails = channeldb.FailureReasonPaymentDetails
|
||||
)
|
||||
|
||||
// pairResult contains the result of the interpretation of a payment attempt for
|
||||
|
|
44
rpcserver.go
44
rpcserver.go
|
@ -4398,24 +4398,29 @@ func (r *rpcServer) ListPayments(ctx context.Context,
|
|||
continue
|
||||
}
|
||||
|
||||
// If a payment attempt has been made we can fetch the route.
|
||||
// Otherwise we'll just populate the RPC response with an empty
|
||||
// one.
|
||||
var route route.Route
|
||||
if payment.Attempt != nil {
|
||||
route = payment.Attempt.Route
|
||||
// Fetch the payment's route and preimage. If no HTLC was
|
||||
// successful, an empty route and preimage will be used.
|
||||
var (
|
||||
route route.Route
|
||||
preimage lntypes.Preimage
|
||||
)
|
||||
for _, htlc := range payment.HTLCs {
|
||||
// Display the last route attempted.
|
||||
route = htlc.Route
|
||||
|
||||
// If any of the htlcs have settled, extract a valid
|
||||
// preimage.
|
||||
if htlc.Settle != nil {
|
||||
preimage = htlc.Settle.Preimage
|
||||
}
|
||||
}
|
||||
|
||||
// Encode the hops from the successful route, if any.
|
||||
path := make([]string, len(route.Hops))
|
||||
for i, hop := range route.Hops {
|
||||
path[i] = hex.EncodeToString(hop.PubKeyBytes[:])
|
||||
}
|
||||
|
||||
// If this payment is settled, the preimage will be available.
|
||||
var preimage lntypes.Preimage
|
||||
if payment.PaymentPreimage != nil {
|
||||
preimage = *payment.PaymentPreimage
|
||||
}
|
||||
|
||||
msatValue := int64(payment.Info.Value)
|
||||
satValue := int64(payment.Info.Value.ToSatoshis())
|
||||
|
||||
|
@ -4424,13 +4429,25 @@ func (r *rpcServer) ListPayments(ctx context.Context,
|
|||
return nil, err
|
||||
}
|
||||
|
||||
htlcs := make([]*lnrpc.HTLCAttempt, 0, len(payment.HTLCs))
|
||||
for _, dbHTLC := range payment.HTLCs {
|
||||
htlc, err := r.routerBackend.MarshalHTLCAttempt(dbHTLC)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
htlcs = append(htlcs, htlc)
|
||||
}
|
||||
|
||||
paymentHash := payment.Info.PaymentHash
|
||||
creationTimeNS := routerrpc.MarshalTimeNano(payment.Info.CreationTime)
|
||||
paymentsResp.Payments = append(paymentsResp.Payments, &lnrpc.Payment{
|
||||
PaymentHash: hex.EncodeToString(paymentHash[:]),
|
||||
Value: satValue,
|
||||
ValueMsat: msatValue,
|
||||
ValueSat: satValue,
|
||||
CreationDate: payment.Info.CreationDate.Unix(),
|
||||
CreationDate: payment.Info.CreationTime.Unix(),
|
||||
CreationTimeNs: creationTimeNS,
|
||||
Path: path,
|
||||
Fee: int64(route.TotalFees().ToSatoshis()),
|
||||
FeeSat: int64(route.TotalFees().ToSatoshis()),
|
||||
|
@ -4438,6 +4455,7 @@ func (r *rpcServer) ListPayments(ctx context.Context,
|
|||
PaymentPreimage: hex.EncodeToString(preimage[:]),
|
||||
PaymentRequest: string(payment.Info.PaymentRequest),
|
||||
Status: status,
|
||||
Htlcs: htlcs,
|
||||
})
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue