From 7795353e9f42567213843b5dd4f6cebdcd46fa89 Mon Sep 17 00:00:00 2001 From: "Johan T. Halseth" Date: Tue, 30 Mar 2021 12:10:30 +0200 Subject: [PATCH 01/17] channeldb: return full payment for inflight payments We might as well return all info, and we'll need the individual HTLCs in later commits. --- channeldb/payment_control.go | 18 ++++-------------- routing/control_tower.go | 4 ++-- routing/mock_test.go | 20 +++++++++++++------- routing/router.go | 11 ++--------- 4 files changed, 21 insertions(+), 32 deletions(-) diff --git a/channeldb/payment_control.go b/channeldb/payment_control.go index c820778c..a5023567 100644 --- a/channeldb/payment_control.go +++ b/channeldb/payment_control.go @@ -676,16 +676,9 @@ func ensureInFlight(payment *MPPayment) error { } } -// InFlightPayment is a wrapper around the info for a payment that has status -// InFlight. -type InFlightPayment struct { - // Info is the PaymentCreationInfo of the in-flight payment. - Info *PaymentCreationInfo -} - // FetchInFlightPayments returns all payments with status InFlight. -func (p *PaymentControl) FetchInFlightPayments() ([]*InFlightPayment, error) { - var inFlights []*InFlightPayment +func (p *PaymentControl) FetchInFlightPayments() ([]*MPPayment, error) { + var inFlights []*MPPayment err := kvdb.View(p.db, func(tx kvdb.RTx) error { payments := tx.ReadBucket(paymentsRootBucket) if payments == nil { @@ -708,15 +701,12 @@ func (p *PaymentControl) FetchInFlightPayments() ([]*InFlightPayment, error) { return nil } - inFlight := &InFlightPayment{} - - // Get the CreationInfo. - inFlight.Info, err = fetchCreationInfo(bucket) + p, err := fetchPayment(bucket) if err != nil { return err } - inFlights = append(inFlights, inFlight) + inFlights = append(inFlights, p) return nil }) }, func() { diff --git a/routing/control_tower.go b/routing/control_tower.go index 3e028c18..950b16f3 100644 --- a/routing/control_tower.go +++ b/routing/control_tower.go @@ -50,7 +50,7 @@ type ControlTower interface { Fail(lntypes.Hash, channeldb.FailureReason) error // FetchInFlightPayments returns all payments with status InFlight. - FetchInFlightPayments() ([]*channeldb.InFlightPayment, error) + FetchInFlightPayments() ([]*channeldb.MPPayment, error) // SubscribePayment subscribes to updates for the payment with the given // hash. A first update with the current state of the payment is always @@ -213,7 +213,7 @@ func (p *controlTower) Fail(paymentHash lntypes.Hash, } // FetchInFlightPayments returns all payments with status InFlight. -func (p *controlTower) FetchInFlightPayments() ([]*channeldb.InFlightPayment, error) { +func (p *controlTower) FetchInFlightPayments() ([]*channeldb.MPPayment, error) { return p.db.FetchInFlightPayments() } diff --git a/routing/mock_test.go b/routing/mock_test.go index a477fb10..8186902f 100644 --- a/routing/mock_test.go +++ b/routing/mock_test.go @@ -452,6 +452,12 @@ func (m *mockControlTower) FetchPayment(phash lntypes.Hash) ( m.Lock() defer m.Unlock() + return m.fetchPayment(phash) +} + +func (m *mockControlTower) fetchPayment(phash lntypes.Hash) ( + *channeldb.MPPayment, error) { + p, ok := m.payments[phash] if !ok { return nil, channeldb.ErrPaymentNotInitiated @@ -468,12 +474,11 @@ func (m *mockControlTower) FetchPayment(phash lntypes.Hash) ( // Return a copy of the current attempts. mp.HTLCs = append(mp.HTLCs, p.attempts...) - return mp, nil } func (m *mockControlTower) FetchInFlightPayments() ( - []*channeldb.InFlightPayment, error) { + []*channeldb.MPPayment, error) { if m.fetchInFlight != nil { m.fetchInFlight <- struct{}{} @@ -483,8 +488,8 @@ func (m *mockControlTower) FetchInFlightPayments() ( defer m.Unlock() // In flight are all payments not successful or failed. - var fl []*channeldb.InFlightPayment - for hash, p := range m.payments { + var fl []*channeldb.MPPayment + for hash := range m.payments { if _, ok := m.successful[hash]; ok { continue } @@ -492,11 +497,12 @@ func (m *mockControlTower) FetchInFlightPayments() ( continue } - ifl := channeldb.InFlightPayment{ - Info: &p.info, + mp, err := m.fetchPayment(hash) + if err != nil { + return nil, err } - fl = append(fl, &ifl) + fl = append(fl, mp) } return fl, nil diff --git a/routing/router.go b/routing/router.go index a1f50215..1d251e44 100644 --- a/routing/router.go +++ b/routing/router.go @@ -583,14 +583,7 @@ func (r *ChannelRouter) Start() error { // until the cleaning has finished. toKeep := make(map[uint64]struct{}) for _, p := range payments { - payment, err := r.cfg.Control.FetchPayment( - p.Info.PaymentHash, - ) - if err != nil { - return err - } - - for _, a := range payment.HTLCs { + for _, a := range p.HTLCs { toKeep[a.AttemptID] = struct{}{} } } @@ -603,7 +596,7 @@ func (r *ChannelRouter) Start() error { for _, payment := range payments { log.Infof("Resuming payment with hash %v", payment.Info.PaymentHash) r.wg.Add(1) - go func(payment *channeldb.InFlightPayment) { + go func(payment *channeldb.MPPayment) { defer r.wg.Done() // We create a dummy, empty payment session such that From a9f19b100b228a0ab756fb16ed1db180154233f4 Mon Sep 17 00:00:00 2001 From: "Johan T. Halseth" Date: Wed, 7 Apr 2021 15:03:54 +0200 Subject: [PATCH 02/17] router+switch: rename paymentID->attemptID To distinguish the attempt's unique ID from the overall payment identifier, we name it attemptID everywhere, and note that the paymentHash argument won't be the actual payment hash for AMP payments. --- htlcswitch/switch.go | 51 +++++++++++++++++++++++--------------------- routing/router.go | 28 +++++++++++++----------- 2 files changed, 43 insertions(+), 36 deletions(-) diff --git a/htlcswitch/switch.go b/htlcswitch/switch.go index be56dd0e..1861a51a 100644 --- a/htlcswitch/switch.go +++ b/htlcswitch/switch.go @@ -363,12 +363,15 @@ func (s *Switch) ProcessContractResolution(msg contractcourt.ResolutionMsg) erro } // GetPaymentResult returns the the result of the payment attempt with the -// given paymentID. The method returns a channel where the payment result will -// be sent when available, or an error is encountered during forwarding. When a -// result is received on the channel, the HTLC is guaranteed to no longer be in -// flight. The switch shutting down is signaled by closing the channel. If the -// paymentID is unknown, ErrPaymentIDNotFound will be returned. -func (s *Switch) GetPaymentResult(paymentID uint64, paymentHash lntypes.Hash, +// given attemptID. The paymentHash should be set to the payment's overall +// hash, or in case of AMP payments the payment's unique identifier. +// +// The method returns a channel where the payment result will be sent when +// available, or an error is encountered during forwarding. When a result is +// received on the channel, the HTLC is guaranteed to no longer be in flight. +// The switch shutting down is signaled by closing the channel. If the +// attemptID is unknown, ErrPaymentIDNotFound will be returned. +func (s *Switch) GetPaymentResult(attemptID uint64, paymentHash lntypes.Hash, deobfuscator ErrorDecrypter) (<-chan *PaymentResult, error) { var ( @@ -376,7 +379,7 @@ func (s *Switch) GetPaymentResult(paymentID uint64, paymentHash lntypes.Hash, err error outKey = CircuitKey{ ChanID: hop.Source, - HtlcID: paymentID, + HtlcID: attemptID, } ) @@ -384,7 +387,7 @@ func (s *Switch) GetPaymentResult(paymentID uint64, paymentHash lntypes.Hash, // result is already available. // Assumption: no one will add this payment ID other than the caller. if s.circuits.LookupCircuit(outKey) == nil { - res, err := s.networkResults.getResult(paymentID) + res, err := s.networkResults.getResult(attemptID) if err != nil { return nil, err } @@ -394,7 +397,7 @@ func (s *Switch) GetPaymentResult(paymentID uint64, paymentHash lntypes.Hash, } else { // The payment was committed to the circuits, subscribe for a // result. - nChan, err = s.networkResults.subscribeResult(paymentID) + nChan, err = s.networkResults.subscribeResult(attemptID) if err != nil { return nil, err } @@ -420,12 +423,12 @@ func (s *Switch) GetPaymentResult(paymentID uint64, paymentHash lntypes.Hash, return } - log.Debugf("Received network result %T for paymentID=%v", n.msg, - paymentID) + log.Debugf("Received network result %T for attemptID=%v", n.msg, + attemptID) // Extract the result and pass it to the result channel. result, err := s.extractResult( - deobfuscator, n, paymentID, paymentHash, + deobfuscator, n, attemptID, paymentHash, ) if err != nil { e := fmt.Errorf("unable to extract result: %v", err) @@ -450,10 +453,10 @@ func (s *Switch) CleanStore(keepPids map[uint64]struct{}) error { } // SendHTLC is used by other subsystems which aren't belong to htlc switch -// package in order to send the htlc update. The paymentID used MUST be unique +// package in order to send the htlc update. The attemptID used MUST be unique // for this HTLC, and MUST be used only once, otherwise the switch might reject // it. -func (s *Switch) SendHTLC(firstHop lnwire.ShortChannelID, paymentID uint64, +func (s *Switch) SendHTLC(firstHop lnwire.ShortChannelID, attemptID uint64, htlc *lnwire.UpdateAddHTLC) error { // Generate and send new update packet, if error will be received on @@ -461,7 +464,7 @@ func (s *Switch) SendHTLC(firstHop lnwire.ShortChannelID, paymentID uint64, // system and something wrong happened. packet := &htlcPacket{ incomingChanID: hop.Source, - incomingHTLCID: paymentID, + incomingHTLCID: attemptID, outgoingChanID: firstHop, htlc: htlc, } @@ -794,7 +797,7 @@ func (s *Switch) getLocalLink(pkt *htlcPacket, htlc *lnwire.UpdateAddHTLC) ( func (s *Switch) handleLocalResponse(pkt *htlcPacket) { defer s.wg.Done() - paymentID := pkt.incomingHTLCID + attemptID := pkt.incomingHTLCID // The error reason will be unencypted in case this a local // failure or a converted error. @@ -807,9 +810,9 @@ func (s *Switch) handleLocalResponse(pkt *htlcPacket) { // Store the result to the db. This will also notify subscribers about // the result. - if err := s.networkResults.storeResult(paymentID, n); err != nil { + if err := s.networkResults.storeResult(attemptID, n); err != nil { log.Errorf("Unable to complete payment for pid=%v: %v", - paymentID, err) + attemptID, err) return } @@ -857,7 +860,7 @@ func (s *Switch) handleLocalResponse(pkt *htlcPacket) { // extractResult uses the given deobfuscator to extract the payment result from // the given network message. func (s *Switch) extractResult(deobfuscator ErrorDecrypter, n *networkResult, - paymentID uint64, paymentHash lntypes.Hash) (*PaymentResult, error) { + attemptID uint64, paymentHash lntypes.Hash) (*PaymentResult, error) { switch htlc := n.msg.(type) { @@ -872,7 +875,7 @@ func (s *Switch) extractResult(deobfuscator ErrorDecrypter, n *networkResult, // user payment and return fail response. case *lnwire.UpdateFailHTLC: paymentErr := s.parseFailedPayment( - deobfuscator, paymentID, paymentHash, n.unencrypted, + deobfuscator, attemptID, paymentHash, n.unencrypted, n.isResolution, htlc, ) @@ -894,7 +897,7 @@ func (s *Switch) extractResult(deobfuscator ErrorDecrypter, n *networkResult, // 3) A failure from the remote party, which will need to be decrypted using // the payment deobfuscator. func (s *Switch) parseFailedPayment(deobfuscator ErrorDecrypter, - paymentID uint64, paymentHash lntypes.Hash, unencrypted, + attemptID uint64, paymentHash lntypes.Hash, unencrypted, isResolution bool, htlc *lnwire.UpdateFailHTLC) error { switch { @@ -918,7 +921,7 @@ func (s *Switch) parseFailedPayment(deobfuscator ErrorDecrypter, log.Errorf("%v: (hash=%v, pid=%d): %v", linkError.FailureDetail.FailureString(), - paymentHash, paymentID, err) + paymentHash, attemptID, err) return linkError } @@ -938,7 +941,7 @@ func (s *Switch) parseFailedPayment(deobfuscator ErrorDecrypter, log.Infof("%v: hash=%v, pid=%d", linkError.FailureDetail.FailureString(), - paymentHash, paymentID) + paymentHash, attemptID) return linkError @@ -951,7 +954,7 @@ func (s *Switch) parseFailedPayment(deobfuscator ErrorDecrypter, if err != nil { log.Errorf("unable to de-obfuscate onion failure "+ "(hash=%v, pid=%d): %v", - paymentHash, paymentID, err) + paymentHash, attemptID, err) return ErrUnreadableFailureMessage } diff --git a/routing/router.go b/routing/router.go index 1d251e44..72b17dcd 100644 --- a/routing/router.go +++ b/routing/router.go @@ -165,17 +165,21 @@ type PaymentAttemptDispatcher interface { // denoted by its public key. A non-nil error is to be returned if the // payment was unsuccessful. SendHTLC(firstHop lnwire.ShortChannelID, - paymentID uint64, + attemptID uint64, htlcAdd *lnwire.UpdateAddHTLC) error // GetPaymentResult returns the the result of the payment attempt with - // the given paymentID. The method returns a channel where the payment - // result will be sent when available, or an error is encountered - // during forwarding. When a result is received on the channel, the - // HTLC is guaranteed to no longer be in flight. The switch shutting - // down is signaled by closing the channel. If the paymentID is - // unknown, ErrPaymentIDNotFound will be returned. - GetPaymentResult(paymentID uint64, paymentHash lntypes.Hash, + // the given attemptID. The paymentHash should be set to the payment's + // overall hash, or in case of AMP payments the payment's unique + // identifier. + // + // The method returns a channel where the payment result will be sent + // when available, or an error is encountered during forwarding. When a + // result is received on the channel, the HTLC is guaranteed to no + // longer be in flight. The switch shutting down is signaled by + // closing the channel. If the attemptID is unknown, + // ErrPaymentIDNotFound will be returned. + GetPaymentResult(attemptID uint64, paymentHash lntypes.Hash, deobfuscator htlcswitch.ErrorDecrypter) ( <-chan *htlcswitch.PaymentResult, error) @@ -211,13 +215,13 @@ type MissionController interface { // input for future probability estimates. It returns a bool indicating // whether this error is a final error and no further payment attempts // need to be made. - ReportPaymentFail(paymentID uint64, rt *route.Route, + ReportPaymentFail(attemptID uint64, rt *route.Route, failureSourceIdx *int, failure lnwire.FailureMessage) ( *channeldb.FailureReason, error) // ReportPaymentSuccess reports a successful payment to mission control as input // for future probability estimates. - ReportPaymentSuccess(paymentID uint64, rt *route.Route) error + ReportPaymentSuccess(attemptID uint64, rt *route.Route) error // GetProbability is expected to return the success probability of a // payment from fromNode along edge. @@ -2081,7 +2085,7 @@ func (r *ChannelRouter) tryApplyChannelUpdate(rt *route.Route, // error type, this error is either the final outcome of the payment or we need // to continue with an alternative route. A final outcome is indicated by a // non-nil return value. -func (r *ChannelRouter) processSendError(paymentID uint64, rt *route.Route, +func (r *ChannelRouter) processSendError(attemptID uint64, rt *route.Route, sendErr error) *channeldb.FailureReason { internalErrorReason := channeldb.FailureReasonError @@ -2091,7 +2095,7 @@ func (r *ChannelRouter) processSendError(paymentID uint64, rt *route.Route, // Report outcome to mission control. reason, err := r.cfg.MissionControl.ReportPaymentFail( - paymentID, rt, srcIdx, msg, + attemptID, rt, srcIdx, msg, ) if err != nil { log.Errorf("Error reporting payment result to mc: %v", From 6474b253d67ef9c3a8265e89709b8d259a6b2510 Mon Sep 17 00:00:00 2001 From: "Johan T. Halseth" Date: Mon, 12 Apr 2021 15:21:27 +0200 Subject: [PATCH 03/17] routing/shards: add ShardTracker interface We'll use this to keep track of the outstanding shards and which preimages we are using for each. For now this is a simple map from attempt ID to hash, but later we'll hide the AMP child derivation behind this interface. --- routing/shards/shard_tracker.go | 126 +++++++++++++++++++++++++++ routing/shards/shard_tracker_test.go | 47 ++++++++++ 2 files changed, 173 insertions(+) create mode 100644 routing/shards/shard_tracker.go create mode 100644 routing/shards/shard_tracker_test.go diff --git a/routing/shards/shard_tracker.go b/routing/shards/shard_tracker.go new file mode 100644 index 00000000..474c85bc --- /dev/null +++ b/routing/shards/shard_tracker.go @@ -0,0 +1,126 @@ +package shards + +import ( + "fmt" + + "github.com/lightningnetwork/lnd/lntypes" + "github.com/lightningnetwork/lnd/record" +) + +// PaymentShard is an interface representing a shard tracked by the +// ShardTracker. It contains options that are specific to the given shard that +// might differ from the overall payment. +type PaymentShard interface { + // Hash returns the hash used for the HTLC representing this shard. + Hash() lntypes.Hash + + // MPP returns any extra MPP records that should be set for the final + // hop on the route used by this shard. + MPP() *record.MPP + + // AMP returns any extra AMP records that should be set for the final + // hop on the route used by this shard. + AMP() *record.AMP +} + +// ShardTracker is an interfae representing a tracker that keeps track of the +// inflight shards of a payment, and is able to assign new shards the correct +// options such as hash and extra records. +type ShardTracker interface { + // NewShard registers a new attempt with the ShardTracker and returns a + // new shard representing this attempt. This attempt's shard should be + // canceled if it ends up not being used by the overall payment, i.e. + // if the attempt fails. + NewShard(uint64, bool) (PaymentShard, error) + + // CancelShard cancel's the shard corresponding to the given attempt + // ID. This lets the ShardTracker free up any slots used by this shard, + // and in case of AMP payments return the share used by this shard to + // the root share. + CancelShard(uint64) error + + // GetHash retrieves the hash used by the shard of the given attempt + // ID. This wil return an error if the attempt ID is unknown. + GetHash(uint64) (lntypes.Hash, error) +} + +// Shard is a struct used for simple shards where we obly need to keep map it +// to a single hash. +type Shard struct { + hash lntypes.Hash +} + +// Hash returns the hash used for the HTLC representing this shard. +func (s *Shard) Hash() lntypes.Hash { + return s.hash +} + +// MPP returns any extra MPP records that should be set for the final hop on +// the route used by this shard. +func (s *Shard) MPP() *record.MPP { + return nil +} + +// AMP returns any extra AMP records that should be set for the final hop on +// the route used by this shard. +func (s *Shard) AMP() *record.AMP { + return nil +} + +// SimpleShardTracker is an implementation of the ShardTracker interface that +// simply maps attempt IDs to hashes. New shards will be given a static payment +// hash. This should be used for regular and MPP payments, in addition to +// resumed payments where all the attempt's hashes have already been created. +type SimpleShardTracker struct { + hash lntypes.Hash + shards map[uint64]lntypes.Hash +} + +// A compile time check to ensure SimpleShardTracker implements the +// ShardTracker interface. +var _ ShardTracker = (*SimpleShardTracker)(nil) + +// NewSimpleShardTracker creates a new intance of the SimpleShardTracker with +// the given payment hash and existing attempts. +func NewSimpleShardTracker(paymentHash lntypes.Hash, + shards map[uint64]lntypes.Hash) ShardTracker { + + if shards == nil { + shards = make(map[uint64]lntypes.Hash) + } + + return &SimpleShardTracker{ + hash: paymentHash, + shards: shards, + } +} + +// NewShard registers a new attempt with the ShardTracker and returns a +// new shard representing this attempt. This attempt's shard should be canceled +// if it ends up not being used by the overall payment, i.e. if the attempt +// fails. +func (m *SimpleShardTracker) NewShard(id uint64, _ bool) (PaymentShard, error) { + m.shards[id] = m.hash + + return &Shard{ + hash: m.hash, + }, nil +} + +// CancelShard cancel's the shard corresponding to the given attempt ID. +func (m *SimpleShardTracker) CancelShard(id uint64) error { + delete(m.shards, id) + return nil +} + +// GetHash retrieves the hash used by the shard of the given attempt ID. This +// will return an error if the attempt ID is unknown. +func (m *SimpleShardTracker) GetHash(id uint64) (lntypes.Hash, error) { + hash, ok := m.shards[id] + if !ok { + return lntypes.Hash{}, fmt.Errorf("hash for attempt id %v "+ + "not found", id) + } + + return hash, nil +} diff --git a/routing/shards/shard_tracker_test.go b/routing/shards/shard_tracker_test.go new file mode 100644 index 00000000..b755e91f --- /dev/null +++ b/routing/shards/shard_tracker_test.go @@ -0,0 +1,47 @@ +package shards_test + +import ( + "crypto/rand" + "testing" + + "github.com/lightningnetwork/lnd/lntypes" + "github.com/lightningnetwork/lnd/routing/shards" + "github.com/stretchr/testify/require" +) + +// TestSimpleShardTracker tests that the simple tracker that keeps a map from +// attemptID-> payment hash works. +func TestSimpleShardTracker(t *testing.T) { + var testHashes [2]lntypes.Hash + for i := range testHashes { + _, err := rand.Read(testHashes[i][:]) + require.NoError(t, err) + } + + startAttempts := map[uint64]lntypes.Hash{ + 1: testHashes[1], + } + + tracker := shards.NewSimpleShardTracker(testHashes[0], startAttempts) + + // Trying to retrieve a hash for id 0 should result in an error. + _, err := tracker.GetHash(0) + require.Error(t, err) + + // Getting id 1 should workd. + hash1, err := tracker.GetHash(1) + require.NoError(t, err) + + require.Equal(t, testHashes[1], hash1) + + // Finally, create a new shard and immediately retrieve the hash. + shard, err := tracker.NewShard(2, false) + require.NoError(t, err) + + // It's hash should be the tracker's overall payment hash. + hash2, err := tracker.GetHash(2) + require.NoError(t, err) + + require.Equal(t, testHashes[0], shard.Hash()) + require.Equal(t, testHashes[0], hash2) +} From 41ae3530a31bbd9722bb18670fc6f59ae125c62d Mon Sep 17 00:00:00 2001 From: "Johan T. Halseth" Date: Mon, 12 Apr 2021 15:21:59 +0200 Subject: [PATCH 04/17] routing/payment_lifecycle: use ShardTracker to track shards We'll let the payment's lifecycle register each shard it's sending with the ShardTracker, canceling failed shards. This will be the foundation for correct AMP derivation for each shard we'll send. --- routing/pathfind.go | 2 + routing/payment_lifecycle.go | 103 ++++++++++++++++++++++---------- routing/router.go | 69 ++++++++++++++++----- routing/shards/shard_tracker.go | 9 +++ 4 files changed, 137 insertions(+), 46 deletions(-) diff --git a/routing/pathfind.go b/routing/pathfind.go index 387e7150..3d722c82 100644 --- a/routing/pathfind.go +++ b/routing/pathfind.go @@ -194,6 +194,8 @@ func newRoute(sourceVertex route.Vertex, } // Otherwise attach the mpp record if it exists. + // TODO(halseth): move this to payment life cycle, + // where AMP options are set. if finalHop.paymentAddr != nil { mpp = record.NewMPP( finalHop.totalAmt, diff --git a/routing/payment_lifecycle.go b/routing/payment_lifecycle.go index be9ade0d..1760488a 100644 --- a/routing/payment_lifecycle.go +++ b/routing/payment_lifecycle.go @@ -12,6 +12,7 @@ import ( "github.com/lightningnetwork/lnd/lntypes" "github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/routing/route" + "github.com/lightningnetwork/lnd/routing/shards" ) // errShardHandlerExiting is returned from the shardHandler when it exits. @@ -25,6 +26,7 @@ type paymentLifecycle struct { feeLimit lnwire.MilliSatoshi paymentHash lntypes.Hash paySession PaymentSession + shardTracker shards.ShardTracker timeoutChan <-chan time.Time currentHeight int32 } @@ -83,10 +85,11 @@ func (p *paymentLifecycle) paymentState(payment *channeldb.MPPayment) ( // resumePayment resumes the paymentLifecycle from the current state. func (p *paymentLifecycle) resumePayment() ([32]byte, *route.Route, error) { shardHandler := &shardHandler{ - router: p.router, - paymentHash: p.paymentHash, - shardErrors: make(chan error), - quit: make(chan struct{}), + router: p.router, + paymentHash: p.paymentHash, + shardTracker: p.shardTracker, + shardErrors: make(chan error), + quit: make(chan struct{}), } // When the payment lifecycle loop exits, we make sure to signal any @@ -246,8 +249,12 @@ lifecycle: continue lifecycle } + // If this route will consume the last remeining amount to send + // to the receiver, this will be our last shard (for now). + lastShard := rt.ReceiverAmt() == state.remainingAmt + // We found a route to try, launch a new shard. - attempt, outcome, err := shardHandler.launchShard(rt) + attempt, outcome, err := shardHandler.launchShard(rt, lastShard) switch { // We may get a terminal error if we've processed a shard with // a terminal state (settled or permanent failure), while we @@ -294,8 +301,9 @@ lifecycle: // shardHandler holds what is necessary to send and collect the result of // shards. type shardHandler struct { - paymentHash lntypes.Hash - router *ChannelRouter + paymentHash lntypes.Hash + router *ChannelRouter + shardTracker shards.ShardTracker // shardErrors is a channel where errors collected by calling // collectResultAsync will be delivered. These results are meant to be @@ -366,19 +374,20 @@ type launchOutcome struct { } // launchShard creates and sends an HTLC attempt along the given route, -// registering it with the control tower before sending it. It returns the -// HTLCAttemptInfo that was created for the shard, along with a launchOutcome. -// The launchOutcome is used to indicate whether the attempt was successfully -// sent. If the launchOutcome wraps a non-nil error, it means that the attempt -// was not sent onto the network, so no result will be available in the future -// for it. -func (p *shardHandler) launchShard(rt *route.Route) (*channeldb.HTLCAttemptInfo, - *launchOutcome, error) { +// registering it with the control tower before sending it. The lastShard +// argument should be true if this shard will consume the remainder of the +// amount to send. It returns the HTLCAttemptInfo that was created for the +// shard, along with a launchOutcome. The launchOutcome is used to indicate +// whether the attempt was successfully sent. If the launchOutcome wraps a +// non-nil error, it means that the attempt was not sent onto the network, so +// no result will be available in the future for it. +func (p *shardHandler) launchShard(rt *route.Route, + lastShard bool) (*channeldb.HTLCAttemptInfo, *launchOutcome, error) { // Using the route received from the payment session, create a new // shard to send. firstHop, htlcAdd, attempt, err := p.createNewPaymentAttempt( - rt, + rt, lastShard, ) if err != nil { return nil, nil, err @@ -480,10 +489,17 @@ func (p *shardHandler) collectResultAsync(attempt *channeldb.HTLCAttemptInfo) { func (p *shardHandler) collectResult(attempt *channeldb.HTLCAttemptInfo) ( *shardResult, error) { + // We'll retrieve the hash specific to this shard from the + // shardTracker, since it will be needed to regenerate the circuit + // below. + hash, err := p.shardTracker.GetHash(attempt.AttemptID) + if err != nil { + return nil, err + } + // Regenerate the circuit for this attempt. _, circuit, err := generateSphinxPacket( - &attempt.Route, p.paymentHash[:], - attempt.SessionKey, + &attempt.Route, hash[:], attempt.SessionKey, ) if err != nil { return nil, err @@ -597,7 +613,7 @@ func (p *shardHandler) collectResult(attempt *channeldb.HTLCAttemptInfo) ( } // createNewPaymentAttempt creates a new payment attempt from the given route. -func (p *shardHandler) createNewPaymentAttempt(rt *route.Route) ( +func (p *shardHandler) createNewPaymentAttempt(rt *route.Route, lastShard bool) ( lnwire.ShortChannelID, *lnwire.UpdateAddHTLC, *channeldb.HTLCAttemptInfo, error) { @@ -607,12 +623,39 @@ func (p *shardHandler) createNewPaymentAttempt(rt *route.Route) ( return lnwire.ShortChannelID{}, nil, nil, err } + // We generate a new, unique payment ID that we will use for + // this HTLC. + attemptID, err := p.router.cfg.NextPaymentID() + if err != nil { + return lnwire.ShortChannelID{}, nil, nil, err + } + + // Requesst a new shard from the ShardTracker. If this is an AMP + // payment, and this is the last shard, the outstanding shards together + // with ths one will be enough for the receiver to derive all HTLC + // preimages. If this a non-AMP payment, the ShardTracker will return a + // simple shard with the payment's static payment hash. + shard, err := p.shardTracker.NewShard(attemptID, lastShard) + if err != nil { + return lnwire.ShortChannelID{}, nil, nil, err + } + + // It this shard carries MPP or AMP options, add them to the last hop + // on the route. + hop := rt.Hops[len(rt.Hops)-1] + if shard.MPP() != nil { + hop.MPP = shard.MPP() + } + + if shard.AMP() != nil { + hop.AMP = shard.AMP() + } + // Generate the raw encoded sphinx packet to be included along // with the htlcAdd message that we send directly to the // switch. - onionBlob, _, err := generateSphinxPacket( - rt, p.paymentHash[:], sessionKey, - ) + hash := shard.Hash() + onionBlob, _, err := generateSphinxPacket(rt, hash[:], sessionKey) if err != nil { return lnwire.ShortChannelID{}, nil, nil, err } @@ -623,7 +666,7 @@ func (p *shardHandler) createNewPaymentAttempt(rt *route.Route) ( htlcAdd := &lnwire.UpdateAddHTLC{ Amount: rt.TotalAmount, Expiry: rt.TotalTimeLock, - PaymentHash: p.paymentHash, + PaymentHash: hash, } copy(htlcAdd.OnionBlob[:], onionBlob) @@ -634,13 +677,6 @@ func (p *shardHandler) createNewPaymentAttempt(rt *route.Route) ( rt.Hops[0].ChannelID, ) - // We generate a new, unique payment ID that we will use for - // this HTLC. - attemptID, err := p.router.cfg.NextPaymentID() - if err != nil { - return lnwire.ShortChannelID{}, nil, nil, err - } - // We now have all the information needed to populate // the current attempt information. attempt := &channeldb.HTLCAttemptInfo{ @@ -722,6 +758,13 @@ func (p *shardHandler) failAttempt(attempt *channeldb.HTLCAttemptInfo, p.router.cfg.Clock.Now(), ) + // Now that we are failing this payment attempt, cancel the shard with + // the ShardTracker such that it can derive the correct hash for the + // next attempt. + if err := p.shardTracker.CancelShard(attempt.AttemptID); err != nil { + return nil, err + } + return p.router.cfg.Control.FailAttempt( p.paymentHash, attempt.AttemptID, failInfo, diff --git a/routing/router.go b/routing/router.go index 72b17dcd..7f54f915 100644 --- a/routing/router.go +++ b/routing/router.go @@ -29,6 +29,7 @@ import ( "github.com/lightningnetwork/lnd/record" "github.com/lightningnetwork/lnd/routing/chainview" "github.com/lightningnetwork/lnd/routing/route" + "github.com/lightningnetwork/lnd/routing/shards" "github.com/lightningnetwork/lnd/ticker" "github.com/lightningnetwork/lnd/zpay32" ) @@ -603,19 +604,40 @@ func (r *ChannelRouter) Start() error { go func(payment *channeldb.MPPayment) { defer r.wg.Done() + // Get the hashes used for the outstanding HTLCs. + htlcs := make(map[uint64]lntypes.Hash) + for _, a := range payment.HTLCs { + a := a + + hash := payment.Info.PaymentHash + htlcs[a.AttemptID] = hash + } + + // Since we are not supporting creating more shards + // after a restart (only receiving the result of the + // shards already outstanding), we create a simple + // shard tracker that will map the attempt IDs to + // hashes used for the HTLCs. This will be enough also + // for AMP payments, since we only need the hashes for + // the individual HTLCs to regenerate the circuits, and + // we don't currently persist the root share necessary + // to re-derive them. + shardTracker := shards.NewSimpleShardTracker( + payment.Info.PaymentHash, htlcs, + ) + // We create a dummy, empty payment session such that // we won't make another payment attempt when the // result for the in-flight attempt is received. paySession := r.cfg.SessionSource.NewPaymentSessionEmpty() - // We pass in a zero timeout value, to indicate we // don't need it to timeout. It will stop immediately // after the existing attempt has finished anyway. We // also set a zero fee limit, as no more routes should // be tried. _, _, err := r.sendPayment( - payment.Info.Value, 0, - payment.Info.PaymentHash, 0, paySession, + payment.Info.Value, 0, payment.Info.PaymentHash, + 0, paySession, shardTracker, ) if err != nil { log.Errorf("Resuming payment with hash %v "+ @@ -1770,7 +1792,7 @@ type LightningPayment struct { func (r *ChannelRouter) SendPayment(payment *LightningPayment) ([32]byte, *route.Route, error) { - paySession, err := r.preparePayment(payment) + paySession, shardTracker, err := r.preparePayment(payment) if err != nil { return [32]byte{}, nil, err } @@ -1782,14 +1804,14 @@ func (r *ChannelRouter) SendPayment(payment *LightningPayment) ([32]byte, // for the existing attempt. return r.sendPayment( payment.Amount, payment.FeeLimit, payment.PaymentHash, - payment.PayAttemptTimeout, paySession, + payment.PayAttemptTimeout, paySession, shardTracker, ) } // SendPaymentAsync is the non-blocking version of SendPayment. The payment // result needs to be retrieved via the control tower. func (r *ChannelRouter) SendPaymentAsync(payment *LightningPayment) error { - paySession, err := r.preparePayment(payment) + paySession, shardTracker, err := r.preparePayment(payment) if err != nil { return err } @@ -1805,7 +1827,7 @@ func (r *ChannelRouter) SendPaymentAsync(payment *LightningPayment) error { _, _, err := r.sendPayment( payment.Amount, payment.FeeLimit, payment.PaymentHash, - payment.PayAttemptTimeout, paySession, + payment.PayAttemptTimeout, paySession, shardTracker, ) if err != nil { log.Errorf("Payment with hash %x failed: %v", @@ -1841,14 +1863,14 @@ func spewPayment(payment *LightningPayment) logClosure { // preparePayment creates the payment session and registers the payment with the // control tower. func (r *ChannelRouter) preparePayment(payment *LightningPayment) ( - PaymentSession, error) { + PaymentSession, shards.ShardTracker, error) { // Before starting the HTLC routing attempt, we'll create a fresh // payment session which will report our errors back to mission // control. paySession, err := r.cfg.SessionSource.NewPaymentSession(payment) if err != nil { - return nil, err + return nil, nil, err } // Record this payment hash with the ControlTower, ensuring it is not @@ -1862,12 +1884,18 @@ func (r *ChannelRouter) preparePayment(payment *LightningPayment) ( PaymentRequest: payment.PaymentRequest, } + // Create a new ShardTracker that we'll use during the life cycle of + // this payment. + shardTracker := shards.NewSimpleShardTracker( + payment.PaymentHash, nil, + ) + err = r.cfg.Control.InitPayment(payment.PaymentHash, info) if err != nil { - return nil, err + return nil, nil, err } - return paySession, nil + return paySession, shardTracker, nil } // SendToRoute attempts to send a payment with the given hash through the @@ -1915,14 +1943,22 @@ func (r *ChannelRouter) SendToRoute(hash lntypes.Hash, rt *route.Route) ( }), ) + // Since the HTLC hashes and preimages are specified manually over the + // RPC for SendToRoute requests, we don't have to worry about creating + // a ShardTracker that can generate hashes for AMP payments. Instead we + // create a simple tracker that can just return the hash for the single + // shard we'll now launch. + shardTracker := shards.NewSimpleShardTracker(hash, nil) + // Launch a shard along the given route. sh := &shardHandler{ - router: r, - paymentHash: hash, + router: r, + paymentHash: hash, + shardTracker: shardTracker, } var shardError error - attempt, outcome, err := sh.launchShard(rt) + attempt, outcome, err := sh.launchShard(rt, false) // With SendToRoute, it can happen that the route exceeds protocol // constraints. Mark the payment as failed with an internal error. @@ -2007,8 +2043,8 @@ func (r *ChannelRouter) SendToRoute(hash lntypes.Hash, rt *route.Route) ( // the ControlTower. func (r *ChannelRouter) sendPayment( totalAmt, feeLimit lnwire.MilliSatoshi, paymentHash lntypes.Hash, - timeout time.Duration, - paySession PaymentSession) ([32]byte, *route.Route, error) { + timeout time.Duration, paySession PaymentSession, + shardTracker shards.ShardTracker) ([32]byte, *route.Route, error) { // We'll also fetch the current block height so we can properly // calculate the required HTLC time locks within the route. @@ -2025,6 +2061,7 @@ func (r *ChannelRouter) sendPayment( feeLimit: feeLimit, paymentHash: paymentHash, paySession: paySession, + shardTracker: shardTracker, currentHeight: currentHeight, } diff --git a/routing/shards/shard_tracker.go b/routing/shards/shard_tracker.go index 474c85bc..a41461eb 100644 --- a/routing/shards/shard_tracker.go +++ b/routing/shards/shard_tracker.go @@ -2,6 +2,7 @@ package shards import ( "fmt" + "sync" "github.com/lightningnetwork/lnd/lntypes" "github.com/lightningnetwork/lnd/record" @@ -74,6 +75,7 @@ func (s *Shard) AMP() *record.AMP { type SimpleShardTracker struct { hash lntypes.Hash shards map[uint64]lntypes.Hash + sync.Mutex } // A compile time check to ensure SimpleShardTracker implements the @@ -100,7 +102,9 @@ func NewSimpleShardTracker(paymentHash lntypes.Hash, // if it ends up not being used by the overall payment, i.e. if the attempt // fails. func (m *SimpleShardTracker) NewShard(id uint64, _ bool) (PaymentShard, error) { + m.Lock() m.shards[id] = m.hash + m.Unlock() return &Shard{ hash: m.hash, @@ -109,14 +113,19 @@ func (m *SimpleShardTracker) NewShard(id uint64, _ bool) (PaymentShard, error) { // CancelShard cancel's the shard corresponding to the given attempt ID. func (m *SimpleShardTracker) CancelShard(id uint64) error { + m.Lock() delete(m.shards, id) + m.Unlock() + return nil } // GetHash retrieves the hash used by the shard of the given attempt ID. This // will return an error if the attempt ID is unknown. func (m *SimpleShardTracker) GetHash(id uint64) (lntypes.Hash, error) { + m.Lock() hash, ok := m.shards[id] + m.Unlock() if !ok { return lntypes.Hash{}, fmt.Errorf("hash for attempt id %v "+ "not found", id) From 06f045fca38ca28a809cec181d7d51d3d267626a Mon Sep 17 00:00:00 2001 From: "Johan T. Halseth" Date: Tue, 30 Mar 2021 14:53:29 +0200 Subject: [PATCH 05/17] channedb/mp_payment: add Hash to individual HTLCs For AMP payments the hash used for each HTLC will differ, and we will need to retrive it after a restart. We therefore persist it with each attempt. --- channeldb/mp_payment.go | 7 +++++++ channeldb/payments.go | 34 +++++++++++++++++++++++++++++++++- channeldb/payments_test.go | 5 ++++- routing/payment_lifecycle.go | 1 + 4 files changed, 45 insertions(+), 2 deletions(-) diff --git a/channeldb/mp_payment.go b/channeldb/mp_payment.go index 74a0ece8..4d14d504 100644 --- a/channeldb/mp_payment.go +++ b/channeldb/mp_payment.go @@ -29,6 +29,13 @@ type HTLCAttemptInfo struct { // AttemptTime is the time at which this HTLC was attempted. AttemptTime time.Time + + // Hash is the hash used for this single HTLC attempt. For AMP payments + // this will differ across attempts, for non-AMP payments each attempt + // will use the same hash. This can be nil for older payment attempts, + // in which the payment's PaymentHash in the PaymentCreationInfo should + // be used. + Hash *lntypes.Hash } // HTLCAttempt contains information about a specific HTLC attempt for a given diff --git a/channeldb/payments.go b/channeldb/payments.go index 7e659d05..2ffd76a0 100644 --- a/channeldb/payments.go +++ b/channeldb/payments.go @@ -926,7 +926,20 @@ func serializeHTLCAttemptInfo(w io.Writer, a *HTLCAttemptInfo) error { return err } - return serializeTime(w, a.AttemptTime) + if err := serializeTime(w, a.AttemptTime); err != nil { + return err + } + + // If the hash is nil we can just return. + if a.Hash == nil { + return nil + } + + if _, err := w.Write(a.Hash[:]); err != nil { + return err + } + + return nil } func deserializeHTLCAttemptInfo(r io.Reader) (*HTLCAttemptInfo, error) { @@ -935,6 +948,7 @@ func deserializeHTLCAttemptInfo(r io.Reader) (*HTLCAttemptInfo, error) { if err != nil { return nil, err } + a.Route, err = DeserializeRoute(r) if err != nil { return nil, err @@ -945,6 +959,24 @@ func deserializeHTLCAttemptInfo(r io.Reader) (*HTLCAttemptInfo, error) { return nil, err } + hash := lntypes.Hash{} + _, err = io.ReadFull(r, hash[:]) + + switch { + + // Older payment attempts wouldn't have the hash set, in which case we + // can just return. + case err == io.EOF, err == io.ErrUnexpectedEOF: + return a, nil + + case err != nil: + return nil, err + + default: + } + + a.Hash = &hash + return a, nil } diff --git a/channeldb/payments_test.go b/channeldb/payments_test.go index ca84613e..8974865a 100644 --- a/channeldb/payments_test.go +++ b/channeldb/payments_test.go @@ -57,8 +57,10 @@ func makeFakeInfo() (*PaymentCreationInfo, *HTLCAttemptInfo) { var preimg lntypes.Preimage copy(preimg[:], rev[:]) + hash := preimg.Hash() + c := &PaymentCreationInfo{ - PaymentHash: preimg.Hash(), + PaymentHash: hash, Value: 1000, // Use single second precision to avoid false positive test // failures due to the monotonic time component. @@ -71,6 +73,7 @@ func makeFakeInfo() (*PaymentCreationInfo, *HTLCAttemptInfo) { SessionKey: priv, Route: testRoute, AttemptTime: time.Unix(100, 0), + Hash: &hash, } return c, a } diff --git a/routing/payment_lifecycle.go b/routing/payment_lifecycle.go index 1760488a..7829b9d1 100644 --- a/routing/payment_lifecycle.go +++ b/routing/payment_lifecycle.go @@ -684,6 +684,7 @@ func (p *shardHandler) createNewPaymentAttempt(rt *route.Route, lastShard bool) AttemptTime: p.router.cfg.Clock.Now(), SessionKey: sessionKey, Route: *rt, + Hash: &hash, } return firstHop, htlcAdd, attempt, nil From e1399fb1ec1f6c21f9eaa5b604deb908f5dce183 Mon Sep 17 00:00:00 2001 From: "Johan T. Halseth" Date: Tue, 30 Mar 2021 15:00:25 +0200 Subject: [PATCH 06/17] routing/router: use attempt's unique hash if set on restart --- routing/router.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/routing/router.go b/routing/router.go index 7f54f915..7acfcaa8 100644 --- a/routing/router.go +++ b/routing/router.go @@ -609,7 +609,14 @@ func (r *ChannelRouter) Start() error { for _, a := range payment.HTLCs { a := a + // We check whether the individual attempts + // have their HTLC hash set, if not we'll fall + // back to the overall payment hash. hash := payment.Info.PaymentHash + if a.Hash != nil { + hash = *a.Hash + } + htlcs[a.AttemptID] = hash } From c1e82e534d25cc09aba349d063f8703186d72d8e Mon Sep 17 00:00:00 2001 From: "Johan T. Halseth" Date: Thu, 8 Apr 2021 12:08:06 +0200 Subject: [PATCH 07/17] amp: add Merge and Zero to Sharer interface --- amp/derivation_test.go | 28 ++++++++++++++++++++++++++++ amp/sharer.go | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 64 insertions(+) diff --git a/amp/derivation_test.go b/amp/derivation_test.go index 4ed533be..af8162d1 100644 --- a/amp/derivation_test.go +++ b/amp/derivation_test.go @@ -10,6 +10,7 @@ import ( type sharerTest struct { name string numShares int + merge bool } var sharerTests = []sharerTest{ @@ -25,6 +26,16 @@ var sharerTests = []sharerTest{ name: "many shares", numShares: 10, }, + { + name: "merge 4 shares", + numShares: 4, + merge: true, + }, + { + name: "merge many shares", + numShares: 20, + merge: true, + }, } // TestSharer executes the end-to-end derivation between sender and receiver, @@ -71,10 +82,27 @@ func testSharer(t *testing.T, test sharerTest) { // Compute the final share and finalize the sharing. child := sharer.Child(0) + sharer = sharer.Zero() assertChildShare(t, child, 0) children = append(children, child) + // If we are testing merging, merge half of the created children back + // into the sharer. + if test.merge { + for i := len(children) / 2; i < len(children); i++ { + sharer = sharer.Merge(children[i]) + } + children = children[:len(children)/2] + + // We must create a new last child from what we just merged + // back. + child := sharer.Child(0) + + assertChildShare(t, child, 0) + children = append(children, child) + } + assertReconstruction(t, children...) } diff --git a/amp/sharer.go b/amp/sharer.go index de27550c..c8a8d059 100644 --- a/amp/sharer.go +++ b/amp/sharer.go @@ -2,8 +2,12 @@ package amp import ( "crypto/rand" + "fmt" ) +// zeroShare is the all-zero 32-byte share. +var zeroShare = Share{} + // Sharer facilitates dynamic splitting of a root share value and derivation of // child preimage and hashes for individual HTLCs in an AMP payment. A sharer // represents a specific node in an abstract binary tree that can generate up to @@ -34,6 +38,16 @@ type Sharer interface { // that the shares of all nodes descending from the parent will XOR to // the parent's share. Split() (Sharer, Sharer, error) + + // Merge takes the given Child and "merges" it into the Sharer by + // XOR-ing its share with the Sharer's current share. + Merge(*Child) Sharer + + // Zero returns a a new "zero Sharer" that has its current share set to + // zero, while keeping the root share. Merging a Child from the + // original Sharer into this zero-Sharer gives back the original + // Sharer. + Zero() Sharer } // SeedSharer orchestrates the sharing of the root AMP seed along multiple @@ -81,6 +95,11 @@ func (s *SeedSharer) Root() Share { // parent share should no longer be used, and the caller should use the Child // method on each to derive preimage/hash pairs for the HTLCs. func (s *SeedSharer) Split() (Sharer, Sharer, error) { + // We cannot split the zero-Sharer. + if s.curr == zeroShare { + return nil, nil, fmt.Errorf("cannot split zero-Sharer") + } + shareLeft, shareRight, err := split(&s.curr) if err != nil { return nil, nil, err @@ -92,6 +111,23 @@ func (s *SeedSharer) Split() (Sharer, Sharer, error) { return left, right, nil } +// Merge takes the given Child and "merges" it into the Sharer by XOR-ing its +// share with the Sharer's current share. +func (s *SeedSharer) Merge(child *Child) Sharer { + var curr Share + curr.Xor(&s.curr, &child.Share) + + sharer := initSeedSharer(&s.root, &curr) + return sharer +} + +// Zero returns a a new "zero Sharer" that has its current share set to zero, +// while keeping the root share. Merging a Child from the original Sharer into +// this zero-Sharer gives back the original Sharer. +func (s *SeedSharer) Zero() Sharer { + return initSeedSharer(&s.root, &zeroShare) +} + // Child derives a preimage/hash pair to be used for an AMP HTLC. // All children of s will use the same underlying share, but have unique // preimage and hash. This can be used to rerandomize the preimage/hash pair for From 2d397b12b1dc0b010e1a4fc64b2c2d627ea70c3f Mon Sep 17 00:00:00 2001 From: "Johan T. Halseth" Date: Mon, 12 Apr 2021 15:05:01 +0200 Subject: [PATCH 08/17] amp: create amp.ShardTracker We'll use this AMP-specific ShardTracker for AMP payments. It will be used to derive hashes for each HTLC attempt using the underlying AMP derivation scheme. --- amp/shard_tracker.go | 165 ++++++++++++++++++++++++++++++++++++++ amp/shard_tracker_test.go | 95 ++++++++++++++++++++++ 2 files changed, 260 insertions(+) create mode 100644 amp/shard_tracker.go create mode 100644 amp/shard_tracker_test.go diff --git a/amp/shard_tracker.go b/amp/shard_tracker.go new file mode 100644 index 00000000..473447e4 --- /dev/null +++ b/amp/shard_tracker.go @@ -0,0 +1,165 @@ +package amp + +import ( + "crypto/rand" + "encoding/binary" + "fmt" + "sync" + + "github.com/lightningnetwork/lnd/lntypes" + "github.com/lightningnetwork/lnd/lnwire" + "github.com/lightningnetwork/lnd/record" + "github.com/lightningnetwork/lnd/routing/shards" +) + +// Shard is an implementation of the shards.PaymentShards interface specific +// to AMP payments. +type Shard struct { + child *Child + mpp *record.MPP + amp *record.AMP +} + +// A compile time check to ensure Shard implements the shards.PaymentShard +// interface. +var _ shards.PaymentShard = (*Shard)(nil) + +// Hash returns the hash used for the HTLC representing this AMP shard. +func (s *Shard) Hash() lntypes.Hash { + return s.child.Hash +} + +// MPP returns any extra MPP records that should be set for the final hop on +// the route used by this shard. +func (s *Shard) MPP() *record.MPP { + return s.mpp +} + +// AMP returns any extra AMP records that should be set for the final hop on +// the route used by this shard. +func (s *Shard) AMP() *record.AMP { + return s.amp +} + +// ShardTracker is an implementation of the shards.ShardTracker interface +// that is able to generate payment shards according to the AMP splitting +// algorithm. It can be used to generate new hashes to use for HTLCs, and also +// cancel shares used for failed payment shards. +type ShardTracker struct { + setID [32]byte + paymentAddr [32]byte + totalAmt lnwire.MilliSatoshi + + sharer Sharer + + shards map[uint64]*Child + sync.Mutex +} + +// A compile time check to ensure ShardTracker implements the +// shards.ShardTracker interface. +var _ shards.ShardTracker = (*ShardTracker)(nil) + +// NewShardTracker creates a new shard tracker to use for AMP payments. The +// root shard, setID, payment address and total amount must be correctly set in +// order for the TLV options to include with each shard to be created +// correctly. +func NewShardTracker(root, setID, payAddr [32]byte, + totalAmt lnwire.MilliSatoshi) *ShardTracker { + + // Create a new seed sharer from this root. + rootShare := Share(root) + rootSharer := SeedSharerFromRoot(&rootShare) + + return &ShardTracker{ + setID: setID, + paymentAddr: payAddr, + totalAmt: totalAmt, + sharer: rootSharer, + shards: make(map[uint64]*Child), + } +} + +// NewShard registers a new attempt with the ShardTracker and returns a +// new shard representing this attempt. This attempt's shard should be canceled +// if it ends up not being used by the overall payment, i.e. if the attempt +// fails. +func (s *ShardTracker) NewShard(pid uint64, last bool) (shards.PaymentShard, + error) { + + s.Lock() + defer s.Unlock() + + // Use a random child index. + var childIndex [4]byte + if _, err := rand.Read(childIndex[:]); err != nil { + return nil, err + } + idx := binary.BigEndian.Uint32(childIndex[:]) + + // Depending on whether we are requesting the last shard or not, either + // split the current share into two, or get a Child directly from the + // current sharer. + var child *Child + if last { + child = s.sharer.Child(idx) + + // If this was the last shard, set the current share to the + // zero share to indicate we cannot split it further. + s.sharer = s.sharer.Zero() + } else { + left, sharer, err := s.sharer.Split() + if err != nil { + return nil, err + } + + s.sharer = sharer + child = left.Child(idx) + } + + // Track the new child and return the shard. + s.shards[pid] = child + + mpp := record.NewMPP(s.totalAmt, s.paymentAddr) + amp := record.NewAMP( + child.ChildDesc.Share, s.setID, child.ChildDesc.Index, + ) + + return &Shard{ + child: child, + mpp: mpp, + amp: amp, + }, nil +} + +// CancelShard cancel's the shard corresponding to the given attempt ID. +func (s *ShardTracker) CancelShard(pid uint64) error { + s.Lock() + defer s.Unlock() + + c, ok := s.shards[pid] + if !ok { + return fmt.Errorf("pid not found") + } + delete(s.shards, pid) + + // Now that we are canceling this shard, we XOR the share back into our + // current share. + s.sharer = s.sharer.Merge(c) + return nil +} + +// GetHash retrieves the hash used by the shard of the given attempt ID. This +// will return an error if the attempt ID is unknown. +func (s *ShardTracker) GetHash(pid uint64) (lntypes.Hash, error) { + s.Lock() + defer s.Unlock() + + c, ok := s.shards[pid] + if !ok { + return lntypes.Hash{}, fmt.Errorf("AMP shard for attempt %v "+ + "not found", pid) + } + + return c.Hash, nil +} diff --git a/amp/shard_tracker_test.go b/amp/shard_tracker_test.go new file mode 100644 index 00000000..4f0ca982 --- /dev/null +++ b/amp/shard_tracker_test.go @@ -0,0 +1,95 @@ +package amp_test + +import ( + "crypto/rand" + "testing" + + "github.com/lightningnetwork/lnd/amp" + "github.com/lightningnetwork/lnd/lnwire" + "github.com/lightningnetwork/lnd/routing/shards" + "github.com/stretchr/testify/require" +) + +// TestAMPShardTracker tests that we can derive and cancel shards at will using +// the AMP shard tracker. +func TestAMPShardTracker(t *testing.T) { + var root, setID, payAddr [32]byte + _, err := rand.Read(root[:]) + require.NoError(t, err) + + _, err = rand.Read(setID[:]) + require.NoError(t, err) + + _, err = rand.Read(payAddr[:]) + require.NoError(t, err) + + var totalAmt lnwire.MilliSatoshi = 1000 + + // Create an AMP shard tracker using the random data we just generated. + tracker := amp.NewShardTracker(root, setID, payAddr, totalAmt) + + // Trying to retrieve a hash for id 0 should result in an error. + _, err = tracker.GetHash(0) + require.Error(t, err) + + // We start by creating 20 shards. + const numShards = 20 + + var shards []shards.PaymentShard + for i := uint64(0); i < numShards; i++ { + s, err := tracker.NewShard(i, i == numShards-1) + require.NoError(t, err) + + // Check that the shards have their payloads set as expected. + require.Equal(t, setID, s.AMP().SetID()) + require.Equal(t, totalAmt, s.MPP().TotalMsat()) + require.Equal(t, payAddr, s.MPP().PaymentAddr()) + + shards = append(shards, s) + } + + // Make sure we can retrieve the hash for all of them. + for i := uint64(0); i < numShards; i++ { + hash, err := tracker.GetHash(i) + require.NoError(t, err) + require.Equal(t, shards[i].Hash(), hash) + } + + // Now cancel half of the shards. + j := 0 + for i := uint64(0); i < numShards; i++ { + if i%2 == 0 { + err := tracker.CancelShard(i) + require.NoError(t, err) + continue + } + + // Keep shard. + shards[j] = shards[i] + j++ + } + shards = shards[:j] + + // Get a new last shard. + s, err := tracker.NewShard(numShards, true) + require.NoError(t, err) + shards = append(shards, s) + + // Finally make sure these shards together can be used to reconstruct + // the children. + childDescs := make([]amp.ChildDesc, len(shards)) + for i, s := range shards { + childDescs[i] = amp.ChildDesc{ + Share: s.AMP().RootShare(), + Index: s.AMP().ChildIndex(), + } + } + + // Using the child descriptors, reconstruct the children. + children := amp.ReconstructChildren(childDescs...) + + // Validate that the derived child preimages match the hash of each shard. + for i, child := range children { + require.Equal(t, shards[i].Hash(), child.Hash) + } +} From 5531b812e3a23d5330d5bcbf3ee84a762ea312d9 Mon Sep 17 00:00:00 2001 From: "Johan T. Halseth" Date: Mon, 12 Apr 2021 15:05:48 +0200 Subject: [PATCH 09/17] routing: use AMP shard tracker We'll use the AMP-specific ShardTracker for payments having non-nil AMPOptions. --- routing/router.go | 32 +++++++++++++++++++++++++++++--- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/routing/router.go b/routing/router.go index 7acfcaa8..75e04a51 100644 --- a/routing/router.go +++ b/routing/router.go @@ -15,6 +15,7 @@ import ( "github.com/go-errors/errors" sphinx "github.com/lightningnetwork/lightning-onion" + "github.com/lightningnetwork/lnd/amp" "github.com/lightningnetwork/lnd/batch" "github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/channeldb/kvdb" @@ -1722,6 +1723,10 @@ type LightningPayment struct { // the first hop. PaymentHash [32]byte + // amp is an optional field that is set if and only if this is am AMP + // payment. + amp *AMPOptions + // FinalCLTVDelta is the CTLV expiry delta to use for the _final_ hop // in the route. This means that the final hop will have a CLTV delta // of at least: currentHeight + FinalCLTVDelta. @@ -1789,6 +1794,13 @@ type LightningPayment struct { MaxShardAmt *lnwire.MilliSatoshi } +// AMPOptions houses information that must be known in order to send an AMP +// payment. +type AMPOptions struct { + SetID [32]byte + RootShare [32]byte +} + // SendPayment attempts to send a payment as described within the passed // LightningPayment. This function is blocking and will return either: when the // payment is successful, or all candidates routes have been attempted and @@ -1893,9 +1905,23 @@ func (r *ChannelRouter) preparePayment(payment *LightningPayment) ( // Create a new ShardTracker that we'll use during the life cycle of // this payment. - shardTracker := shards.NewSimpleShardTracker( - payment.PaymentHash, nil, - ) + var shardTracker shards.ShardTracker + switch { + + // If this is an AMP payment, we'll use the AMP shard tracker. + case payment.amp != nil: + shardTracker = amp.NewShardTracker( + payment.amp.RootShare, payment.amp.SetID, + *payment.PaymentAddr, payment.Amount, + ) + + // Otherwise we'll use the simple tracker that will map each attempt to + // the same payment hash. + default: + shardTracker = shards.NewSimpleShardTracker( + payment.PaymentHash, nil, + ) + } err = r.cfg.Control.InitPayment(payment.PaymentHash, info) if err != nil { From 6104d12cf8fd7bdd23a24ffdb0cae3373c653b03 Mon Sep 17 00:00:00 2001 From: "Johan T. Halseth" Date: Tue, 30 Mar 2021 15:21:59 +0200 Subject: [PATCH 10/17] routerrpc: add amp bool to SendPayment For now this is how you indicate you want to perform an AMP payment to the destination. --- lnrpc/routerrpc/router.pb.go | 390 ++++++++++++++-------------- lnrpc/routerrpc/router.proto | 5 + lnrpc/routerrpc/router.swagger.json | 5 + 3 files changed, 210 insertions(+), 190 deletions(-) diff --git a/lnrpc/routerrpc/router.pb.go b/lnrpc/routerrpc/router.pb.go index 459140ee..55215607 100644 --- a/lnrpc/routerrpc/router.pb.go +++ b/lnrpc/routerrpc/router.pb.go @@ -356,7 +356,10 @@ type SendPaymentRequest struct { //splitting is necessary. Setting this value will effectively cause lnd to //split more aggressively, vs only when it thinks it needs to. Note that this //value is in milli-satoshis. - MaxShardSizeMsat uint64 `protobuf:"varint,21,opt,name=max_shard_size_msat,json=maxShardSizeMsat,proto3" json:"max_shard_size_msat,omitempty"` + MaxShardSizeMsat uint64 `protobuf:"varint,21,opt,name=max_shard_size_msat,json=maxShardSizeMsat,proto3" json:"max_shard_size_msat,omitempty"` + // + //If set, an AMP-payment will be attempted. + Amp bool `protobuf:"varint,22,opt,name=amp,proto3" json:"amp,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -535,6 +538,13 @@ func (m *SendPaymentRequest) GetMaxShardSizeMsat() uint64 { return 0 } +func (m *SendPaymentRequest) GetAmp() bool { + if m != nil { + return m.Amp + } + return false +} + type TrackPaymentRequest struct { // The hash of the payment to look up. PaymentHash []byte `protobuf:"bytes,1,opt,name=payment_hash,json=paymentHash,proto3" json:"payment_hash,omitempty"` @@ -2485,195 +2495,195 @@ func init() { func init() { proto.RegisterFile("routerrpc/router.proto", fileDescriptor_7a0613f69d37b0a5) } var fileDescriptor_7a0613f69d37b0a5 = []byte{ - // 2997 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x59, 0x4b, 0x77, 0xdb, 0xc8, - 0xb1, 0x1e, 0x90, 0x14, 0x45, 0x16, 0x1f, 0x82, 0x5a, 0xb2, 0xc5, 0x4b, 0xf9, 0xa1, 0xc1, 0x3c, - 0xcc, 0xeb, 0x3b, 0x23, 0x7b, 0x34, 0xf7, 0xce, 0xcc, 0xbd, 0xf3, 0xb8, 0x43, 0x91, 0x90, 0x05, - 0x9b, 0x22, 0x35, 0x4d, 0xca, 0x63, 0x8f, 0x17, 0x08, 0x44, 0x36, 0x45, 0x8c, 0xf0, 0x60, 0x80, - 0xa6, 0x6d, 0xcd, 0x2a, 0xc9, 0x2a, 0x27, 0x3f, 0x26, 0xbf, 0x20, 0xe7, 0x24, 0x9b, 0x6c, 0xf2, - 0x27, 0xb2, 0xcd, 0x36, 0x9b, 0xac, 0x73, 0xfa, 0x01, 0x10, 0xa0, 0x28, 0xd9, 0x79, 0x6c, 0x24, - 0xe0, 0xab, 0xaf, 0xab, 0xab, 0xbb, 0xab, 0xaa, 0x0b, 0x45, 0xb8, 0x19, 0xf8, 0x33, 0x4a, 0x82, - 0x60, 0x3a, 0x7c, 0x20, 0x9e, 0x76, 0xa7, 0x81, 0x4f, 0x7d, 0x54, 0x8c, 0xf1, 0x7a, 0x31, 0x98, - 0x0e, 0x05, 0xaa, 0xfd, 0x71, 0x15, 0x50, 0x9f, 0x78, 0xa3, 0x63, 0xeb, 0xc2, 0x25, 0x1e, 0xc5, - 0xe4, 0xe7, 0x33, 0x12, 0x52, 0x84, 0x20, 0x37, 0x22, 0x21, 0xad, 0x29, 0x3b, 0x4a, 0xa3, 0x8c, - 0xf9, 0x33, 0x52, 0x21, 0x6b, 0xb9, 0xb4, 0x96, 0xd9, 0x51, 0x1a, 0x59, 0xcc, 0x1e, 0xd1, 0x7f, - 0x40, 0xc1, 0x72, 0xa9, 0xe9, 0x86, 0x16, 0xad, 0x95, 0x39, 0xbc, 0x6a, 0xb9, 0xf4, 0x28, 0xb4, - 0x28, 0x7a, 0x17, 0xca, 0x53, 0xa1, 0xd2, 0x9c, 0x58, 0xe1, 0xa4, 0x96, 0xe5, 0x8a, 0x4a, 0x12, - 0x3b, 0xb4, 0xc2, 0x09, 0x6a, 0x80, 0x3a, 0xb6, 0x3d, 0xcb, 0x31, 0x87, 0x0e, 0x7d, 0x69, 0x8e, - 0x88, 0x43, 0xad, 0x5a, 0x6e, 0x47, 0x69, 0xac, 0xe0, 0x2a, 0xc7, 0x5b, 0x0e, 0x7d, 0xd9, 0x66, - 0x68, 0x52, 0x99, 0x35, 0x1a, 0x05, 0xb5, 0xcd, 0x94, 0xb2, 0xe6, 0x68, 0x14, 0xa0, 0x7b, 0xb0, - 0x16, 0x51, 0x02, 0xb1, 0x86, 0xda, 0xca, 0x8e, 0xd2, 0x28, 0xe2, 0xea, 0x34, 0xbd, 0xb2, 0x7b, - 0xb0, 0x46, 0x6d, 0x97, 0xf8, 0x33, 0x6a, 0x86, 0x64, 0xe8, 0x7b, 0xa3, 0xb0, 0x96, 0x17, 0x93, - 0x4a, 0xb8, 0x2f, 0x50, 0xa4, 0x41, 0x65, 0x4c, 0x88, 0xe9, 0xd8, 0xae, 0x4d, 0x4d, 0xb6, 0xc2, - 0x55, 0xbe, 0xc2, 0xd2, 0x98, 0x90, 0x0e, 0xc3, 0xfa, 0x16, 0x45, 0xef, 0x43, 0x75, 0xce, 0xe1, - 0xdb, 0x50, 0xe1, 0xa4, 0x72, 0x44, 0xe2, 0x7b, 0xb1, 0x0b, 0xaa, 0x3f, 0xa3, 0x67, 0xbe, 0xed, - 0x9d, 0x99, 0xc3, 0x89, 0xe5, 0x99, 0xf6, 0xa8, 0x56, 0xd8, 0x51, 0x1a, 0xb9, 0xfd, 0x5c, 0x4d, - 0x79, 0xa8, 0xe0, 0x6a, 0x24, 0x6d, 0x4d, 0x2c, 0xcf, 0x18, 0xa1, 0xfb, 0xb0, 0xbe, 0xc8, 0x0f, - 0x6b, 0x1b, 0x3b, 0xd9, 0x46, 0x0e, 0xaf, 0xa5, 0xa9, 0x21, 0xfa, 0x10, 0xd6, 0x1c, 0x2b, 0xa4, - 0xe6, 0xc4, 0x9f, 0x9a, 0xd3, 0xd9, 0xe9, 0x39, 0xb9, 0xa8, 0x55, 0xf9, 0xee, 0x54, 0x18, 0x7c, - 0xe8, 0x4f, 0x8f, 0x39, 0x88, 0x6e, 0x03, 0xf0, 0x6d, 0xe6, 0xa6, 0xd6, 0x8a, 0x7c, 0xc5, 0x45, - 0x86, 0x70, 0x33, 0xd1, 0x27, 0x50, 0xe2, 0xee, 0x61, 0x4e, 0x6c, 0x8f, 0x86, 0x35, 0xd8, 0xc9, - 0x36, 0x4a, 0x7b, 0xea, 0xae, 0xe3, 0x31, 0x4f, 0xc1, 0x4c, 0x72, 0x68, 0x7b, 0x14, 0x43, 0x10, - 0x3d, 0x86, 0x68, 0x04, 0x1b, 0xcc, 0x2d, 0xcc, 0xe1, 0x2c, 0xa4, 0xbe, 0x6b, 0x06, 0x64, 0xe8, - 0x07, 0xa3, 0xb0, 0x56, 0xe2, 0x43, 0xff, 0x7b, 0x37, 0xf6, 0xb6, 0xdd, 0xcb, 0xee, 0xb5, 0xdb, - 0x26, 0x21, 0x6d, 0xf1, 0x71, 0x58, 0x0c, 0xd3, 0x3d, 0x1a, 0x5c, 0xe0, 0xf5, 0xd1, 0x22, 0x8e, - 0x3e, 0x02, 0x64, 0x39, 0x8e, 0xff, 0xca, 0x0c, 0x89, 0x33, 0x36, 0xe5, 0x59, 0xd6, 0xd6, 0x76, - 0x94, 0x46, 0x01, 0xab, 0x5c, 0xd2, 0x27, 0xce, 0x58, 0xaa, 0x47, 0x9f, 0x41, 0x85, 0xdb, 0x34, - 0x26, 0x16, 0x9d, 0x05, 0x24, 0xac, 0xa9, 0x3b, 0xd9, 0x46, 0x75, 0x6f, 0x5d, 0x2e, 0xe4, 0x40, - 0xc0, 0xfb, 0x36, 0xc5, 0x65, 0xc6, 0x93, 0xef, 0x21, 0xda, 0x86, 0xa2, 0x6b, 0xbd, 0x36, 0xa7, - 0x56, 0x40, 0xc3, 0xda, 0xfa, 0x8e, 0xd2, 0xa8, 0xe0, 0x82, 0x6b, 0xbd, 0x3e, 0x66, 0xef, 0x68, - 0x17, 0x36, 0x3c, 0xdf, 0xb4, 0xbd, 0xb1, 0x63, 0x9f, 0x4d, 0xa8, 0x39, 0x9b, 0x8e, 0x2c, 0x4a, - 0xc2, 0x1a, 0xe2, 0x36, 0xac, 0x7b, 0xbe, 0x21, 0x25, 0x27, 0x42, 0x80, 0x3e, 0x86, 0x0d, 0xa6, - 0x2c, 0x9c, 0x58, 0xc1, 0xc8, 0x0c, 0xed, 0x9f, 0x88, 0xf0, 0x8c, 0x1b, 0xec, 0xc4, 0xb1, 0xea, - 0x5a, 0xaf, 0xfb, 0x4c, 0xd2, 0xb7, 0x7f, 0x22, 0xcc, 0x3b, 0xea, 0x6d, 0xb8, 0xb9, 0x7c, 0x3b, - 0x58, 0xc0, 0xb1, 0xf3, 0x54, 0xf8, 0x40, 0xf6, 0x88, 0x36, 0x61, 0xe5, 0xa5, 0xe5, 0xcc, 0x08, - 0x0f, 0xc2, 0x32, 0x16, 0x2f, 0xff, 0x97, 0xf9, 0x42, 0xd1, 0x26, 0xb0, 0x31, 0x08, 0xac, 0xe1, - 0xf9, 0x42, 0x1c, 0x2f, 0x86, 0xa1, 0x72, 0x39, 0x0c, 0xaf, 0x58, 0x5e, 0xe6, 0x8a, 0xe5, 0x69, - 0xdf, 0xc0, 0x1a, 0x77, 0x88, 0x03, 0x42, 0xae, 0xcb, 0x16, 0x5b, 0xc0, 0x72, 0x01, 0x0f, 0x1c, - 0x91, 0x31, 0xf2, 0x96, 0xcb, 0x62, 0x46, 0x1b, 0x81, 0x3a, 0x1f, 0x1f, 0x4e, 0x7d, 0x2f, 0x24, - 0x2c, 0x15, 0x30, 0x7f, 0x61, 0x0e, 0xcf, 0xe2, 0x89, 0xef, 0x97, 0xc2, 0x47, 0x55, 0x25, 0x7e, - 0x40, 0xf8, 0x6e, 0x31, 0x7f, 0x67, 0x71, 0x6a, 0x3a, 0xfe, 0xf0, 0x9c, 0xe5, 0x0c, 0xeb, 0x42, - 0xaa, 0xaf, 0x30, 0xb8, 0xe3, 0x0f, 0xcf, 0xdb, 0x0c, 0xd4, 0x5e, 0x88, 0xb4, 0x36, 0xf0, 0xf9, - 0x5c, 0xff, 0xc0, 0x76, 0x68, 0xb0, 0xc2, 0x5d, 0x97, 0xab, 0x2d, 0xed, 0x95, 0x93, 0x31, 0x80, - 0x85, 0x48, 0x7b, 0x01, 0x1b, 0x29, 0xe5, 0x72, 0x15, 0x75, 0x28, 0x4c, 0x03, 0x62, 0xbb, 0xd6, - 0x19, 0x91, 0x9a, 0xe3, 0x77, 0xd4, 0x80, 0xd5, 0xb1, 0x65, 0x3b, 0xb3, 0x20, 0x52, 0x5c, 0x8d, - 0x7c, 0x52, 0xa0, 0x38, 0x12, 0x6b, 0xb7, 0xa0, 0x8e, 0x49, 0x48, 0xe8, 0x91, 0x1d, 0x86, 0xb6, - 0xef, 0xb5, 0x7c, 0x8f, 0x06, 0xbe, 0x23, 0x57, 0xa0, 0xdd, 0x86, 0xed, 0xa5, 0x52, 0x61, 0x02, - 0x1b, 0xfc, 0xdd, 0x8c, 0x04, 0x17, 0xcb, 0x07, 0x7f, 0x07, 0xdb, 0x4b, 0xa5, 0xd2, 0xfe, 0x8f, - 0x60, 0x65, 0x6a, 0xd9, 0x01, 0x3b, 0x7b, 0x16, 0xc3, 0x37, 0x13, 0x31, 0x7c, 0x6c, 0xd9, 0xc1, - 0xa1, 0x1d, 0x52, 0x3f, 0xb8, 0xc0, 0x82, 0xf4, 0x38, 0x57, 0x50, 0xd4, 0x8c, 0xd6, 0x81, 0x5b, - 0xcf, 0x0c, 0x77, 0xea, 0x07, 0xcb, 0xed, 0x9d, 0xeb, 0x54, 0xde, 0x42, 0xa7, 0x76, 0x17, 0x6e, - 0x5f, 0xa1, 0x4d, 0xae, 0xef, 0x37, 0x0a, 0x94, 0x12, 0xe3, 0x58, 0xe0, 0x7a, 0xfe, 0x88, 0x98, - 0xe3, 0xc0, 0x77, 0xa3, 0x3d, 0x67, 0xc0, 0x41, 0xe0, 0xbb, 0xcc, 0x05, 0xb9, 0x90, 0xfa, 0x32, - 0x5e, 0xf2, 0xec, 0x75, 0xe0, 0xa3, 0x8f, 0x61, 0x75, 0x22, 0x14, 0xf0, 0xa4, 0x5e, 0xda, 0xdb, - 0x58, 0x30, 0xab, 0x6d, 0x51, 0x0b, 0x47, 0x9c, 0xc7, 0xb9, 0x42, 0x56, 0xcd, 0x3d, 0xce, 0x15, - 0x72, 0xea, 0xca, 0xe3, 0x5c, 0x61, 0x45, 0xcd, 0x3f, 0xce, 0x15, 0xf2, 0xea, 0xaa, 0xf6, 0x17, - 0x05, 0x0a, 0x11, 0x9b, 0x59, 0xc2, 0x4e, 0xd0, 0x64, 0x6e, 0x28, 0x7d, 0xb7, 0xc0, 0x80, 0x81, - 0xed, 0x12, 0xb4, 0x03, 0x65, 0x2e, 0x4c, 0x47, 0x04, 0x30, 0xac, 0xc9, 0xa3, 0x82, 0xdf, 0x36, - 0x11, 0x83, 0xbb, 0x7f, 0x4e, 0xde, 0x36, 0x82, 0x12, 0xdd, 0xa9, 0xe1, 0x6c, 0x38, 0x24, 0x61, - 0x28, 0x66, 0x59, 0x11, 0x14, 0x89, 0xf1, 0x89, 0x3e, 0x84, 0xb5, 0x88, 0x12, 0xcd, 0x95, 0x17, - 0xe1, 0x21, 0x61, 0x39, 0x5d, 0x03, 0xd4, 0x24, 0xcf, 0x9d, 0xdf, 0x6f, 0xd5, 0x39, 0x91, 0x4d, - 0x2a, 0x16, 0xaf, 0xed, 0xc0, 0x9d, 0x47, 0x8b, 0x4e, 0xd7, 0xf2, 0xbd, 0xb1, 0x7d, 0x16, 0xf9, - 0xd6, 0x0f, 0x70, 0xf7, 0x4a, 0x86, 0xf4, 0xaf, 0xcf, 0x21, 0x3f, 0xe4, 0x08, 0xdf, 0x9f, 0xd2, - 0xde, 0xdd, 0xc4, 0xae, 0x2f, 0x1d, 0x28, 0xe9, 0xda, 0x73, 0xb8, 0xd3, 0xbf, 0x76, 0xf6, 0x7f, - 0x5e, 0xf5, 0xbb, 0x70, 0xb7, 0x7f, 0xbd, 0xd9, 0xda, 0x2f, 0x32, 0xb0, 0xb9, 0x8c, 0xc0, 0xee, - 0xe9, 0x89, 0xe5, 0x8c, 0x4d, 0xc7, 0x1e, 0x93, 0xb8, 0x98, 0x10, 0xd9, 0x7a, 0x8d, 0x09, 0x3a, - 0xf6, 0x98, 0x44, 0xd5, 0xc4, 0x3d, 0x58, 0xe3, 0x57, 0x74, 0xe0, 0x9f, 0x5a, 0xa7, 0xb6, 0x63, - 0x53, 0x91, 0xb7, 0x32, 0xb8, 0x3a, 0xf1, 0xa7, 0xc7, 0x73, 0x14, 0xdd, 0x84, 0xfc, 0x2b, 0xc2, - 0xf2, 0x2d, 0x2f, 0x99, 0x32, 0x58, 0xbe, 0xa1, 0xcf, 0x60, 0xcb, 0xb5, 0x5e, 0xdb, 0xee, 0xcc, - 0x35, 0xe7, 0x85, 0x4e, 0x38, 0x73, 0x68, 0xc8, 0x5d, 0xa5, 0x82, 0x6f, 0x48, 0x71, 0x7c, 0x03, - 0x70, 0x21, 0x6a, 0xc1, 0x1d, 0xd7, 0xf6, 0xf8, 0x38, 0x99, 0x61, 0xcc, 0x80, 0x38, 0xd6, 0x6b, - 0xd3, 0xf6, 0x28, 0x09, 0x5e, 0x5a, 0x0e, 0x77, 0xa3, 0x1c, 0xde, 0x96, 0xac, 0x28, 0x1f, 0x31, - 0x8e, 0x21, 0x29, 0xda, 0x8f, 0xb0, 0xc5, 0x13, 0x47, 0xc2, 0xd0, 0x68, 0xe7, 0x99, 0xdf, 0x07, - 0xbe, 0x6b, 0xb2, 0xd0, 0x8a, 0x22, 0x90, 0x01, 0x5d, 0x7f, 0x44, 0x58, 0x04, 0x52, 0x5f, 0x88, - 0x64, 0x04, 0x52, 0x9f, 0x0b, 0x92, 0x95, 0x63, 0x36, 0x55, 0x39, 0x6a, 0xe7, 0x50, 0xbb, 0x3c, - 0x97, 0xf4, 0xa0, 0x1d, 0x28, 0x25, 0x77, 0x90, 0x4d, 0xa7, 0xe0, 0x24, 0x94, 0x0c, 0xed, 0xcc, - 0x9b, 0x43, 0x5b, 0xfb, 0x93, 0x02, 0xeb, 0xfb, 0x33, 0xdb, 0x19, 0xa5, 0xae, 0x89, 0xa4, 0x75, - 0x4a, 0xba, 0xae, 0x5d, 0x56, 0xb4, 0x66, 0x96, 0x16, 0xad, 0x1f, 0x2d, 0xa9, 0xfa, 0xb2, 0xbc, - 0xea, 0xcb, 0x2c, 0xa9, 0xf9, 0xee, 0x42, 0x69, 0x5e, 0xc2, 0xb1, 0x23, 0xcd, 0x36, 0xca, 0x18, - 0x26, 0x51, 0xfd, 0x16, 0x5e, 0xaa, 0x81, 0x57, 0x2e, 0xd5, 0xc0, 0xda, 0x17, 0x80, 0x92, 0x6b, - 0x91, 0x7b, 0x16, 0x5f, 0x68, 0xca, 0xd5, 0x17, 0xda, 0x2d, 0xa8, 0xf7, 0x67, 0xa7, 0xe1, 0x30, - 0xb0, 0x4f, 0xc9, 0x21, 0x75, 0x86, 0xfa, 0x4b, 0xe2, 0xd1, 0x30, 0x0a, 0xed, 0xbf, 0xe5, 0xa0, - 0x18, 0xa3, 0xac, 0x5e, 0xb0, 0xbd, 0xa1, 0xef, 0x46, 0xeb, 0xf2, 0x88, 0xc3, 0x96, 0x26, 0xfc, - 0x7e, 0x3d, 0x12, 0xb5, 0x84, 0xc4, 0x18, 0x31, 0x7e, 0x6a, 0x1f, 0x24, 0x3f, 0x23, 0xf8, 0xc9, - 0x6d, 0x10, 0xfc, 0x06, 0xa8, 0xb1, 0xfe, 0x09, 0x75, 0x86, 0xf1, 0xbe, 0xe1, 0x6a, 0x84, 0x33, - 0x63, 0x04, 0x33, 0xd6, 0x1c, 0x31, 0x73, 0x82, 0x19, 0xe1, 0x92, 0xf9, 0x2e, 0x94, 0x59, 0xc6, - 0x0c, 0xa9, 0xe5, 0x4e, 0x4d, 0x2f, 0x94, 0x2e, 0x5f, 0x8a, 0xb1, 0x6e, 0x88, 0xbe, 0x06, 0x20, - 0x6c, 0x7d, 0x26, 0xbd, 0x98, 0x12, 0x9e, 0x34, 0xab, 0x7b, 0x77, 0x12, 0xbe, 0x13, 0x6f, 0xc0, - 0x2e, 0xff, 0x3b, 0xb8, 0x98, 0x12, 0x5c, 0x24, 0xd1, 0x23, 0xfa, 0x06, 0x2a, 0x63, 0x3f, 0x78, - 0xc5, 0x4a, 0x3e, 0x0e, 0xca, 0x8b, 0x65, 0x2b, 0xa1, 0xe1, 0x40, 0xc8, 0xf9, 0xf0, 0xc3, 0x77, - 0x70, 0x79, 0x9c, 0x78, 0x47, 0x4f, 0x00, 0x45, 0xe3, 0xf9, 0x3d, 0x20, 0x94, 0x14, 0xb8, 0x92, - 0xed, 0xcb, 0x4a, 0x58, 0x94, 0x46, 0x8a, 0xd4, 0xf1, 0x02, 0x86, 0xbe, 0x84, 0x72, 0x48, 0x28, - 0x75, 0x88, 0x54, 0x53, 0xe4, 0x6a, 0x6e, 0xa6, 0x6a, 0x72, 0x26, 0x8e, 0x34, 0x94, 0xc2, 0xf9, - 0x2b, 0xda, 0x87, 0x35, 0xc7, 0xf6, 0xce, 0x93, 0x66, 0x00, 0x1f, 0x5f, 0x4b, 0x8c, 0xef, 0xd8, - 0xde, 0x79, 0xd2, 0x86, 0x8a, 0x93, 0x04, 0xb4, 0xaf, 0xa0, 0x18, 0xef, 0x12, 0x2a, 0xc1, 0xea, - 0x49, 0xf7, 0x49, 0xb7, 0xf7, 0x7d, 0x57, 0x7d, 0x07, 0x15, 0x20, 0xd7, 0xd7, 0xbb, 0x6d, 0x55, - 0x61, 0x30, 0xd6, 0x5b, 0xba, 0xf1, 0x54, 0x57, 0x33, 0xec, 0xe5, 0xa0, 0x87, 0xbf, 0x6f, 0xe2, - 0xb6, 0x9a, 0xdd, 0x5f, 0x85, 0x15, 0x3e, 0xaf, 0xf6, 0x3b, 0x05, 0x0a, 0xfc, 0x04, 0xbd, 0xb1, - 0x8f, 0xfe, 0x0b, 0x62, 0xe7, 0xe2, 0xd7, 0x1f, 0xab, 0x00, 0xb9, 0xd7, 0x55, 0x70, 0xec, 0x30, - 0x03, 0x89, 0x33, 0x72, 0xec, 0x1a, 0x31, 0x39, 0x23, 0xc8, 0x91, 0x20, 0x26, 0xdf, 0x4f, 0x68, - 0x4e, 0x65, 0xa5, 0x1c, 0x5e, 0x8b, 0x04, 0xd1, 0x1d, 0x9c, 0xfc, 0x36, 0x4b, 0xdd, 0xd5, 0x89, - 0x6f, 0x33, 0xc9, 0xd5, 0x3e, 0x87, 0x72, 0xf2, 0xcc, 0xd1, 0x3d, 0xc8, 0xd9, 0xde, 0xd8, 0x97, - 0x81, 0xb8, 0xb1, 0xe0, 0x5c, 0x6c, 0x91, 0x98, 0x13, 0x34, 0x04, 0xea, 0xe2, 0x39, 0x6b, 0x15, - 0x28, 0x25, 0x0e, 0x4d, 0xfb, 0xb3, 0x02, 0x95, 0xd4, 0x21, 0xbc, 0xb5, 0x76, 0xf4, 0x35, 0x94, - 0x5f, 0xd9, 0x01, 0x31, 0x93, 0xf5, 0x68, 0x75, 0xaf, 0x9e, 0xae, 0x47, 0xa3, 0xff, 0x2d, 0x7f, - 0x44, 0x70, 0x89, 0xf1, 0x25, 0x80, 0xfe, 0x1f, 0xaa, 0xd1, 0x45, 0x32, 0x22, 0xd4, 0xb2, 0x1d, - 0xbe, 0x55, 0xd5, 0x94, 0x7b, 0x48, 0x6e, 0x9b, 0xcb, 0x71, 0x65, 0x9c, 0x7c, 0x45, 0x1f, 0xcc, - 0x15, 0x84, 0x34, 0xb0, 0xbd, 0x33, 0xbe, 0x7f, 0xc5, 0x98, 0xd6, 0xe7, 0x20, 0x2b, 0xf5, 0x2a, - 0xf2, 0x2e, 0xeb, 0x53, 0x8b, 0xce, 0xd8, 0x87, 0xd5, 0x4a, 0x48, 0x2d, 0x99, 0xc9, 0xaa, 0xa9, - 0xd8, 0x4a, 0x10, 0x09, 0x16, 0xac, 0x54, 0x39, 0x9e, 0xb9, 0x54, 0x8e, 0xaf, 0xb0, 0x8c, 0x21, - 0x12, 0x6d, 0x69, 0x0f, 0xc9, 0xc5, 0x1f, 0x0e, 0x3a, 0xad, 0x26, 0xa5, 0xc4, 0x9d, 0x52, 0x2c, - 0x08, 0xb2, 0xfe, 0xf9, 0x06, 0xa0, 0x65, 0x07, 0xc3, 0x99, 0x4d, 0x9f, 0x90, 0x0b, 0x76, 0xad, - 0x45, 0x19, 0x5d, 0xa4, 0xbd, 0xfc, 0x50, 0x64, 0xf1, 0x2d, 0x58, 0x8d, 0x12, 0x91, 0xc8, 0x6f, - 0xf9, 0x09, 0x4f, 0x40, 0xda, 0xef, 0x73, 0xb0, 0x2d, 0x8f, 0x54, 0x9c, 0x06, 0x25, 0xc1, 0x90, - 0x4c, 0xe3, 0xef, 0xb4, 0x47, 0xb0, 0x39, 0x4f, 0xaa, 0x62, 0x22, 0x33, 0xfa, 0xf6, 0x2b, 0xed, - 0xdd, 0x48, 0xac, 0x74, 0x6e, 0x06, 0x46, 0x71, 0xb2, 0x9d, 0x9b, 0xf6, 0x30, 0xa1, 0xc8, 0x72, - 0xfd, 0x99, 0x27, 0x5d, 0x54, 0x64, 0x3c, 0x34, 0x77, 0x67, 0x26, 0xe2, 0x1e, 0x7d, 0x0f, 0x62, - 0x27, 0x37, 0xc9, 0xeb, 0xa9, 0x1d, 0x5c, 0xf0, 0xec, 0x57, 0x99, 0xa7, 0x5b, 0x9d, 0xa3, 0x97, - 0x3e, 0x9e, 0x32, 0x97, 0x3f, 0x9e, 0xbe, 0x84, 0x7a, 0x1c, 0x1d, 0xb2, 0x0d, 0x43, 0x46, 0xf1, - 0xed, 0xb7, 0xca, 0x6d, 0xd8, 0x8a, 0x18, 0x38, 0x22, 0xc8, 0x2b, 0xf0, 0x21, 0x6c, 0x26, 0x42, - 0x6b, 0x6e, 0xba, 0x88, 0x44, 0x34, 0x8f, 0xae, 0xa4, 0xe9, 0xf1, 0x08, 0x69, 0xba, 0xa8, 0x85, - 0xe2, 0xfc, 0x2f, 0x4d, 0xff, 0x19, 0x54, 0x17, 0xda, 0x14, 0x05, 0x7e, 0xee, 0xff, 0x7b, 0x39, - 0xb3, 0x2e, 0x3b, 0x9e, 0xdd, 0x25, 0xbd, 0x8a, 0xca, 0x30, 0xd5, 0xa7, 0xb8, 0x0d, 0xe0, 0x7b, - 0xb6, 0xef, 0x99, 0xa7, 0x8e, 0x7f, 0xca, 0x13, 0x6e, 0x19, 0x17, 0x39, 0xb2, 0xef, 0xf8, 0xa7, - 0xf5, 0x6f, 0x01, 0xfd, 0x8b, 0x1f, 0xf8, 0x7f, 0x50, 0xe0, 0xd6, 0x72, 0x13, 0xe5, 0x3d, 0xff, - 0x6f, 0x73, 0xa1, 0x2f, 0x21, 0x6f, 0x0d, 0xa9, 0xed, 0x7b, 0x32, 0x33, 0xbc, 0x97, 0x18, 0x8a, - 0x49, 0xe8, 0x3b, 0x2f, 0xc9, 0xa1, 0xef, 0x8c, 0xa4, 0x31, 0x4d, 0x4e, 0xc5, 0x72, 0x48, 0x2a, - 0xe8, 0xb2, 0xe9, 0xa0, 0xd3, 0x7e, 0xa5, 0xc0, 0x96, 0xe8, 0x22, 0xb0, 0x13, 0x17, 0x41, 0x1d, - 0x05, 0xc0, 0x1e, 0x00, 0x77, 0x93, 0xa9, 0x6f, 0x7b, 0x34, 0xce, 0x61, 0x22, 0x2a, 0x65, 0x6d, - 0x70, 0xcc, 0x44, 0xb8, 0xc8, 0x68, 0xfc, 0x11, 0x7d, 0xba, 0x60, 0x68, 0xf2, 0x9e, 0x9c, 0xcf, - 0x90, 0x36, 0x50, 0xab, 0x43, 0xed, 0xb2, 0x0d, 0x62, 0x0b, 0xef, 0xff, 0x32, 0x07, 0x95, 0x54, - 0xea, 0x4a, 0xdf, 0x5d, 0x15, 0x28, 0x76, 0x7b, 0x66, 0x5b, 0x1f, 0x34, 0x8d, 0x8e, 0xaa, 0x20, - 0x15, 0xca, 0xbd, 0xae, 0xd1, 0xeb, 0x9a, 0x6d, 0xbd, 0xd5, 0x6b, 0xb3, 0x5b, 0xec, 0x06, 0xac, - 0x77, 0x8c, 0xee, 0x13, 0xb3, 0xdb, 0x1b, 0x98, 0x7a, 0xc7, 0x78, 0x64, 0xec, 0x77, 0x74, 0x35, - 0x8b, 0x36, 0x41, 0xed, 0x75, 0xcd, 0xd6, 0x61, 0xd3, 0xe8, 0x9a, 0x03, 0xe3, 0x48, 0xef, 0x9d, - 0x0c, 0xd4, 0x1c, 0x43, 0x59, 0xba, 0x31, 0xf5, 0x67, 0x2d, 0x5d, 0x6f, 0xf7, 0xcd, 0xa3, 0xe6, - 0x33, 0x75, 0x05, 0xd5, 0x60, 0xd3, 0xe8, 0xf6, 0x4f, 0x0e, 0x0e, 0x8c, 0x96, 0xa1, 0x77, 0x07, - 0xe6, 0x7e, 0xb3, 0xd3, 0xec, 0xb6, 0x74, 0x35, 0x8f, 0x6e, 0x02, 0x32, 0xba, 0xad, 0xde, 0xd1, - 0x71, 0x47, 0x1f, 0xe8, 0x66, 0x74, 0x5b, 0xae, 0xa2, 0x0d, 0x58, 0xe3, 0x7a, 0x9a, 0xed, 0xb6, - 0x79, 0xd0, 0x34, 0x3a, 0x7a, 0x5b, 0x2d, 0x30, 0x4b, 0x24, 0xa3, 0x6f, 0xb6, 0x8d, 0x7e, 0x73, - 0x9f, 0xc1, 0x45, 0x36, 0xa7, 0xd1, 0x7d, 0xda, 0x33, 0x5a, 0xba, 0xd9, 0x62, 0x6a, 0x19, 0x0a, - 0x8c, 0x1c, 0xa1, 0x27, 0xdd, 0xb6, 0x8e, 0x8f, 0x9b, 0x46, 0x5b, 0x2d, 0xa1, 0x6d, 0xd8, 0x8a, - 0x60, 0xfd, 0xd9, 0xb1, 0x81, 0x9f, 0x9b, 0x83, 0x5e, 0xcf, 0xec, 0xf7, 0x7a, 0x5d, 0xb5, 0x9c, - 0xd4, 0xc4, 0x56, 0xdb, 0x3b, 0xd6, 0xbb, 0x6a, 0x05, 0x6d, 0xc1, 0xc6, 0xd1, 0xf1, 0xb1, 0x19, - 0x49, 0xa2, 0xc5, 0x56, 0x19, 0xbd, 0xd9, 0x6e, 0x63, 0xbd, 0xdf, 0x37, 0x8f, 0x8c, 0xfe, 0x51, - 0x73, 0xd0, 0x3a, 0x54, 0xd7, 0xd8, 0x92, 0xfa, 0xfa, 0xc0, 0x1c, 0xf4, 0x06, 0xcd, 0xce, 0x1c, - 0x57, 0x99, 0x41, 0x73, 0x9c, 0x4d, 0xda, 0xe9, 0x7d, 0xaf, 0xae, 0xb3, 0x0d, 0x67, 0x70, 0xef, - 0xa9, 0x34, 0x11, 0xb1, 0xb5, 0xcb, 0xe3, 0x89, 0xe6, 0x54, 0x37, 0x18, 0x68, 0x74, 0x9f, 0x36, - 0x3b, 0x46, 0xdb, 0x7c, 0xa2, 0x3f, 0xe7, 0xd5, 0xc6, 0x26, 0x03, 0x85, 0x65, 0xe6, 0x31, 0xee, - 0x3d, 0x62, 0x86, 0xa8, 0x37, 0x10, 0x82, 0x6a, 0xcb, 0xc0, 0xad, 0x93, 0x4e, 0x13, 0x9b, 0xb8, - 0x77, 0x32, 0xd0, 0xd5, 0x9b, 0xf7, 0x7f, 0xab, 0x40, 0x39, 0x79, 0x9b, 0xb0, 0x53, 0x37, 0xba, - 0xe6, 0x41, 0xc7, 0x78, 0x74, 0x38, 0x10, 0x4e, 0xd0, 0x3f, 0x69, 0xb1, 0x23, 0xd3, 0x59, 0x15, - 0x83, 0xa0, 0x2a, 0x36, 0x3d, 0x5e, 0x6c, 0x86, 0xcd, 0x25, 0xb1, 0x6e, 0x4f, 0xea, 0xcd, 0x32, - 0xe3, 0x25, 0xa8, 0x63, 0xdc, 0xc3, 0x6a, 0x0e, 0xbd, 0x0f, 0x3b, 0x12, 0x61, 0xe7, 0x8a, 0xb1, - 0xde, 0x1a, 0x98, 0xc7, 0xcd, 0xe7, 0x47, 0xec, 0xd8, 0x85, 0x93, 0xf5, 0xd5, 0x15, 0x74, 0x17, - 0xb6, 0x63, 0xd6, 0x32, 0xbf, 0xb8, 0xff, 0x15, 0xd4, 0xae, 0x8a, 0x4a, 0x04, 0x90, 0xef, 0xeb, - 0x83, 0x41, 0x47, 0x17, 0x95, 0xd7, 0x81, 0x70, 0x5c, 0x80, 0x3c, 0xd6, 0xfb, 0x27, 0x47, 0xba, - 0x9a, 0xb9, 0xff, 0x3f, 0xa0, 0x2e, 0x86, 0x0a, 0x93, 0xeb, 0x5d, 0xe6, 0x32, 0xea, 0x3b, 0x2c, - 0x00, 0xa4, 0xff, 0xa8, 0x0a, 0x53, 0xd1, 0x3c, 0x19, 0xf4, 0xd4, 0xcc, 0xde, 0x5f, 0x4b, 0x90, - 0xe7, 0x5f, 0x10, 0x01, 0xfa, 0x16, 0x2a, 0x89, 0x0e, 0xef, 0xd3, 0x3d, 0x74, 0xfb, 0xda, 0xde, - 0x6f, 0x3d, 0x6a, 0x7c, 0x49, 0xf8, 0xa1, 0x82, 0xf6, 0xa1, 0x9a, 0xec, 0x5d, 0x3e, 0xdd, 0x43, - 0xc9, 0xc2, 0x7b, 0x49, 0x5b, 0x73, 0x89, 0x8e, 0x27, 0xa0, 0xea, 0x21, 0xb5, 0x5d, 0x76, 0xff, - 0xcb, 0xee, 0x22, 0xaa, 0x27, 0x13, 0x57, 0xba, 0x65, 0x59, 0xdf, 0x5e, 0x2a, 0x93, 0xa9, 0xf4, - 0x3b, 0x56, 0x6b, 0xc5, 0xfd, 0xbd, 0x4b, 0x0b, 0x4a, 0x37, 0x15, 0xeb, 0x77, 0xae, 0x12, 0xcb, - 0xfe, 0x41, 0xf6, 0xd7, 0x19, 0xb6, 0xc6, 0x4a, 0x42, 0xb6, 0x64, 0x97, 0x16, 0x94, 0x2e, 0xa9, - 0x48, 0xd0, 0x08, 0x36, 0x96, 0xf4, 0xfe, 0xd0, 0x07, 0xe9, 0xfc, 0x7c, 0x45, 0xe7, 0xb0, 0xfe, - 0xe1, 0x9b, 0x68, 0x72, 0xf1, 0x23, 0xd8, 0x58, 0xd2, 0x24, 0x4c, 0xcd, 0x72, 0x75, 0x8b, 0x31, - 0x35, 0xcb, 0x75, 0xbd, 0xc6, 0x1f, 0xe1, 0xc6, 0xd2, 0x4e, 0x1f, 0xba, 0x97, 0x50, 0x70, 0x5d, - 0x67, 0xb1, 0xde, 0x78, 0x33, 0x51, 0xce, 0x35, 0x85, 0xad, 0x2b, 0x5a, 0x53, 0xe8, 0x3f, 0x13, - 0x4a, 0xae, 0x6f, 0x70, 0xd5, 0xef, 0xbf, 0x0d, 0x75, 0x3e, 0x63, 0xff, 0x2d, 0x66, 0xec, 0xbf, - 0xfd, 0x8c, 0x6f, 0x68, 0x52, 0xa1, 0x17, 0xa0, 0x2e, 0x76, 0x4d, 0x90, 0xb6, 0x78, 0x16, 0x97, - 0xdb, 0x37, 0xf5, 0xf7, 0xae, 0xe5, 0x48, 0xe5, 0x06, 0xc0, 0xbc, 0xb1, 0x80, 0x6e, 0x25, 0x86, - 0x5c, 0xea, 0x9d, 0xd4, 0x6f, 0x5f, 0x21, 0x95, 0xaa, 0x06, 0xb0, 0xb1, 0xa4, 0xd3, 0x90, 0xf2, - 0xae, 0xab, 0x3b, 0x11, 0xf5, 0xcd, 0x65, 0x1f, 0xe4, 0x0f, 0x15, 0x74, 0x24, 0x02, 0x36, 0xfa, - 0x19, 0xe8, 0x0d, 0x19, 0xa8, 0xb6, 0xfc, 0xc3, 0x61, 0x16, 0xf2, 0x50, 0x7d, 0xa8, 0xa0, 0x1e, - 0x94, 0x93, 0x59, 0xe7, 0x8d, 0xe9, 0xe8, 0x8d, 0x0a, 0xc7, 0xb0, 0x96, 0x2a, 0xda, 0xfc, 0x20, - 0xe5, 0xe7, 0xd7, 0xd5, 0x75, 0xa9, 0x88, 0xba, 0xa6, 0x46, 0x6d, 0xb0, 0x79, 0x5e, 0x80, 0xba, - 0x58, 0xdc, 0xa4, 0xbc, 0xe0, 0x8a, 0xea, 0x2b, 0xe5, 0x05, 0x57, 0x55, 0x47, 0xfb, 0x9f, 0xfc, - 0xf0, 0xe0, 0xcc, 0xa6, 0x93, 0xd9, 0xe9, 0xee, 0xd0, 0x77, 0x1f, 0xf0, 0xdf, 0x84, 0x3c, 0xdb, - 0x3b, 0xf3, 0x08, 0x7d, 0xe5, 0x07, 0xe7, 0x0f, 0x1c, 0x6f, 0xf4, 0x80, 0xe7, 0xac, 0x07, 0xb1, - 0xae, 0xd3, 0x3c, 0xff, 0x91, 0xf9, 0xd3, 0xbf, 0x07, 0x00, 0x00, 0xff, 0xff, 0xc3, 0x50, 0x5a, - 0xbb, 0x94, 0x1e, 0x00, 0x00, + // 3003 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x59, 0x4b, 0x7b, 0xdb, 0xc6, + 0xd5, 0x0e, 0x28, 0x8a, 0x22, 0x0f, 0x2f, 0x82, 0x46, 0xb2, 0xc4, 0x8f, 0xf2, 0x45, 0x41, 0x12, + 0x9b, 0x9f, 0xbf, 0x44, 0x76, 0x94, 0xaf, 0x49, 0xda, 0x5c, 0x1a, 0x8a, 0x84, 0x2c, 0xd8, 0x14, + 0xa9, 0x0c, 0x29, 0xc7, 0x8e, 0x17, 0x28, 0x44, 0x0e, 0x45, 0x44, 0xb8, 0xb0, 0xc0, 0xd0, 0xb6, + 0xb2, 0x6a, 0xbb, 0xea, 0xd3, 0x1f, 0xd3, 0x5f, 0xd0, 0xe7, 0x69, 0xd7, 0xfd, 0x03, 0x5d, 0x76, + 0xdb, 0x6d, 0x37, 0x5d, 0xf7, 0x99, 0x0b, 0x40, 0x80, 0xa2, 0x64, 0xf7, 0xb2, 0x91, 0x80, 0xf7, + 0xbc, 0x73, 0xe6, 0xcc, 0xcc, 0x39, 0x67, 0x0e, 0x0e, 0x61, 0x33, 0xf0, 0xa7, 0x94, 0x04, 0xc1, + 0x64, 0xf0, 0x40, 0x3c, 0xed, 0x4e, 0x02, 0x9f, 0xfa, 0xa8, 0x10, 0xe3, 0xb5, 0x42, 0x30, 0x19, + 0x08, 0x54, 0xfb, 0xcb, 0x0a, 0xa0, 0x1e, 0xf1, 0x86, 0xc7, 0xd6, 0x85, 0x4b, 0x3c, 0x8a, 0xc9, + 0x2f, 0xa7, 0x24, 0xa4, 0x08, 0x41, 0x76, 0x48, 0x42, 0x5a, 0x55, 0x76, 0x94, 0x7a, 0x09, 0xf3, + 0x67, 0xa4, 0xc2, 0x92, 0xe5, 0xd2, 0x6a, 0x66, 0x47, 0xa9, 0x2f, 0x61, 0xf6, 0x88, 0xfe, 0x07, + 0xf2, 0x96, 0x4b, 0x4d, 0x37, 0xb4, 0x68, 0xb5, 0xc4, 0xe1, 0x15, 0xcb, 0xa5, 0x47, 0xa1, 0x45, + 0xd1, 0xbb, 0x50, 0x9a, 0x08, 0x95, 0xe6, 0xd8, 0x0a, 0xc7, 0xd5, 0x25, 0xae, 0xa8, 0x28, 0xb1, + 0x43, 0x2b, 0x1c, 0xa3, 0x3a, 0xa8, 0x23, 0xdb, 0xb3, 0x1c, 0x73, 0xe0, 0xd0, 0x97, 0xe6, 0x90, + 0x38, 0xd4, 0xaa, 0x66, 0x77, 0x94, 0xfa, 0x32, 0xae, 0x70, 0xbc, 0xe9, 0xd0, 0x97, 0x2d, 0x86, + 0x26, 0x95, 0x59, 0xc3, 0x61, 0x50, 0xdd, 0x48, 0x29, 0x6b, 0x0c, 0x87, 0x01, 0xba, 0x07, 0xab, + 0x11, 0x25, 0x10, 0x6b, 0xa8, 0x2e, 0xef, 0x28, 0xf5, 0x02, 0xae, 0x4c, 0xd2, 0x2b, 0xbb, 0x07, + 0xab, 0xd4, 0x76, 0x89, 0x3f, 0xa5, 0x66, 0x48, 0x06, 0xbe, 0x37, 0x0c, 0xab, 0x39, 0x31, 0xa9, + 0x84, 0x7b, 0x02, 0x45, 0x1a, 0x94, 0x47, 0x84, 0x98, 0x8e, 0xed, 0xda, 0xd4, 0x64, 0x2b, 0x5c, + 0xe1, 0x2b, 0x2c, 0x8e, 0x08, 0x69, 0x33, 0xac, 0x67, 0x51, 0xf4, 0x3e, 0x54, 0x66, 0x1c, 0xbe, + 0x0d, 0x65, 0x4e, 0x2a, 0x45, 0x24, 0xbe, 0x17, 0xbb, 0xa0, 0xfa, 0x53, 0x7a, 0xe6, 0xdb, 0xde, + 0x99, 0x39, 0x18, 0x5b, 0x9e, 0x69, 0x0f, 0xab, 0xf9, 0x1d, 0xa5, 0x9e, 0xdd, 0xcf, 0x56, 0x95, + 0x87, 0x0a, 0xae, 0x44, 0xd2, 0xe6, 0xd8, 0xf2, 0x8c, 0x21, 0xba, 0x0f, 0x6b, 0xf3, 0xfc, 0xb0, + 0xba, 0xbe, 0xb3, 0x54, 0xcf, 0xe2, 0xd5, 0x34, 0x35, 0x44, 0x77, 0x61, 0xd5, 0xb1, 0x42, 0x6a, + 0x8e, 0xfd, 0x89, 0x39, 0x99, 0x9e, 0x9e, 0x93, 0x8b, 0x6a, 0x85, 0xef, 0x4e, 0x99, 0xc1, 0x87, + 0xfe, 0xe4, 0x98, 0x83, 0xe8, 0x16, 0x00, 0xdf, 0x66, 0x6e, 0x6a, 0xb5, 0xc0, 0x57, 0x5c, 0x60, + 0x08, 0x37, 0x13, 0x7d, 0x0c, 0x45, 0xee, 0x1e, 0xe6, 0xd8, 0xf6, 0x68, 0x58, 0x85, 0x9d, 0xa5, + 0x7a, 0x71, 0x4f, 0xdd, 0x75, 0x3c, 0xe6, 0x29, 0x98, 0x49, 0x0e, 0x6d, 0x8f, 0x62, 0x08, 0xa2, + 0xc7, 0x10, 0x0d, 0x61, 0x9d, 0xb9, 0x85, 0x39, 0x98, 0x86, 0xd4, 0x77, 0xcd, 0x80, 0x0c, 0xfc, + 0x60, 0x18, 0x56, 0x8b, 0x7c, 0xe8, 0xff, 0xef, 0xc6, 0xde, 0xb6, 0x7b, 0xd9, 0xbd, 0x76, 0x5b, + 0x24, 0xa4, 0x4d, 0x3e, 0x0e, 0x8b, 0x61, 0xba, 0x47, 0x83, 0x0b, 0xbc, 0x36, 0x9c, 0xc7, 0xd1, + 0x87, 0x80, 0x2c, 0xc7, 0xf1, 0x5f, 0x99, 0x21, 0x71, 0x46, 0xa6, 0x3c, 0xcb, 0xea, 0xea, 0x8e, + 0x52, 0xcf, 0x63, 0x95, 0x4b, 0x7a, 0xc4, 0x19, 0x49, 0xf5, 0xe8, 0x53, 0x28, 0x73, 0x9b, 0x46, + 0xc4, 0xa2, 0xd3, 0x80, 0x84, 0x55, 0x75, 0x67, 0xa9, 0x5e, 0xd9, 0x5b, 0x93, 0x0b, 0x39, 0x10, + 0xf0, 0xbe, 0x4d, 0x71, 0x89, 0xf1, 0xe4, 0x7b, 0x88, 0xb6, 0xa1, 0xe0, 0x5a, 0xaf, 0xcd, 0x89, + 0x15, 0xd0, 0xb0, 0xba, 0xb6, 0xa3, 0xd4, 0xcb, 0x38, 0xef, 0x5a, 0xaf, 0x8f, 0xd9, 0x3b, 0xda, + 0x85, 0x75, 0xcf, 0x37, 0x6d, 0x6f, 0xe4, 0xd8, 0x67, 0x63, 0x6a, 0x4e, 0x27, 0x43, 0x8b, 0x92, + 0xb0, 0x8a, 0xb8, 0x0d, 0x6b, 0x9e, 0x6f, 0x48, 0xc9, 0x89, 0x10, 0xa0, 0x8f, 0x60, 0x9d, 0x29, + 0x0b, 0xc7, 0x56, 0x30, 0x34, 0x43, 0xfb, 0x47, 0x22, 0x3c, 0xe3, 0x06, 0x3b, 0x71, 0xac, 0xba, + 0xd6, 0xeb, 0x1e, 0x93, 0xf4, 0xec, 0x1f, 0x09, 0xf7, 0x0e, 0x1e, 0x56, 0x93, 0xea, 0x26, 0x57, + 0xc7, 0x1e, 0x6b, 0x2d, 0xd8, 0x5c, 0xbc, 0x41, 0x8c, 0xcb, 0x4e, 0x58, 0xe1, 0xaa, 0xd8, 0x23, + 0xda, 0x80, 0xe5, 0x97, 0x96, 0x33, 0x25, 0x3c, 0x2c, 0x4b, 0x58, 0xbc, 0xfc, 0x2c, 0xf3, 0xb9, + 0xa2, 0x8d, 0x61, 0xbd, 0x1f, 0x58, 0x83, 0xf3, 0xb9, 0xc8, 0x9e, 0x0f, 0x4c, 0xe5, 0x72, 0x60, + 0x5e, 0xb1, 0xe0, 0xcc, 0x15, 0x0b, 0xd6, 0xbe, 0x86, 0x55, 0xee, 0x22, 0x07, 0x84, 0x5c, 0x97, + 0x3f, 0xb6, 0x80, 0x65, 0x07, 0x1e, 0x4a, 0x22, 0x87, 0xe4, 0x2c, 0x97, 0x45, 0x91, 0x36, 0x04, + 0x75, 0x36, 0x3e, 0x9c, 0xf8, 0x5e, 0x48, 0x58, 0x72, 0x60, 0x1e, 0xc4, 0x42, 0x80, 0x45, 0x18, + 0xdf, 0x41, 0x85, 0x8f, 0xaa, 0x48, 0xfc, 0x80, 0x88, 0xfd, 0xbb, 0x2b, 0x02, 0xda, 0x74, 0xfc, + 0xc1, 0x39, 0xcb, 0x22, 0xd6, 0x85, 0x54, 0x5f, 0x66, 0x70, 0xdb, 0x1f, 0x9c, 0xb7, 0x18, 0xa8, + 0xbd, 0x10, 0x89, 0xae, 0xef, 0xf3, 0xb9, 0xfe, 0x85, 0xed, 0xd0, 0x60, 0x99, 0x3b, 0x33, 0x57, + 0x5b, 0xdc, 0x2b, 0x25, 0xa3, 0x02, 0x0b, 0x91, 0xf6, 0x02, 0xd6, 0x53, 0xca, 0xe5, 0x2a, 0x6a, + 0x90, 0x9f, 0x04, 0xc4, 0x76, 0xad, 0x33, 0x22, 0x35, 0xc7, 0xef, 0xa8, 0x0e, 0x2b, 0x23, 0xcb, + 0x76, 0xa6, 0x41, 0xa4, 0xb8, 0x12, 0x79, 0xa9, 0x40, 0x71, 0x24, 0xd6, 0x6e, 0x42, 0x0d, 0x93, + 0x90, 0xd0, 0x23, 0x3b, 0x0c, 0x6d, 0xdf, 0x6b, 0xfa, 0x1e, 0x0d, 0x7c, 0x47, 0xae, 0x40, 0xbb, + 0x05, 0xdb, 0x0b, 0xa5, 0xc2, 0x04, 0x36, 0xf8, 0xdb, 0x29, 0x09, 0x2e, 0x16, 0x0f, 0xfe, 0x16, + 0xb6, 0x17, 0x4a, 0xa5, 0xfd, 0x1f, 0xc2, 0xf2, 0xc4, 0xb2, 0x03, 0x76, 0xf6, 0x2c, 0xaa, 0x37, + 0x13, 0x51, 0x7d, 0x6c, 0xd9, 0xc1, 0xa1, 0x1d, 0x52, 0x3f, 0xb8, 0xc0, 0x82, 0xf4, 0x38, 0x9b, + 0x57, 0xd4, 0x8c, 0xd6, 0x86, 0x9b, 0xcf, 0x0c, 0x77, 0xe2, 0x07, 0x8b, 0xed, 0x9d, 0xe9, 0x54, + 0xde, 0x42, 0xa7, 0x76, 0x07, 0x6e, 0x5d, 0xa1, 0x4d, 0xae, 0xef, 0x77, 0x0a, 0x14, 0x13, 0xe3, + 0x58, 0x28, 0x7b, 0xfe, 0x90, 0x98, 0xa3, 0xc0, 0x77, 0xa3, 0x3d, 0x67, 0xc0, 0x41, 0xe0, 0xbb, + 0xcc, 0x05, 0xb9, 0x90, 0xfa, 0x32, 0x5e, 0x72, 0xec, 0xb5, 0xef, 0xa3, 0x8f, 0x60, 0x65, 0x2c, + 0x14, 0xf0, 0x34, 0x5f, 0xdc, 0x5b, 0x9f, 0x33, 0xab, 0x65, 0x51, 0x0b, 0x47, 0x9c, 0xc7, 0xd9, + 0xfc, 0x92, 0x9a, 0x7d, 0x9c, 0xcd, 0x67, 0xd5, 0xe5, 0xc7, 0xd9, 0xfc, 0xb2, 0x9a, 0x7b, 0x9c, + 0xcd, 0xe7, 0xd4, 0x15, 0xed, 0x6f, 0x0a, 0xe4, 0x23, 0x36, 0xb3, 0x84, 0x9d, 0xa0, 0xc9, 0xdc, + 0x50, 0xfa, 0x6e, 0x9e, 0x01, 0x7d, 0xdb, 0x25, 0x68, 0x07, 0x4a, 0x5c, 0x98, 0x8e, 0x08, 0x60, + 0x58, 0x83, 0x47, 0x05, 0xbf, 0x7f, 0x22, 0x06, 0x77, 0xff, 0xac, 0xbc, 0x7f, 0x04, 0x25, 0xba, + 0x65, 0xc3, 0xe9, 0x60, 0x40, 0xc2, 0x50, 0xcc, 0xb2, 0x2c, 0x28, 0x12, 0xe3, 0x13, 0xdd, 0x85, + 0xd5, 0x88, 0x12, 0xcd, 0x95, 0x13, 0xe1, 0x21, 0x61, 0x39, 0x5d, 0x1d, 0xd4, 0x24, 0xcf, 0x9d, + 0xdd, 0x78, 0x95, 0x19, 0x91, 0x4d, 0x2a, 0x16, 0xaf, 0xed, 0xc0, 0xed, 0x47, 0xf3, 0x4e, 0xd7, + 0xf4, 0xbd, 0x91, 0x7d, 0x16, 0xf9, 0xd6, 0xf7, 0x70, 0xe7, 0x4a, 0x86, 0xf4, 0xaf, 0xcf, 0x20, + 0x37, 0xe0, 0x08, 0xdf, 0x9f, 0xe2, 0xde, 0x9d, 0xc4, 0xae, 0x2f, 0x1c, 0x28, 0xe9, 0xda, 0x73, + 0xb8, 0xdd, 0xbb, 0x76, 0xf6, 0x7f, 0x5f, 0xf5, 0xbb, 0x70, 0xa7, 0x77, 0xbd, 0xd9, 0xda, 0xaf, + 0x32, 0xb0, 0xb1, 0x88, 0xc0, 0x6e, 0xee, 0xb1, 0xe5, 0x8c, 0x4c, 0xc7, 0x1e, 0x91, 0xb8, 0xbc, + 0x10, 0xd9, 0x7a, 0x95, 0x09, 0xda, 0xf6, 0x88, 0x44, 0xf5, 0xc5, 0x3d, 0x58, 0xe5, 0x97, 0x76, + 0xe0, 0x9f, 0x5a, 0xa7, 0xb6, 0x63, 0x53, 0x91, 0xb7, 0x32, 0xb8, 0x32, 0xf6, 0x27, 0xc7, 0x33, + 0x14, 0x6d, 0x42, 0xee, 0x15, 0x61, 0xf9, 0x96, 0x17, 0x51, 0x19, 0x2c, 0xdf, 0xd0, 0xa7, 0xb0, + 0xe5, 0x5a, 0xaf, 0x6d, 0x77, 0xea, 0x9a, 0xb3, 0xd2, 0x27, 0x9c, 0x3a, 0x34, 0xe4, 0xae, 0x52, + 0xc6, 0x37, 0xa4, 0x38, 0xbe, 0x01, 0xb8, 0x10, 0x35, 0xe1, 0xb6, 0x6b, 0x7b, 0x7c, 0x9c, 0xcc, + 0x30, 0x66, 0x40, 0x1c, 0xeb, 0xb5, 0x69, 0x7b, 0x94, 0x04, 0x2f, 0x2d, 0x87, 0xbb, 0x51, 0x16, + 0x6f, 0x4b, 0x56, 0x94, 0x8f, 0x18, 0xc7, 0x90, 0x14, 0xed, 0x07, 0xd8, 0xe2, 0x89, 0x23, 0x61, + 0x68, 0xb4, 0xf3, 0xcc, 0xef, 0x03, 0xdf, 0x35, 0x59, 0x68, 0x45, 0x11, 0xc8, 0x80, 0x8e, 0x3f, + 0x24, 0x2c, 0x02, 0xa9, 0x2f, 0x44, 0x32, 0x02, 0xa9, 0xcf, 0x05, 0xc9, 0x5a, 0x72, 0x29, 0x55, + 0x4b, 0x6a, 0xe7, 0x50, 0xbd, 0x3c, 0x97, 0xf4, 0xa0, 0x1d, 0x28, 0x26, 0x77, 0x90, 0x4d, 0xa7, + 0xe0, 0x24, 0x94, 0x0c, 0xed, 0xcc, 0x9b, 0x43, 0x5b, 0xfb, 0xb3, 0x02, 0x6b, 0xfb, 0x53, 0xdb, + 0x19, 0xa6, 0xae, 0x89, 0xa4, 0x75, 0x4a, 0xba, 0xd2, 0x5d, 0x54, 0xc6, 0x66, 0x16, 0x96, 0xb1, + 0x1f, 0x2e, 0xa8, 0x03, 0x97, 0x78, 0x1d, 0x98, 0x59, 0x50, 0x05, 0xde, 0x81, 0xe2, 0xac, 0xa8, + 0x63, 0x47, 0xba, 0x54, 0x2f, 0x61, 0x18, 0x47, 0x15, 0x5d, 0x78, 0xa9, 0x2a, 0x5e, 0xbe, 0x54, + 0x15, 0x6b, 0x9f, 0x03, 0x4a, 0xae, 0x45, 0xee, 0x59, 0x7c, 0xa1, 0x29, 0x57, 0x5f, 0x68, 0x37, + 0xa1, 0xd6, 0x9b, 0x9e, 0x86, 0x83, 0xc0, 0x3e, 0x25, 0x87, 0xd4, 0x19, 0xe8, 0x2f, 0x89, 0x47, + 0xc3, 0x28, 0xb4, 0xff, 0x91, 0x85, 0x42, 0x8c, 0xb2, 0x7a, 0xc1, 0xf6, 0x06, 0xbe, 0x1b, 0xad, + 0xcb, 0x23, 0x0e, 0x5b, 0x9a, 0xf0, 0xfb, 0xb5, 0x48, 0xd4, 0x14, 0x12, 0x63, 0xc8, 0xf8, 0xa9, + 0x7d, 0x90, 0xfc, 0x8c, 0xe0, 0x27, 0xb7, 0x41, 0xf0, 0xeb, 0xa0, 0xc6, 0xfa, 0xc7, 0xd4, 0x19, + 0xc4, 0xfb, 0x86, 0x2b, 0x11, 0xce, 0x8c, 0x11, 0xcc, 0x58, 0x73, 0xc4, 0xcc, 0x0a, 0x66, 0x84, + 0x4b, 0xe6, 0xbb, 0x50, 0x62, 0x19, 0x33, 0xa4, 0x96, 0x3b, 0x31, 0xbd, 0x50, 0xba, 0x7c, 0x31, + 0xc6, 0x3a, 0x21, 0xfa, 0x0a, 0x80, 0xb0, 0xf5, 0x99, 0xf4, 0x62, 0x42, 0x78, 0xd2, 0xac, 0xec, + 0xdd, 0x4e, 0xf8, 0x4e, 0xbc, 0x01, 0xbb, 0xfc, 0x6f, 0xff, 0x62, 0x42, 0x70, 0x81, 0x44, 0x8f, + 0xe8, 0x6b, 0x28, 0x8f, 0xfc, 0xe0, 0x15, 0x2b, 0x02, 0x39, 0x28, 0x2f, 0x96, 0xad, 0x84, 0x86, + 0x03, 0x21, 0xe7, 0xc3, 0x0f, 0xdf, 0xc1, 0xa5, 0x51, 0xe2, 0x1d, 0x3d, 0x01, 0x14, 0x8d, 0xe7, + 0xf7, 0x80, 0x50, 0x92, 0xe7, 0x4a, 0xb6, 0x2f, 0x2b, 0x61, 0x51, 0x1a, 0x29, 0x52, 0x47, 0x73, + 0x18, 0xfa, 0x02, 0x4a, 0x21, 0xa1, 0xd4, 0x21, 0x52, 0x4d, 0x81, 0xab, 0xd9, 0x4c, 0x55, 0xe9, + 0x4c, 0x1c, 0x69, 0x28, 0x86, 0xb3, 0x57, 0xb4, 0x0f, 0xab, 0x8e, 0xed, 0x9d, 0x27, 0xcd, 0x00, + 0x3e, 0xbe, 0x9a, 0x18, 0xdf, 0xb6, 0xbd, 0xf3, 0xa4, 0x0d, 0x65, 0x27, 0x09, 0x68, 0x5f, 0x42, + 0x21, 0xde, 0x25, 0x54, 0x84, 0x95, 0x93, 0xce, 0x93, 0x4e, 0xf7, 0xbb, 0x8e, 0xfa, 0x0e, 0xca, + 0x43, 0xb6, 0xa7, 0x77, 0x5a, 0xaa, 0xc2, 0x60, 0xac, 0x37, 0x75, 0xe3, 0xa9, 0xae, 0x66, 0xd8, + 0xcb, 0x41, 0x17, 0x7f, 0xd7, 0xc0, 0x2d, 0x75, 0x69, 0x7f, 0x05, 0x96, 0xf9, 0xbc, 0xda, 0x1f, + 0x14, 0xc8, 0xf3, 0x13, 0xf4, 0x46, 0x3e, 0xfa, 0x3f, 0x88, 0x9d, 0x8b, 0x5f, 0x7f, 0xac, 0x02, + 0xe4, 0x5e, 0x57, 0xc6, 0xb1, 0xc3, 0xf4, 0x25, 0xce, 0xc8, 0xb1, 0x6b, 0xc4, 0xe4, 0x8c, 0x20, + 0x47, 0x82, 0x98, 0x7c, 0x3f, 0xa1, 0x39, 0x95, 0x95, 0xb2, 0x78, 0x35, 0x12, 0x44, 0x77, 0x70, + 0xf2, 0x6b, 0x2d, 0x75, 0x57, 0x27, 0xbe, 0xd6, 0x24, 0x57, 0xfb, 0x0c, 0x4a, 0xc9, 0x33, 0x47, + 0xf7, 0x20, 0x6b, 0x7b, 0x23, 0x5f, 0x06, 0xe2, 0xfa, 0x9c, 0x73, 0xb1, 0x45, 0x62, 0x4e, 0xd0, + 0x10, 0xa8, 0xf3, 0xe7, 0xac, 0x95, 0xa1, 0x98, 0x38, 0x34, 0xed, 0xaf, 0x0a, 0x94, 0x53, 0x87, + 0xf0, 0xd6, 0xda, 0xd1, 0x57, 0x50, 0x7a, 0x65, 0x07, 0xc4, 0x4c, 0xd6, 0xa3, 0x95, 0xbd, 0x5a, + 0xba, 0x1e, 0x8d, 0xfe, 0x37, 0xfd, 0x21, 0xc1, 0x45, 0xc6, 0x97, 0x00, 0xfa, 0x39, 0x54, 0xa2, + 0x8b, 0x64, 0x48, 0xa8, 0x65, 0x3b, 0x7c, 0xab, 0x2a, 0x29, 0xf7, 0x90, 0xdc, 0x16, 0x97, 0xe3, + 0xf2, 0x28, 0xf9, 0x8a, 0x3e, 0x98, 0x29, 0x08, 0x69, 0x60, 0x7b, 0x67, 0x7c, 0xff, 0x0a, 0x31, + 0xad, 0xc7, 0x41, 0x56, 0xea, 0x95, 0xe5, 0x5d, 0xd6, 0xa3, 0x16, 0x9d, 0xb2, 0x4f, 0xad, 0xe5, + 0x90, 0x5a, 0x32, 0x93, 0x55, 0x52, 0xb1, 0x95, 0x20, 0x12, 0x2c, 0x58, 0xa9, 0x72, 0x3c, 0x73, + 0xa9, 0x1c, 0x5f, 0x66, 0x19, 0x43, 0x24, 0xda, 0xe2, 0x1e, 0x92, 0x8b, 0x3f, 0xec, 0xb7, 0x9b, + 0x0d, 0x4a, 0x89, 0x3b, 0xa1, 0x58, 0x10, 0x64, 0xfd, 0xf3, 0x35, 0x40, 0xd3, 0x0e, 0x06, 0x53, + 0x9b, 0x3e, 0x21, 0x17, 0xec, 0x5a, 0x8b, 0x32, 0xba, 0x48, 0x7b, 0xb9, 0x81, 0xc8, 0xe2, 0x5b, + 0xb0, 0x12, 0x25, 0x22, 0x91, 0xdf, 0x72, 0x63, 0x9e, 0x80, 0xb4, 0x3f, 0x66, 0x61, 0x5b, 0x1e, + 0xa9, 0x38, 0x0d, 0x4a, 0x82, 0x01, 0x99, 0xc4, 0xdf, 0x69, 0x8f, 0x60, 0x63, 0x96, 0x54, 0xc5, + 0x44, 0x66, 0xf4, 0xed, 0x57, 0xdc, 0xbb, 0x91, 0x58, 0xe9, 0xcc, 0x0c, 0x8c, 0xe2, 0x64, 0x3b, + 0x33, 0xed, 0x61, 0x42, 0x91, 0xe5, 0xfa, 0x53, 0x4f, 0xba, 0xa8, 0xc8, 0x78, 0x68, 0xe6, 0xce, + 0x4c, 0xc4, 0x3d, 0xfa, 0x1e, 0xc4, 0x4e, 0x6e, 0x92, 0xd7, 0x13, 0x3b, 0xb8, 0xe0, 0xd9, 0xaf, + 0x3c, 0x4b, 0xb7, 0x3a, 0x47, 0x2f, 0x7d, 0x3c, 0x65, 0x2e, 0x7f, 0x3c, 0x7d, 0x01, 0xb5, 0x38, + 0x3a, 0x64, 0x63, 0x86, 0x0c, 0xe3, 0xdb, 0x6f, 0x85, 0xdb, 0xb0, 0x15, 0x31, 0x70, 0x44, 0x90, + 0x57, 0xe0, 0x43, 0xd8, 0x48, 0x84, 0xd6, 0xcc, 0x74, 0x11, 0x89, 0x68, 0x16, 0x5d, 0x49, 0xd3, + 0xe3, 0x11, 0xd2, 0x74, 0x51, 0x0b, 0xc5, 0xf9, 0x5f, 0x9a, 0xfe, 0x0b, 0xa8, 0xcc, 0x35, 0x2e, + 0xf2, 0xfc, 0xdc, 0x7f, 0x7a, 0x39, 0xb3, 0x2e, 0x3a, 0x9e, 0xdd, 0x05, 0xdd, 0x8b, 0xf2, 0x20, + 0xd5, 0xb9, 0xb8, 0x05, 0xe0, 0x7b, 0xb6, 0xef, 0x99, 0xa7, 0x8e, 0x7f, 0xca, 0x13, 0x6e, 0x09, + 0x17, 0x38, 0xb2, 0xef, 0xf8, 0xa7, 0xb5, 0x6f, 0x00, 0xfd, 0x87, 0x1f, 0xf8, 0x7f, 0x52, 0xe0, + 0xe6, 0x62, 0x13, 0xe5, 0x3d, 0xff, 0x5f, 0x73, 0xa1, 0x2f, 0x20, 0x67, 0x0d, 0xa8, 0xed, 0x7b, + 0x32, 0x33, 0xbc, 0x97, 0x18, 0x8a, 0x49, 0xe8, 0x3b, 0x2f, 0xc9, 0xa1, 0xef, 0x0c, 0xa5, 0x31, + 0x0d, 0x4e, 0xc5, 0x72, 0x48, 0x2a, 0xe8, 0x96, 0xd2, 0x41, 0xa7, 0xfd, 0x46, 0x81, 0x2d, 0xd1, + 0x45, 0x60, 0x27, 0x2e, 0x82, 0x3a, 0x0a, 0x80, 0x3d, 0x00, 0xee, 0x26, 0x13, 0xdf, 0xf6, 0x68, + 0x9c, 0xc3, 0x44, 0x54, 0xca, 0xda, 0xe0, 0x98, 0x89, 0x70, 0x81, 0xd1, 0xf8, 0x23, 0xfa, 0x64, + 0xce, 0xd0, 0xe4, 0x3d, 0x39, 0x9b, 0x21, 0x6d, 0xa0, 0x56, 0x83, 0xea, 0x65, 0x1b, 0xc4, 0x16, + 0xde, 0xff, 0x75, 0x16, 0xca, 0xa9, 0xd4, 0x95, 0xbe, 0xbb, 0xca, 0x50, 0xe8, 0x74, 0xcd, 0x96, + 0xde, 0x6f, 0x18, 0x6d, 0x55, 0x41, 0x2a, 0x94, 0xba, 0x1d, 0xa3, 0xdb, 0x31, 0x5b, 0x7a, 0xb3, + 0xdb, 0x62, 0xb7, 0xd8, 0x0d, 0x58, 0x6b, 0x1b, 0x9d, 0x27, 0x66, 0xa7, 0xdb, 0x37, 0xf5, 0xb6, + 0xf1, 0xc8, 0xd8, 0x6f, 0xeb, 0xea, 0x12, 0xda, 0x00, 0xb5, 0xdb, 0x31, 0x9b, 0x87, 0x0d, 0xa3, + 0x63, 0xf6, 0x8d, 0x23, 0xbd, 0x7b, 0xd2, 0x57, 0xb3, 0x0c, 0x65, 0xe9, 0xc6, 0xd4, 0x9f, 0x35, + 0x75, 0xbd, 0xd5, 0x33, 0x8f, 0x1a, 0xcf, 0xd4, 0x65, 0x54, 0x85, 0x0d, 0xa3, 0xd3, 0x3b, 0x39, + 0x38, 0x30, 0x9a, 0x86, 0xde, 0xe9, 0x9b, 0xfb, 0x8d, 0x76, 0xa3, 0xd3, 0xd4, 0xd5, 0x1c, 0xda, + 0x04, 0x64, 0x74, 0x9a, 0xdd, 0xa3, 0xe3, 0xb6, 0xde, 0xd7, 0xcd, 0xe8, 0xb6, 0x5c, 0x41, 0xeb, + 0xb0, 0xca, 0xf5, 0x34, 0x5a, 0x2d, 0xf3, 0xa0, 0x61, 0xb4, 0xf5, 0x96, 0x9a, 0x67, 0x96, 0x48, + 0x46, 0xcf, 0x6c, 0x19, 0xbd, 0xc6, 0x3e, 0x83, 0x0b, 0x6c, 0x4e, 0xa3, 0xf3, 0xb4, 0x6b, 0x34, + 0x75, 0xb3, 0xc9, 0xd4, 0x32, 0x14, 0x18, 0x39, 0x42, 0x4f, 0x3a, 0x2d, 0x1d, 0x1f, 0x37, 0x8c, + 0x96, 0x5a, 0x44, 0xdb, 0xb0, 0x15, 0xc1, 0xfa, 0xb3, 0x63, 0x03, 0x3f, 0x37, 0xfb, 0xdd, 0xae, + 0xd9, 0xeb, 0x76, 0x3b, 0x6a, 0x29, 0xa9, 0x89, 0xad, 0xb6, 0x7b, 0xac, 0x77, 0xd4, 0x32, 0xda, + 0x82, 0xf5, 0xa3, 0xe3, 0x63, 0x33, 0x92, 0x44, 0x8b, 0xad, 0x30, 0x7a, 0xa3, 0xd5, 0xc2, 0x7a, + 0xaf, 0x67, 0x1e, 0x19, 0xbd, 0xa3, 0x46, 0xbf, 0x79, 0xa8, 0xae, 0xb2, 0x25, 0xf5, 0xf4, 0xbe, + 0xd9, 0xef, 0xf6, 0x1b, 0xed, 0x19, 0xae, 0x32, 0x83, 0x66, 0x38, 0x9b, 0xb4, 0xdd, 0xfd, 0x4e, + 0x5d, 0x63, 0x1b, 0xce, 0xe0, 0xee, 0x53, 0x69, 0x22, 0x62, 0x6b, 0x97, 0xc7, 0x13, 0xcd, 0xa9, + 0xae, 0x33, 0xd0, 0xe8, 0x3c, 0x6d, 0xb4, 0x8d, 0x96, 0xf9, 0x44, 0x7f, 0xce, 0xab, 0x8d, 0x0d, + 0x06, 0x0a, 0xcb, 0xcc, 0x63, 0xdc, 0x7d, 0xc4, 0x0c, 0x51, 0x6f, 0x20, 0x04, 0x95, 0xa6, 0x81, + 0x9b, 0x27, 0xed, 0x06, 0x36, 0x71, 0xf7, 0xa4, 0xaf, 0xab, 0x9b, 0xf7, 0x7f, 0xaf, 0x40, 0x29, + 0x79, 0x9b, 0xb0, 0x53, 0x37, 0x3a, 0xe6, 0x41, 0xdb, 0x78, 0x74, 0xd8, 0x17, 0x4e, 0xd0, 0x3b, + 0x69, 0xb2, 0x23, 0xd3, 0x59, 0x15, 0x83, 0xa0, 0x22, 0x36, 0x3d, 0x5e, 0x6c, 0x86, 0xcd, 0x25, + 0xb1, 0x4e, 0x57, 0xea, 0x5d, 0x62, 0xc6, 0x4b, 0x50, 0xc7, 0xb8, 0x8b, 0xd5, 0x2c, 0x7a, 0x1f, + 0x76, 0x24, 0xc2, 0xce, 0x15, 0x63, 0xbd, 0xd9, 0x37, 0x8f, 0x1b, 0xcf, 0x8f, 0xd8, 0xb1, 0x0b, + 0x27, 0xeb, 0xa9, 0xcb, 0xe8, 0x0e, 0x6c, 0xc7, 0xac, 0x45, 0x7e, 0x71, 0xff, 0x4b, 0xa8, 0x5e, + 0x15, 0x95, 0x08, 0x20, 0xd7, 0xd3, 0xfb, 0xfd, 0xb6, 0x2e, 0x2a, 0xaf, 0x03, 0xe1, 0xb8, 0x00, + 0x39, 0xac, 0xf7, 0x4e, 0x8e, 0x74, 0x35, 0x73, 0xff, 0x27, 0xa0, 0xce, 0x87, 0x0a, 0x93, 0xeb, + 0x1d, 0xe6, 0x32, 0xea, 0x3b, 0x2c, 0x00, 0xa4, 0xff, 0xa8, 0x0a, 0x53, 0xd1, 0x38, 0xe9, 0x77, + 0xd5, 0xcc, 0xde, 0xdf, 0x8b, 0x90, 0xe3, 0x5f, 0x10, 0x01, 0xfa, 0x06, 0xca, 0x89, 0x9e, 0xef, + 0xd3, 0x3d, 0x74, 0xeb, 0xda, 0x6e, 0x70, 0x2d, 0x6a, 0x7c, 0x49, 0xf8, 0xa1, 0x82, 0xf6, 0xa1, + 0x92, 0xec, 0x5d, 0x3e, 0xdd, 0x43, 0xc9, 0xc2, 0x7b, 0x41, 0x5b, 0x73, 0x81, 0x8e, 0x27, 0xa0, + 0xea, 0x21, 0xb5, 0x5d, 0x76, 0xff, 0xcb, 0xee, 0x22, 0xaa, 0x25, 0x13, 0x57, 0xba, 0x65, 0x59, + 0xdb, 0x5e, 0x28, 0x93, 0xa9, 0xf4, 0x5b, 0x56, 0x6b, 0xc5, 0xfd, 0xbd, 0x4b, 0x0b, 0x4a, 0x37, + 0x15, 0x6b, 0xb7, 0xaf, 0x12, 0xcb, 0xfe, 0xc1, 0xd2, 0x6f, 0x33, 0x6c, 0x8d, 0xe5, 0x84, 0x6c, + 0xc1, 0x2e, 0xcd, 0x29, 0x5d, 0x50, 0x91, 0xa0, 0x21, 0xac, 0x2f, 0xe8, 0xfd, 0xa1, 0x0f, 0xd2, + 0xf9, 0xf9, 0x8a, 0xce, 0x61, 0xed, 0xee, 0x9b, 0x68, 0x72, 0xf1, 0x43, 0x58, 0x5f, 0xd0, 0x24, + 0x4c, 0xcd, 0x72, 0x75, 0x8b, 0x31, 0x35, 0xcb, 0x75, 0xbd, 0xc6, 0x1f, 0xe0, 0xc6, 0xc2, 0x4e, + 0x1f, 0xba, 0x97, 0x50, 0x70, 0x5d, 0x67, 0xb1, 0x56, 0x7f, 0x33, 0x51, 0xce, 0x35, 0x81, 0xad, + 0x2b, 0x5a, 0x53, 0xe8, 0x7f, 0x13, 0x4a, 0xae, 0x6f, 0x70, 0xd5, 0xee, 0xbf, 0x0d, 0x75, 0x36, + 0x63, 0xef, 0x2d, 0x66, 0xec, 0xbd, 0xfd, 0x8c, 0x6f, 0x68, 0x52, 0xa1, 0x17, 0xa0, 0xce, 0x77, + 0x4d, 0x90, 0x36, 0x7f, 0x16, 0x97, 0xdb, 0x37, 0xb5, 0xf7, 0xae, 0xe5, 0x48, 0xe5, 0x06, 0xc0, + 0xac, 0xb1, 0x80, 0x6e, 0x26, 0x86, 0x5c, 0xea, 0x9d, 0xd4, 0x6e, 0x5d, 0x21, 0x95, 0xaa, 0xfa, + 0xb0, 0xbe, 0xa0, 0xd3, 0x90, 0xf2, 0xae, 0xab, 0x3b, 0x11, 0xb5, 0x8d, 0x45, 0x1f, 0xe4, 0x0f, + 0x15, 0x74, 0x24, 0x02, 0x36, 0xfa, 0x61, 0xe8, 0x0d, 0x19, 0xa8, 0xba, 0xf8, 0xc3, 0x61, 0x1a, + 0xf2, 0x50, 0x7d, 0xa8, 0xa0, 0x2e, 0x94, 0x92, 0x59, 0xe7, 0x8d, 0xe9, 0xe8, 0x8d, 0x0a, 0x47, + 0xb0, 0x9a, 0x2a, 0xda, 0xfc, 0x20, 0xe5, 0xe7, 0xd7, 0xd5, 0x75, 0xa9, 0x88, 0xba, 0xa6, 0x46, + 0xad, 0xb3, 0x79, 0x5e, 0x80, 0x3a, 0x5f, 0xdc, 0xa4, 0xbc, 0xe0, 0x8a, 0xea, 0x2b, 0xe5, 0x05, + 0x57, 0x55, 0x47, 0xfb, 0x1f, 0x7f, 0xff, 0xe0, 0xcc, 0xa6, 0xe3, 0xe9, 0xe9, 0xee, 0xc0, 0x77, + 0x1f, 0xf0, 0xdf, 0x84, 0x3c, 0xdb, 0x3b, 0xf3, 0x08, 0x7d, 0xe5, 0x07, 0xe7, 0x0f, 0x1c, 0x6f, + 0xf8, 0x80, 0xe7, 0xac, 0x07, 0xb1, 0xae, 0xd3, 0x1c, 0xff, 0xd9, 0xf9, 0x93, 0x7f, 0x06, 0x00, + 0x00, 0xff, 0xff, 0x59, 0xf4, 0x3e, 0x26, 0xa6, 0x1e, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. diff --git a/lnrpc/routerrpc/router.proto b/lnrpc/routerrpc/router.proto index 7ed136fa..2743dff0 100644 --- a/lnrpc/routerrpc/router.proto +++ b/lnrpc/routerrpc/router.proto @@ -279,6 +279,11 @@ message SendPaymentRequest { value is in milli-satoshis. */ uint64 max_shard_size_msat = 21; + + /* + If set, an AMP-payment will be attempted. + */ + bool amp = 22; } message TrackPaymentRequest { diff --git a/lnrpc/routerrpc/router.swagger.json b/lnrpc/routerrpc/router.swagger.json index 32f0af50..9ccfedaa 100644 --- a/lnrpc/routerrpc/router.swagger.json +++ b/lnrpc/routerrpc/router.swagger.json @@ -1493,6 +1493,11 @@ "type": "string", "format": "uint64", "description": "The largest payment split that should be attempted when making a payment if\nsplitting is necessary. Setting this value will effectively cause lnd to\nsplit more aggressively, vs only when it thinks it needs to. Note that this\nvalue is in milli-satoshis." + }, + "amp": { + "type": "boolean", + "format": "boolean", + "description": "If set, an AMP-payment will be attempted." } } }, From f07c9d002c29e2cc5c0d8a96e4465a12675b67f6 Mon Sep 17 00:00:00 2001 From: "Johan T. Halseth" Date: Wed, 31 Mar 2021 12:23:08 +0200 Subject: [PATCH 11/17] routing: use Identifier in place of PaymentHash Since we want to support AMP payment using a different unique payment identifier (AMP payments don't go to one specific hash), we change the nomenclature to be Identifier instead of PaymentHash. --- channeldb/duplicate_payments.go | 2 +- channeldb/payment_control.go | 12 +- channeldb/payment_control_test.go | 194 ++++++++++----------- channeldb/payments.go | 9 +- channeldb/payments_test.go | 38 ++-- lnrpc/routerrpc/router_backend.go | 21 ++- lnrpc/routerrpc/router_server.go | 16 +- lntest/itest/log_error_whitelist.txt | 20 +-- routing/control_tower_test.go | 32 ++-- routing/integrated_routing_context_test.go | 5 + routing/payment_lifecycle.go | 58 +++--- routing/payment_lifecycle_test.go | 2 +- routing/payment_session.go | 2 +- routing/payment_session_test.go | 5 + routing/router.go | 94 ++++++---- routing/router_test.go | 26 +-- rpcserver.go | 5 +- 17 files changed, 296 insertions(+), 245 deletions(-) diff --git a/channeldb/duplicate_payments.go b/channeldb/duplicate_payments.go index 5d23764b..0338fab7 100644 --- a/channeldb/duplicate_payments.go +++ b/channeldb/duplicate_payments.go @@ -99,7 +99,7 @@ func deserializeDuplicatePaymentCreationInfo(r io.Reader) ( c := &PaymentCreationInfo{} - if _, err := io.ReadFull(r, c.PaymentHash[:]); err != nil { + if _, err := io.ReadFull(r, c.PaymentIdentifier[:]); err != nil { return nil, err } diff --git a/channeldb/payment_control.go b/channeldb/payment_control.go index a5023567..d7b4e743 100644 --- a/channeldb/payment_control.go +++ b/channeldb/payment_control.go @@ -173,8 +173,10 @@ func (p *PaymentControl) InitPayment(paymentHash lntypes.Hash, // Once we have obtained a sequence number, we add an entry // to our index bucket which will map the sequence number to - // our payment hash. - err = createPaymentIndexEntry(tx, sequenceNum, info.PaymentHash) + // our payment identifier. + err = createPaymentIndexEntry( + tx, sequenceNum, info.PaymentIdentifier, + ) if err != nil { return err } @@ -220,12 +222,12 @@ const paymentIndexTypeHash paymentIndexType = 0 // createPaymentIndexEntry creates a payment hash typed index for a payment. The // index produced contains a payment index type (which can be used in future to -// signal different payment index types) and the payment hash. +// signal different payment index types) and the payment identifier. func createPaymentIndexEntry(tx kvdb.RwTx, sequenceNumber []byte, - hash lntypes.Hash) error { + id lntypes.Hash) error { var b bytes.Buffer - if err := WriteElements(&b, paymentIndexTypeHash, hash[:]); err != nil { + if err := WriteElements(&b, paymentIndexTypeHash, id[:]); err != nil { return err } diff --git a/channeldb/payment_control_test.go b/channeldb/payment_control_test.go index 5bf6a05d..7d293c7f 100644 --- a/channeldb/payment_control_test.go +++ b/channeldb/payment_control_test.go @@ -38,10 +38,10 @@ func genInfo() (*PaymentCreationInfo, *HTLCAttemptInfo, rhash := sha256.Sum256(preimage[:]) return &PaymentCreationInfo{ - PaymentHash: rhash, - Value: testRoute.ReceiverAmt(), - CreationTime: time.Unix(time.Now().Unix(), 0), - PaymentRequest: []byte("hola"), + PaymentIdentifier: rhash, + Value: testRoute.ReceiverAmt(), + CreationTime: time.Unix(time.Now().Unix(), 0), + PaymentRequest: []byte("hola"), }, &HTLCAttemptInfo{ AttemptID: 0, @@ -70,63 +70,63 @@ func TestPaymentControlSwitchFail(t *testing.T) { } // Sends base htlc message which initiate StatusInFlight. - err = pControl.InitPayment(info.PaymentHash, info) + err = pControl.InitPayment(info.PaymentIdentifier, info) if err != nil { t.Fatalf("unable to send htlc message: %v", err) } - assertPaymentIndex(t, pControl, info.PaymentHash) - assertPaymentStatus(t, pControl, info.PaymentHash, StatusInFlight) + assertPaymentIndex(t, pControl, info.PaymentIdentifier) + assertPaymentStatus(t, pControl, info.PaymentIdentifier, StatusInFlight) assertPaymentInfo( - t, pControl, info.PaymentHash, info, nil, nil, + t, pControl, info.PaymentIdentifier, info, nil, nil, ) // Fail the payment, which should moved it to Failed. failReason := FailureReasonNoRoute - _, err = pControl.Fail(info.PaymentHash, failReason) + _, err = pControl.Fail(info.PaymentIdentifier, failReason) if err != nil { t.Fatalf("unable to fail payment hash: %v", err) } // Verify the status is indeed Failed. - assertPaymentStatus(t, pControl, info.PaymentHash, StatusFailed) + assertPaymentStatus(t, pControl, info.PaymentIdentifier, StatusFailed) assertPaymentInfo( - t, pControl, info.PaymentHash, info, &failReason, nil, + t, pControl, info.PaymentIdentifier, info, &failReason, nil, ) // Lookup the payment so we can get its old sequence number before it is // overwritten. - payment, err := pControl.FetchPayment(info.PaymentHash) + payment, err := pControl.FetchPayment(info.PaymentIdentifier) assert.NoError(t, err) // Sends the htlc again, which should succeed since the prior payment // failed. - err = pControl.InitPayment(info.PaymentHash, info) + err = pControl.InitPayment(info.PaymentIdentifier, info) if err != nil { t.Fatalf("unable to send htlc message: %v", err) } // Check that our index has been updated, and the old index has been // removed. - assertPaymentIndex(t, pControl, info.PaymentHash) + assertPaymentIndex(t, pControl, info.PaymentIdentifier) assertNoIndex(t, pControl, payment.SequenceNum) - assertPaymentStatus(t, pControl, info.PaymentHash, StatusInFlight) + assertPaymentStatus(t, pControl, info.PaymentIdentifier, StatusInFlight) assertPaymentInfo( - t, pControl, info.PaymentHash, info, nil, nil, + t, pControl, info.PaymentIdentifier, info, nil, nil, ) // Record a new attempt. In this test scenario, the attempt fails. // However, this is not communicated to control tower in the current // implementation. It only registers the initiation of the attempt. - _, err = pControl.RegisterAttempt(info.PaymentHash, attempt) + _, err = pControl.RegisterAttempt(info.PaymentIdentifier, attempt) if err != nil { t.Fatalf("unable to register attempt: %v", err) } htlcReason := HTLCFailUnreadable _, err = pControl.FailAttempt( - info.PaymentHash, attempt.AttemptID, + info.PaymentIdentifier, attempt.AttemptID, &HTLCFailInfo{ Reason: htlcReason, }, @@ -134,35 +134,35 @@ func TestPaymentControlSwitchFail(t *testing.T) { if err != nil { t.Fatal(err) } - assertPaymentStatus(t, pControl, info.PaymentHash, StatusInFlight) + assertPaymentStatus(t, pControl, info.PaymentIdentifier, StatusInFlight) htlc := &htlcStatus{ HTLCAttemptInfo: attempt, failure: &htlcReason, } - assertPaymentInfo(t, pControl, info.PaymentHash, info, nil, htlc) + assertPaymentInfo(t, pControl, info.PaymentIdentifier, info, nil, htlc) // Record another attempt. attempt.AttemptID = 1 - _, err = pControl.RegisterAttempt(info.PaymentHash, attempt) + _, err = pControl.RegisterAttempt(info.PaymentIdentifier, attempt) if err != nil { t.Fatalf("unable to send htlc message: %v", err) } - assertPaymentStatus(t, pControl, info.PaymentHash, StatusInFlight) + assertPaymentStatus(t, pControl, info.PaymentIdentifier, StatusInFlight) htlc = &htlcStatus{ HTLCAttemptInfo: attempt, } assertPaymentInfo( - t, pControl, info.PaymentHash, info, nil, htlc, + t, pControl, info.PaymentIdentifier, info, nil, htlc, ) // Settle the attempt and verify that status was changed to // StatusSucceeded. payment, err = pControl.SettleAttempt( - info.PaymentHash, attempt.AttemptID, + info.PaymentIdentifier, attempt.AttemptID, &HTLCSettleInfo{ Preimage: preimg, }, @@ -183,16 +183,16 @@ func TestPaymentControlSwitchFail(t *testing.T) { spew.Sdump(payment.HTLCs[0].Route), err) } - assertPaymentStatus(t, pControl, info.PaymentHash, StatusSucceeded) + assertPaymentStatus(t, pControl, info.PaymentIdentifier, StatusSucceeded) htlc.settle = &preimg assertPaymentInfo( - t, pControl, info.PaymentHash, info, nil, htlc, + t, pControl, info.PaymentIdentifier, info, nil, htlc, ) // Attempt a final payment, which should now fail since the prior // payment succeed. - err = pControl.InitPayment(info.PaymentHash, info) + err = pControl.InitPayment(info.PaymentIdentifier, info) if err != ErrAlreadyPaid { t.Fatalf("unable to send htlc message: %v", err) } @@ -219,42 +219,42 @@ func TestPaymentControlSwitchDoubleSend(t *testing.T) { // Sends base htlc message which initiate base status and move it to // StatusInFlight and verifies that it was changed. - err = pControl.InitPayment(info.PaymentHash, info) + err = pControl.InitPayment(info.PaymentIdentifier, info) if err != nil { t.Fatalf("unable to send htlc message: %v", err) } - assertPaymentIndex(t, pControl, info.PaymentHash) - assertPaymentStatus(t, pControl, info.PaymentHash, StatusInFlight) + assertPaymentIndex(t, pControl, info.PaymentIdentifier) + assertPaymentStatus(t, pControl, info.PaymentIdentifier, StatusInFlight) assertPaymentInfo( - t, pControl, info.PaymentHash, info, nil, nil, + t, pControl, info.PaymentIdentifier, info, nil, nil, ) // Try to initiate double sending of htlc message with the same // payment hash, should result in error indicating that payment has // already been sent. - err = pControl.InitPayment(info.PaymentHash, info) + err = pControl.InitPayment(info.PaymentIdentifier, info) if err != ErrPaymentInFlight { t.Fatalf("payment control wrong behaviour: " + "double sending must trigger ErrPaymentInFlight error") } // Record an attempt. - _, err = pControl.RegisterAttempt(info.PaymentHash, attempt) + _, err = pControl.RegisterAttempt(info.PaymentIdentifier, attempt) if err != nil { t.Fatalf("unable to send htlc message: %v", err) } - assertPaymentStatus(t, pControl, info.PaymentHash, StatusInFlight) + assertPaymentStatus(t, pControl, info.PaymentIdentifier, StatusInFlight) htlc := &htlcStatus{ HTLCAttemptInfo: attempt, } assertPaymentInfo( - t, pControl, info.PaymentHash, info, nil, htlc, + t, pControl, info.PaymentIdentifier, info, nil, htlc, ) // Sends base htlc message which initiate StatusInFlight. - err = pControl.InitPayment(info.PaymentHash, info) + err = pControl.InitPayment(info.PaymentIdentifier, info) if err != ErrPaymentInFlight { t.Fatalf("payment control wrong behaviour: " + "double sending must trigger ErrPaymentInFlight error") @@ -262,7 +262,7 @@ func TestPaymentControlSwitchDoubleSend(t *testing.T) { // After settling, the error should be ErrAlreadyPaid. _, err = pControl.SettleAttempt( - info.PaymentHash, attempt.AttemptID, + info.PaymentIdentifier, attempt.AttemptID, &HTLCSettleInfo{ Preimage: preimg, }, @@ -270,12 +270,12 @@ func TestPaymentControlSwitchDoubleSend(t *testing.T) { if err != nil { t.Fatalf("error shouldn't have been received, got: %v", err) } - assertPaymentStatus(t, pControl, info.PaymentHash, StatusSucceeded) + assertPaymentStatus(t, pControl, info.PaymentIdentifier, StatusSucceeded) htlc.settle = &preimg - assertPaymentInfo(t, pControl, info.PaymentHash, info, nil, htlc) + assertPaymentInfo(t, pControl, info.PaymentIdentifier, info, nil, htlc) - err = pControl.InitPayment(info.PaymentHash, info) + err = pControl.InitPayment(info.PaymentIdentifier, info) if err != ErrAlreadyPaid { t.Fatalf("unable to send htlc message: %v", err) } @@ -302,7 +302,7 @@ func TestPaymentControlSuccessesWithoutInFlight(t *testing.T) { // Attempt to complete the payment should fail. _, err = pControl.SettleAttempt( - info.PaymentHash, 0, + info.PaymentIdentifier, 0, &HTLCSettleInfo{ Preimage: preimg, }, @@ -311,7 +311,7 @@ func TestPaymentControlSuccessesWithoutInFlight(t *testing.T) { t.Fatalf("expected ErrPaymentNotInitiated, got %v", err) } - assertPaymentStatus(t, pControl, info.PaymentHash, StatusUnknown) + assertPaymentStatus(t, pControl, info.PaymentIdentifier, StatusUnknown) } // TestPaymentControlFailsWithoutInFlight checks that a strict payment @@ -334,12 +334,12 @@ func TestPaymentControlFailsWithoutInFlight(t *testing.T) { } // Calling Fail should return an error. - _, err = pControl.Fail(info.PaymentHash, FailureReasonNoRoute) + _, err = pControl.Fail(info.PaymentIdentifier, FailureReasonNoRoute) if err != ErrPaymentNotInitiated { t.Fatalf("expected ErrPaymentNotInitiated, got %v", err) } - assertPaymentStatus(t, pControl, info.PaymentHash, StatusUnknown) + assertPaymentStatus(t, pControl, info.PaymentIdentifier, StatusUnknown) } // TestPaymentControlDeleteNonInFlight checks that calling DeletePayments only @@ -397,11 +397,11 @@ func TestPaymentControlDeleteNonInFligt(t *testing.T) { } // Sends base htlc message which initiate StatusInFlight. - err = pControl.InitPayment(info.PaymentHash, info) + err = pControl.InitPayment(info.PaymentIdentifier, info) if err != nil { t.Fatalf("unable to send htlc message: %v", err) } - _, err = pControl.RegisterAttempt(info.PaymentHash, attempt) + _, err = pControl.RegisterAttempt(info.PaymentIdentifier, attempt) if err != nil { t.Fatalf("unable to send htlc message: %v", err) } @@ -414,7 +414,7 @@ func TestPaymentControlDeleteNonInFligt(t *testing.T) { // Fail the payment attempt. htlcFailure := HTLCFailUnreadable _, err := pControl.FailAttempt( - info.PaymentHash, attempt.AttemptID, + info.PaymentIdentifier, attempt.AttemptID, &HTLCFailInfo{ Reason: htlcFailure, }, @@ -425,23 +425,23 @@ func TestPaymentControlDeleteNonInFligt(t *testing.T) { // Fail the payment, which should moved it to Failed. failReason := FailureReasonNoRoute - _, err = pControl.Fail(info.PaymentHash, failReason) + _, err = pControl.Fail(info.PaymentIdentifier, failReason) if err != nil { t.Fatalf("unable to fail payment hash: %v", err) } // Verify the status is indeed Failed. - assertPaymentStatus(t, pControl, info.PaymentHash, StatusFailed) + assertPaymentStatus(t, pControl, info.PaymentIdentifier, StatusFailed) htlc.failure = &htlcFailure assertPaymentInfo( - t, pControl, info.PaymentHash, info, + t, pControl, info.PaymentIdentifier, info, &failReason, htlc, ) } else if p.success { // Verifies that status was changed to StatusSucceeded. _, err := pControl.SettleAttempt( - info.PaymentHash, attempt.AttemptID, + info.PaymentIdentifier, attempt.AttemptID, &HTLCSettleInfo{ Preimage: preimg, }, @@ -450,18 +450,18 @@ func TestPaymentControlDeleteNonInFligt(t *testing.T) { t.Fatalf("error shouldn't have been received, got: %v", err) } - assertPaymentStatus(t, pControl, info.PaymentHash, StatusSucceeded) + assertPaymentStatus(t, pControl, info.PaymentIdentifier, StatusSucceeded) htlc.settle = &preimg assertPaymentInfo( - t, pControl, info.PaymentHash, info, nil, htlc, + t, pControl, info.PaymentIdentifier, info, nil, htlc, ) numSuccess++ } else { - assertPaymentStatus(t, pControl, info.PaymentHash, StatusInFlight) + assertPaymentStatus(t, pControl, info.PaymentIdentifier, StatusInFlight) assertPaymentInfo( - t, pControl, info.PaymentHash, info, nil, htlc, + t, pControl, info.PaymentIdentifier, info, nil, htlc, ) numInflight++ @@ -471,7 +471,7 @@ func TestPaymentControlDeleteNonInFligt(t *testing.T) { // add one. if p.hasDuplicate { appendDuplicatePayment( - t, pControl.db, info.PaymentHash, + t, pControl.db, info.PaymentIdentifier, uint64(duplicateSeqNr), preimg, ) duplicateSeqNr++ @@ -582,20 +582,20 @@ func TestPaymentControlDeletePayments(t *testing.T) { attemptID++ // Init the payment. - err = pControl.InitPayment(info.PaymentHash, info) + err = pControl.InitPayment(info.PaymentIdentifier, info) if err != nil { t.Fatalf("unable to send htlc message: %v", err) } // Register and fail the first attempt for all three payments. - _, err = pControl.RegisterAttempt(info.PaymentHash, attempt) + _, err = pControl.RegisterAttempt(info.PaymentIdentifier, attempt) if err != nil { t.Fatalf("unable to send htlc message: %v", err) } htlcFailure := HTLCFailUnreadable _, err = pControl.FailAttempt( - info.PaymentHash, attempt.AttemptID, + info.PaymentIdentifier, attempt.AttemptID, &HTLCFailInfo{ Reason: htlcFailure, }, @@ -609,7 +609,7 @@ func TestPaymentControlDeletePayments(t *testing.T) { attempt.AttemptID = attemptID attemptID++ - _, err = pControl.RegisterAttempt(info.PaymentHash, attempt) + _, err = pControl.RegisterAttempt(info.PaymentIdentifier, attempt) if err != nil { t.Fatalf("unable to send htlc message: %v", err) } @@ -620,7 +620,7 @@ func TestPaymentControlDeletePayments(t *testing.T) { case 0: htlcFailure := HTLCFailUnreadable _, err = pControl.FailAttempt( - info.PaymentHash, attempt.AttemptID, + info.PaymentIdentifier, attempt.AttemptID, &HTLCFailInfo{ Reason: htlcFailure, }, @@ -630,7 +630,7 @@ func TestPaymentControlDeletePayments(t *testing.T) { } failReason := FailureReasonNoRoute - _, err = pControl.Fail(info.PaymentHash, failReason) + _, err = pControl.Fail(info.PaymentIdentifier, failReason) if err != nil { t.Fatalf("unable to fail payment hash: %v", err) } @@ -638,7 +638,7 @@ func TestPaymentControlDeletePayments(t *testing.T) { // Settle the attempt case 1: _, err := pControl.SettleAttempt( - info.PaymentHash, attempt.AttemptID, + info.PaymentIdentifier, attempt.AttemptID, &HTLCSettleInfo{ Preimage: preimg, }, @@ -807,15 +807,15 @@ func TestPaymentControlMultiShard(t *testing.T) { } // Init the payment, moving it to the StatusInFlight state. - err = pControl.InitPayment(info.PaymentHash, info) + err = pControl.InitPayment(info.PaymentIdentifier, info) if err != nil { t.Fatalf("unable to send htlc message: %v", err) } - assertPaymentIndex(t, pControl, info.PaymentHash) - assertPaymentStatus(t, pControl, info.PaymentHash, StatusInFlight) + assertPaymentIndex(t, pControl, info.PaymentIdentifier) + assertPaymentStatus(t, pControl, info.PaymentIdentifier, StatusInFlight) assertPaymentInfo( - t, pControl, info.PaymentHash, info, nil, nil, + t, pControl, info.PaymentIdentifier, info, nil, nil, ) // Create three unique attempts we'll use for the test, and @@ -834,19 +834,19 @@ func TestPaymentControlMultiShard(t *testing.T) { a.AttemptID = i attempts = append(attempts, &a) - _, err = pControl.RegisterAttempt(info.PaymentHash, &a) + _, err = pControl.RegisterAttempt(info.PaymentIdentifier, &a) if err != nil { t.Fatalf("unable to send htlc message: %v", err) } assertPaymentStatus( - t, pControl, info.PaymentHash, StatusInFlight, + t, pControl, info.PaymentIdentifier, StatusInFlight, ) htlc := &htlcStatus{ HTLCAttemptInfo: &a, } assertPaymentInfo( - t, pControl, info.PaymentHash, info, nil, htlc, + t, pControl, info.PaymentIdentifier, info, nil, htlc, ) } @@ -855,7 +855,7 @@ func TestPaymentControlMultiShard(t *testing.T) { // will be too large. b := *attempt b.AttemptID = 3 - _, err = pControl.RegisterAttempt(info.PaymentHash, &b) + _, err = pControl.RegisterAttempt(info.PaymentIdentifier, &b) if err != ErrValueExceedsAmt { t.Fatalf("expected ErrValueExceedsAmt, got: %v", err) @@ -865,7 +865,7 @@ func TestPaymentControlMultiShard(t *testing.T) { a := attempts[1] htlcFail := HTLCFailUnreadable _, err = pControl.FailAttempt( - info.PaymentHash, a.AttemptID, + info.PaymentIdentifier, a.AttemptID, &HTLCFailInfo{ Reason: htlcFail, }, @@ -879,11 +879,11 @@ func TestPaymentControlMultiShard(t *testing.T) { failure: &htlcFail, } assertPaymentInfo( - t, pControl, info.PaymentHash, info, nil, htlc, + t, pControl, info.PaymentIdentifier, info, nil, htlc, ) // Payment should still be in-flight. - assertPaymentStatus(t, pControl, info.PaymentHash, StatusInFlight) + assertPaymentStatus(t, pControl, info.PaymentIdentifier, StatusInFlight) // Depending on the test case, settle or fail the first attempt. a = attempts[0] @@ -894,7 +894,7 @@ func TestPaymentControlMultiShard(t *testing.T) { var firstFailReason *FailureReason if test.settleFirst { _, err := pControl.SettleAttempt( - info.PaymentHash, a.AttemptID, + info.PaymentIdentifier, a.AttemptID, &HTLCSettleInfo{ Preimage: preimg, }, @@ -907,11 +907,11 @@ func TestPaymentControlMultiShard(t *testing.T) { // Assert that the HTLC has had the preimage recorded. htlc.settle = &preimg assertPaymentInfo( - t, pControl, info.PaymentHash, info, nil, htlc, + t, pControl, info.PaymentIdentifier, info, nil, htlc, ) } else { _, err := pControl.FailAttempt( - info.PaymentHash, a.AttemptID, + info.PaymentIdentifier, a.AttemptID, &HTLCFailInfo{ Reason: htlcFail, }, @@ -924,13 +924,13 @@ func TestPaymentControlMultiShard(t *testing.T) { // Assert the failure was recorded. htlc.failure = &htlcFail assertPaymentInfo( - t, pControl, info.PaymentHash, info, nil, htlc, + t, pControl, info.PaymentIdentifier, info, nil, htlc, ) // We also record a payment level fail, to move it into // a terminal state. failReason := FailureReasonNoRoute - _, err = pControl.Fail(info.PaymentHash, failReason) + _, err = pControl.Fail(info.PaymentIdentifier, failReason) if err != nil { t.Fatalf("unable to fail payment hash: %v", err) } @@ -942,18 +942,18 @@ func TestPaymentControlMultiShard(t *testing.T) { // The payment should still be considered in-flight, since there // is still an active HTLC. - assertPaymentStatus(t, pControl, info.PaymentHash, StatusInFlight) + assertPaymentStatus(t, pControl, info.PaymentIdentifier, StatusInFlight) // Try to register yet another attempt. This should fail now // that the payment has reached a terminal condition. b = *attempt b.AttemptID = 3 - _, err = pControl.RegisterAttempt(info.PaymentHash, &b) + _, err = pControl.RegisterAttempt(info.PaymentIdentifier, &b) if err != ErrPaymentTerminal { t.Fatalf("expected ErrPaymentTerminal, got: %v", err) } - assertPaymentStatus(t, pControl, info.PaymentHash, StatusInFlight) + assertPaymentStatus(t, pControl, info.PaymentIdentifier, StatusInFlight) // Settle or fail the remaining attempt based on the testcase. a = attempts[2] @@ -963,7 +963,7 @@ func TestPaymentControlMultiShard(t *testing.T) { if test.settleLast { // Settle the last outstanding attempt. _, err = pControl.SettleAttempt( - info.PaymentHash, a.AttemptID, + info.PaymentIdentifier, a.AttemptID, &HTLCSettleInfo{ Preimage: preimg, }, @@ -975,13 +975,13 @@ func TestPaymentControlMultiShard(t *testing.T) { htlc.settle = &preimg assertPaymentInfo( - t, pControl, info.PaymentHash, info, + t, pControl, info.PaymentIdentifier, info, firstFailReason, htlc, ) } else { // Fail the attempt. _, err := pControl.FailAttempt( - info.PaymentHash, a.AttemptID, + info.PaymentIdentifier, a.AttemptID, &HTLCFailInfo{ Reason: htlcFail, }, @@ -994,7 +994,7 @@ func TestPaymentControlMultiShard(t *testing.T) { // Assert the failure was recorded. htlc.failure = &htlcFail assertPaymentInfo( - t, pControl, info.PaymentHash, info, + t, pControl, info.PaymentIdentifier, info, firstFailReason, htlc, ) @@ -1003,7 +1003,7 @@ func TestPaymentControlMultiShard(t *testing.T) { // write a terminal failure to the database without // syncing. failReason := FailureReasonPaymentDetails - _, err = pControl.Fail(info.PaymentHash, failReason) + _, err = pControl.Fail(info.PaymentIdentifier, failReason) if err != nil { t.Fatalf("unable to fail payment hash: %v", err) } @@ -1017,10 +1017,10 @@ func TestPaymentControlMultiShard(t *testing.T) { finalStatus = StatusSucceeded } - assertPaymentStatus(t, pControl, info.PaymentHash, finalStatus) + assertPaymentStatus(t, pControl, info.PaymentIdentifier, finalStatus) // Finally assert we cannot register more attempts. - _, err = pControl.RegisterAttempt(info.PaymentHash, &b) + _, err = pControl.RegisterAttempt(info.PaymentIdentifier, &b) require.Equal(t, ErrPaymentTerminal, err) } @@ -1053,7 +1053,7 @@ func TestPaymentControlMPPRecordValidation(t *testing.T) { } // Init the payment. - err = pControl.InitPayment(info.PaymentHash, info) + err = pControl.InitPayment(info.PaymentIdentifier, info) if err != nil { t.Fatalf("unable to send htlc message: %v", err) } @@ -1068,7 +1068,7 @@ func TestPaymentControlMPPRecordValidation(t *testing.T) { info.Value, [32]byte{1}, ) - _, err = pControl.RegisterAttempt(info.PaymentHash, attempt) + _, err = pControl.RegisterAttempt(info.PaymentIdentifier, attempt) if err != nil { t.Fatalf("unable to send htlc message: %v", err) } @@ -1077,7 +1077,7 @@ func TestPaymentControlMPPRecordValidation(t *testing.T) { b := *attempt b.AttemptID = 1 b.Route.FinalHop().MPP = nil - _, err = pControl.RegisterAttempt(info.PaymentHash, &b) + _, err = pControl.RegisterAttempt(info.PaymentIdentifier, &b) if err != ErrMPPayment { t.Fatalf("expected ErrMPPayment, got: %v", err) } @@ -1086,7 +1086,7 @@ func TestPaymentControlMPPRecordValidation(t *testing.T) { b.Route.FinalHop().MPP = record.NewMPP( info.Value, [32]byte{2}, ) - _, err = pControl.RegisterAttempt(info.PaymentHash, &b) + _, err = pControl.RegisterAttempt(info.PaymentIdentifier, &b) if err != ErrMPPPaymentAddrMismatch { t.Fatalf("expected ErrMPPPaymentAddrMismatch, got: %v", err) } @@ -1095,7 +1095,7 @@ func TestPaymentControlMPPRecordValidation(t *testing.T) { b.Route.FinalHop().MPP = record.NewMPP( info.Value/2, [32]byte{1}, ) - _, err = pControl.RegisterAttempt(info.PaymentHash, &b) + _, err = pControl.RegisterAttempt(info.PaymentIdentifier, &b) if err != ErrMPPTotalAmountMismatch { t.Fatalf("expected ErrMPPTotalAmountMismatch, got: %v", err) } @@ -1107,13 +1107,13 @@ func TestPaymentControlMPPRecordValidation(t *testing.T) { t.Fatalf("unable to generate htlc message: %v", err) } - err = pControl.InitPayment(info.PaymentHash, info) + err = pControl.InitPayment(info.PaymentIdentifier, info) if err != nil { t.Fatalf("unable to send htlc message: %v", err) } attempt.Route.FinalHop().MPP = nil - _, err = pControl.RegisterAttempt(info.PaymentHash, attempt) + _, err = pControl.RegisterAttempt(info.PaymentIdentifier, attempt) if err != nil { t.Fatalf("unable to send htlc message: %v", err) } @@ -1125,7 +1125,7 @@ func TestPaymentControlMPPRecordValidation(t *testing.T) { info.Value, [32]byte{1}, ) - _, err = pControl.RegisterAttempt(info.PaymentHash, &b) + _, err = pControl.RegisterAttempt(info.PaymentIdentifier, &b) if err != ErrNonMPPayment { t.Fatalf("expected ErrNonMPPayment, got: %v", err) } diff --git a/channeldb/payments.go b/channeldb/payments.go index 2ffd76a0..96748d6f 100644 --- a/channeldb/payments.go +++ b/channeldb/payments.go @@ -216,8 +216,9 @@ func (ps PaymentStatus) String() string { // PaymentCreationInfo is the information necessary to have ready when // initiating a payment, moving it into state InFlight. type PaymentCreationInfo struct { - // PaymentHash is the hash this payment is paying to. - PaymentHash lntypes.Hash + // PaymentIdentifier is the hash this payment is paying to in case of + // non-AMP payments, and the SetID for AMP payments. + PaymentIdentifier lntypes.Hash // Value is the amount we are paying. Value lnwire.MilliSatoshi @@ -856,7 +857,7 @@ func fetchSequenceNumbers(paymentBucket kvdb.RBucket) ([][]byte, error) { func serializePaymentCreationInfo(w io.Writer, c *PaymentCreationInfo) error { var scratch [8]byte - if _, err := w.Write(c.PaymentHash[:]); err != nil { + if _, err := w.Write(c.PaymentIdentifier[:]); err != nil { return err } @@ -886,7 +887,7 @@ func deserializePaymentCreationInfo(r io.Reader) (*PaymentCreationInfo, error) { c := &PaymentCreationInfo{} - if _, err := io.ReadFull(r, c.PaymentHash[:]); err != nil { + if _, err := io.ReadFull(r, c.PaymentIdentifier[:]); err != nil { return nil, err } diff --git a/channeldb/payments_test.go b/channeldb/payments_test.go index 8974865a..c7f6e98a 100644 --- a/channeldb/payments_test.go +++ b/channeldb/payments_test.go @@ -60,8 +60,8 @@ func makeFakeInfo() (*PaymentCreationInfo, *HTLCAttemptInfo) { hash := preimg.Hash() c := &PaymentCreationInfo{ - PaymentHash: hash, - Value: 1000, + PaymentIdentifier: hash, + Value: 1000, // Use single second precision to avoid false positive test // failures due to the monotonic time component. CreationTime: time.Unix(time.Now().Unix(), 0), @@ -433,7 +433,7 @@ func TestQueryPayments(t *testing.T) { } // Create a new payment entry in the database. - err = pControl.InitPayment(info.PaymentHash, info) + err = pControl.InitPayment(info.PaymentIdentifier, info) if err != nil { t.Fatalf("unable to initialize "+ "payment in database: %v", err) @@ -442,11 +442,11 @@ func TestQueryPayments(t *testing.T) { // Immediately delete the payment with index 2. if i == 1 { pmt, err := pControl.FetchPayment( - info.PaymentHash, + info.PaymentIdentifier, ) require.NoError(t, err) - deletePayment(t, db, info.PaymentHash, + deletePayment(t, db, info.PaymentIdentifier, pmt.SequenceNum) } @@ -456,13 +456,13 @@ func TestQueryPayments(t *testing.T) { // duplicate payments will always be succeeded. if i == (nonDuplicatePayments - 1) { pmt, err := pControl.FetchPayment( - info.PaymentHash, + info.PaymentIdentifier, ) require.NoError(t, err) appendDuplicatePayment( t, pControl.db, - info.PaymentHash, + info.PaymentIdentifier, pmt.SequenceNum+1, preimg, ) @@ -529,12 +529,12 @@ func TestFetchPaymentWithSequenceNumber(t *testing.T) { require.NoError(t, err) // Create a new payment entry in the database. - err = pControl.InitPayment(noDuplicates.PaymentHash, noDuplicates) + err = pControl.InitPayment(noDuplicates.PaymentIdentifier, noDuplicates) require.NoError(t, err) // Fetch the payment so we can get its sequence nr. noDuplicatesPayment, err := pControl.FetchPayment( - noDuplicates.PaymentHash, + noDuplicates.PaymentIdentifier, ) require.NoError(t, err) @@ -543,12 +543,12 @@ func TestFetchPaymentWithSequenceNumber(t *testing.T) { require.NoError(t, err) // Create a new payment entry in the database. - err = pControl.InitPayment(hasDuplicates.PaymentHash, hasDuplicates) + err = pControl.InitPayment(hasDuplicates.PaymentIdentifier, hasDuplicates) require.NoError(t, err) // Fetch the payment so we can get its sequence nr. hasDuplicatesPayment, err := pControl.FetchPayment( - hasDuplicates.PaymentHash, + hasDuplicates.PaymentIdentifier, ) require.NoError(t, err) @@ -561,10 +561,10 @@ func TestFetchPaymentWithSequenceNumber(t *testing.T) { // Add two duplicates to our second payment. appendDuplicatePayment( - t, db, hasDuplicates.PaymentHash, duplicateOneSeqNr, preimg, + t, db, hasDuplicates.PaymentIdentifier, duplicateOneSeqNr, preimg, ) appendDuplicatePayment( - t, db, hasDuplicates.PaymentHash, duplicateTwoSeqNr, preimg, + t, db, hasDuplicates.PaymentIdentifier, duplicateTwoSeqNr, preimg, ) tests := []struct { @@ -575,37 +575,37 @@ func TestFetchPaymentWithSequenceNumber(t *testing.T) { }{ { name: "lookup payment without duplicates", - paymentHash: noDuplicates.PaymentHash, + paymentHash: noDuplicates.PaymentIdentifier, sequenceNumber: noDuplicatesPayment.SequenceNum, expectedErr: nil, }, { name: "lookup payment with duplicates", - paymentHash: hasDuplicates.PaymentHash, + paymentHash: hasDuplicates.PaymentIdentifier, sequenceNumber: hasDuplicatesPayment.SequenceNum, expectedErr: nil, }, { name: "lookup first duplicate", - paymentHash: hasDuplicates.PaymentHash, + paymentHash: hasDuplicates.PaymentIdentifier, sequenceNumber: duplicateOneSeqNr, expectedErr: nil, }, { name: "lookup second duplicate", - paymentHash: hasDuplicates.PaymentHash, + paymentHash: hasDuplicates.PaymentIdentifier, sequenceNumber: duplicateTwoSeqNr, expectedErr: nil, }, { name: "lookup non-existent duplicate", - paymentHash: hasDuplicates.PaymentHash, + paymentHash: hasDuplicates.PaymentIdentifier, sequenceNumber: 999999, expectedErr: ErrDuplicateNotFound, }, { name: "lookup duplicate, no duplicates bucket", - paymentHash: noDuplicates.PaymentHash, + paymentHash: noDuplicates.PaymentIdentifier, sequenceNumber: duplicateTwoSeqNr, expectedErr: ErrNoDuplicateBucket, }, diff --git a/lnrpc/routerrpc/router_backend.go b/lnrpc/routerrpc/router_backend.go index 66fab66e..9cf2c710 100644 --- a/lnrpc/routerrpc/router_backend.go +++ b/lnrpc/routerrpc/router_backend.go @@ -698,7 +698,11 @@ func (r *RouterBackend) extractIntentFromSendRequest( payIntent.MaxParts = 1 } - copy(payIntent.PaymentHash[:], payReq.PaymentHash[:]) + err = payIntent.SetPaymentHash(*payReq.PaymentHash) + if err != nil { + return nil, err + } + destKey := payReq.Destination.SerializeCompressed() copy(payIntent.Target[:], destKey) @@ -737,7 +741,15 @@ func (r *RouterBackend) extractIntentFromSendRequest( payIntent.Amount = reqAmt // Payment hash. - copy(payIntent.PaymentHash[:], rpcPayReq.PaymentHash) + paymentHash, err := lntypes.MakeHash(rpcPayReq.PaymentHash) + if err != nil { + return nil, err + } + + err = payIntent.SetPaymentHash(paymentHash) + if err != nil { + return nil, err + } // Parse destination feature bits. features, err := UnmarshalFeatures(rpcPayReq.DestFeatures) @@ -1217,7 +1229,7 @@ func (r *RouterBackend) MarshallPayment(payment *channeldb.MPPayment) ( htlcs = append(htlcs, htlc) } - paymentHash := payment.Info.PaymentHash + paymentID := payment.Info.PaymentIdentifier creationTimeNS := MarshalTimeNano(payment.Info.CreationTime) failureReason, err := marshallPaymentFailureReason( @@ -1228,7 +1240,8 @@ func (r *RouterBackend) MarshallPayment(payment *channeldb.MPPayment) ( } return &lnrpc.Payment{ - PaymentHash: hex.EncodeToString(paymentHash[:]), + // TODO: set this to setID for AMP-payments? + PaymentHash: hex.EncodeToString(paymentID[:]), Value: satValue, ValueMsat: msatValue, ValueSat: satValue, diff --git a/lnrpc/routerrpc/router_server.go b/lnrpc/routerrpc/router_server.go index ba9d9a9e..8c40517d 100644 --- a/lnrpc/routerrpc/router_server.go +++ b/lnrpc/routerrpc/router_server.go @@ -316,21 +316,21 @@ func (s *Server) SendPaymentV2(req *SendPaymentRequest, if err == channeldb.ErrPaymentInFlight || err == channeldb.ErrAlreadyPaid { - log.Debugf("SendPayment async result for hash %x: %v", - payment.PaymentHash, err) + log.Debugf("SendPayment async result for payment %x: %v", + payment.Identifier(), err) return status.Error( codes.AlreadyExists, err.Error(), ) } - log.Errorf("SendPayment async error for hash %x: %v", - payment.PaymentHash, err) + log.Errorf("SendPayment async error for payment %x: %v", + payment.Identifier(), err) return err } - return s.trackPayment(payment.PaymentHash, stream, req.NoInflightUpdates) + return s.trackPayment(payment.Identifier(), stream, req.NoInflightUpdates) } // EstimateRouteFee allows callers to obtain a lower bound w.r.t how much it @@ -719,14 +719,14 @@ func (s *Server) TrackPaymentV2(request *TrackPaymentRequest, } // trackPayment writes payment status updates to the provided stream. -func (s *Server) trackPayment(paymentHash lntypes.Hash, +func (s *Server) trackPayment(identifier lntypes.Hash, stream Router_TrackPaymentV2Server, noInflightUpdates bool) error { router := s.cfg.RouterBackend // Subscribe to the outcome of this payment. subscription, err := router.Tower.SubscribePayment( - paymentHash, + identifier, ) switch { case err == channeldb.ErrPaymentNotInitiated: @@ -769,7 +769,7 @@ func (s *Server) trackPayment(paymentHash lntypes.Hash, return errServerShuttingDown case <-stream.Context().Done(): - log.Debugf("Payment status stream %v canceled", paymentHash) + log.Debugf("Payment status stream %v canceled", identifier) return stream.Context().Err() } } diff --git a/lntest/itest/log_error_whitelist.txt b/lntest/itest/log_error_whitelist.txt index 08b3419f..ec164c9d 100644 --- a/lntest/itest/log_error_whitelist.txt +++ b/lntest/itest/log_error_whitelist.txt @@ -36,16 +36,16 @@