Merge pull request #3831 from carlaKC/invoices-surfaceupdateresult
Add MPPTimeout ResolutionResult and expose to caller
This commit is contained in:
commit
8d0205e9e4
@ -167,10 +167,10 @@ func (h *htlcIncomingContestResolver) Resolve() (ContractResolver, error) {
|
||||
preimageSubscription := h.PreimageDB.SubscribeUpdates()
|
||||
defer preimageSubscription.CancelSubscription()
|
||||
|
||||
// Define closure to process hodl events either direct or triggered by
|
||||
// later notifcation.
|
||||
processHodlEvent := func(e invoices.HodlEvent) (ContractResolver,
|
||||
error) {
|
||||
// Define closure to process htlc resolutions either direct or triggered by
|
||||
// later notification.
|
||||
processHtlcResolution := func(e invoices.HtlcResolution) (
|
||||
ContractResolver, error) {
|
||||
|
||||
if e.Preimage == nil {
|
||||
log.Infof("%T(%v): Exit hop HTLC canceled "+
|
||||
@ -201,23 +201,26 @@ func (h *htlcIncomingContestResolver) Resolve() (ContractResolver, error) {
|
||||
HtlcID: h.htlc.HtlcIndex,
|
||||
}
|
||||
|
||||
event, err := h.Registry.NotifyExitHopHtlc(
|
||||
resolution, err := h.Registry.NotifyExitHopHtlc(
|
||||
h.htlc.RHash, h.htlc.Amt, h.htlcExpiry, currentHeight,
|
||||
circuitKey, hodlChan, payload,
|
||||
)
|
||||
switch err {
|
||||
case channeldb.ErrInvoiceNotFound:
|
||||
case nil:
|
||||
defer h.Registry.HodlUnsubscribeAll(hodlChan)
|
||||
|
||||
// Resolve the htlc directly if possible.
|
||||
if event != nil {
|
||||
return processHodlEvent(*event)
|
||||
}
|
||||
default:
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
defer h.Registry.HodlUnsubscribeAll(hodlChan)
|
||||
|
||||
// If the resolution is non-nil (indicating that a settle or cancel has
|
||||
// occurred), and the invoice is known to the registry (indicating that
|
||||
// the htlc is paying one of our invoices and is not a forward), try to
|
||||
// resolve it directly.
|
||||
if resolution != nil &&
|
||||
resolution.Outcome != invoices.ResultInvoiceNotFound {
|
||||
|
||||
return processHtlcResolution(*resolution)
|
||||
}
|
||||
|
||||
// With the epochs and preimage subscriptions initialized, we'll query
|
||||
// to see if we already know the preimage.
|
||||
preimage, ok := h.PreimageDB.LookupPreimage(h.htlc.RHash)
|
||||
@ -252,9 +255,9 @@ func (h *htlcIncomingContestResolver) Resolve() (ContractResolver, error) {
|
||||
return &h.htlcSuccessResolver, nil
|
||||
|
||||
case hodlItem := <-hodlChan:
|
||||
hodlEvent := hodlItem.(invoices.HodlEvent)
|
||||
htlcResolution := hodlItem.(invoices.HtlcResolution)
|
||||
|
||||
return processHodlEvent(hodlEvent)
|
||||
return processHtlcResolution(htlcResolution)
|
||||
|
||||
case newBlock, ok := <-blockEpochs.Epochs:
|
||||
if !ok {
|
||||
|
@ -21,10 +21,11 @@ const (
|
||||
)
|
||||
|
||||
var (
|
||||
testResPreimage = lntypes.Preimage{1, 2, 3}
|
||||
testResHash = testResPreimage.Hash()
|
||||
testResCircuitKey = channeldb.CircuitKey{}
|
||||
testOnionBlob = []byte{4, 5, 6}
|
||||
testResPreimage = lntypes.Preimage{1, 2, 3}
|
||||
testResHash = testResPreimage.Hash()
|
||||
testResCircuitKey = channeldb.CircuitKey{}
|
||||
testOnionBlob = []byte{4, 5, 6}
|
||||
testAcceptHeight int32 = 1234
|
||||
)
|
||||
|
||||
// TestHtlcIncomingResolverFwdPreimageKnown tests resolution of a forwarded htlc
|
||||
@ -34,7 +35,10 @@ func TestHtlcIncomingResolverFwdPreimageKnown(t *testing.T) {
|
||||
defer timeout(t)()
|
||||
|
||||
ctx := newIncomingResolverTestContext(t)
|
||||
ctx.registry.notifyErr = channeldb.ErrInvoiceNotFound
|
||||
ctx.registry.notifyResolution = invoices.NewFailureResolution(
|
||||
testResCircuitKey, testHtlcExpiry,
|
||||
invoices.ResultInvoiceNotFound,
|
||||
)
|
||||
ctx.witnessBeacon.lookupPreimage[testResHash] = testResPreimage
|
||||
ctx.resolve()
|
||||
ctx.waitForResult(true)
|
||||
@ -48,7 +52,10 @@ func TestHtlcIncomingResolverFwdContestedSuccess(t *testing.T) {
|
||||
defer timeout(t)()
|
||||
|
||||
ctx := newIncomingResolverTestContext(t)
|
||||
ctx.registry.notifyErr = channeldb.ErrInvoiceNotFound
|
||||
ctx.registry.notifyResolution = invoices.NewFailureResolution(
|
||||
testResCircuitKey, testHtlcExpiry,
|
||||
invoices.ResultInvoiceNotFound,
|
||||
)
|
||||
ctx.resolve()
|
||||
|
||||
// Simulate a new block coming in. HTLC is not yet expired.
|
||||
@ -65,7 +72,10 @@ func TestHtlcIncomingResolverFwdContestedTimeout(t *testing.T) {
|
||||
defer timeout(t)()
|
||||
|
||||
ctx := newIncomingResolverTestContext(t)
|
||||
ctx.registry.notifyErr = channeldb.ErrInvoiceNotFound
|
||||
ctx.registry.notifyResolution = invoices.NewFailureResolution(
|
||||
testResCircuitKey, testHtlcExpiry,
|
||||
invoices.ResultInvoiceNotFound,
|
||||
)
|
||||
ctx.resolve()
|
||||
|
||||
// Simulate a new block coming in. HTLC expires.
|
||||
@ -81,8 +91,10 @@ func TestHtlcIncomingResolverFwdTimeout(t *testing.T) {
|
||||
defer timeout(t)()
|
||||
|
||||
ctx := newIncomingResolverTestContext(t)
|
||||
|
||||
ctx.registry.notifyErr = channeldb.ErrInvoiceNotFound
|
||||
ctx.registry.notifyResolution = invoices.NewFailureResolution(
|
||||
testResCircuitKey, testHtlcExpiry,
|
||||
invoices.ResultInvoiceNotFound,
|
||||
)
|
||||
ctx.witnessBeacon.lookupPreimage[testResHash] = testResPreimage
|
||||
ctx.resolver.htlcExpiry = 90
|
||||
ctx.resolve()
|
||||
@ -96,10 +108,11 @@ func TestHtlcIncomingResolverExitSettle(t *testing.T) {
|
||||
defer timeout(t)()
|
||||
|
||||
ctx := newIncomingResolverTestContext(t)
|
||||
ctx.registry.notifyEvent = &invoices.HodlEvent{
|
||||
CircuitKey: testResCircuitKey,
|
||||
Preimage: &testResPreimage,
|
||||
}
|
||||
ctx.registry.notifyResolution = invoices.NewSettleResolution(
|
||||
testResPreimage, testResCircuitKey, testAcceptHeight,
|
||||
invoices.ResultReplayToSettled,
|
||||
)
|
||||
|
||||
ctx.resolve()
|
||||
|
||||
data := <-ctx.registry.notifyChan
|
||||
@ -126,9 +139,11 @@ func TestHtlcIncomingResolverExitCancel(t *testing.T) {
|
||||
defer timeout(t)()
|
||||
|
||||
ctx := newIncomingResolverTestContext(t)
|
||||
ctx.registry.notifyEvent = &invoices.HodlEvent{
|
||||
CircuitKey: testResCircuitKey,
|
||||
}
|
||||
ctx.registry.notifyResolution = invoices.NewFailureResolution(
|
||||
testResCircuitKey, testAcceptHeight,
|
||||
invoices.ResultInvoiceAlreadyCanceled,
|
||||
)
|
||||
|
||||
ctx.resolve()
|
||||
ctx.waitForResult(false)
|
||||
}
|
||||
@ -143,10 +158,10 @@ func TestHtlcIncomingResolverExitSettleHodl(t *testing.T) {
|
||||
ctx.resolve()
|
||||
|
||||
notifyData := <-ctx.registry.notifyChan
|
||||
notifyData.hodlChan <- invoices.HodlEvent{
|
||||
CircuitKey: testResCircuitKey,
|
||||
Preimage: &testResPreimage,
|
||||
}
|
||||
notifyData.hodlChan <- *invoices.NewSettleResolution(
|
||||
testResPreimage, testResCircuitKey, testAcceptHeight,
|
||||
invoices.ResultSettled,
|
||||
)
|
||||
|
||||
ctx.waitForResult(true)
|
||||
}
|
||||
@ -172,9 +187,10 @@ func TestHtlcIncomingResolverExitCancelHodl(t *testing.T) {
|
||||
ctx := newIncomingResolverTestContext(t)
|
||||
ctx.resolve()
|
||||
notifyData := <-ctx.registry.notifyChan
|
||||
notifyData.hodlChan <- invoices.HodlEvent{
|
||||
CircuitKey: testResCircuitKey,
|
||||
}
|
||||
notifyData.hodlChan <- *invoices.NewFailureResolution(
|
||||
testResCircuitKey, testAcceptHeight, invoices.ResultCanceled,
|
||||
)
|
||||
|
||||
ctx.waitForResult(false)
|
||||
}
|
||||
|
||||
|
@ -27,9 +27,9 @@ type Registry interface {
|
||||
NotifyExitHopHtlc(payHash lntypes.Hash, paidAmount lnwire.MilliSatoshi,
|
||||
expiry uint32, currentHeight int32,
|
||||
circuitKey channeldb.CircuitKey, hodlChan chan<- interface{},
|
||||
payload invoices.Payload) (*invoices.HodlEvent, error)
|
||||
payload invoices.Payload) (*invoices.HtlcResolution, error)
|
||||
|
||||
// HodlUnsubscribeAll unsubscribes from all hodl events.
|
||||
// HodlUnsubscribeAll unsubscribes from all htlc resolutions.
|
||||
HodlUnsubscribeAll(subscriber chan<- interface{})
|
||||
}
|
||||
|
||||
|
@ -16,15 +16,15 @@ type notifyExitHopData struct {
|
||||
}
|
||||
|
||||
type mockRegistry struct {
|
||||
notifyChan chan notifyExitHopData
|
||||
notifyErr error
|
||||
notifyEvent *invoices.HodlEvent
|
||||
notifyChan chan notifyExitHopData
|
||||
notifyErr error
|
||||
notifyResolution *invoices.HtlcResolution
|
||||
}
|
||||
|
||||
func (r *mockRegistry) NotifyExitHopHtlc(payHash lntypes.Hash,
|
||||
paidAmount lnwire.MilliSatoshi, expiry uint32, currentHeight int32,
|
||||
circuitKey channeldb.CircuitKey, hodlChan chan<- interface{},
|
||||
payload invoices.Payload) (*invoices.HodlEvent, error) {
|
||||
payload invoices.Payload) (*invoices.HtlcResolution, error) {
|
||||
|
||||
r.notifyChan <- notifyExitHopData{
|
||||
hodlChan: hodlChan,
|
||||
@ -34,7 +34,7 @@ func (r *mockRegistry) NotifyExitHopHtlc(payHash lntypes.Hash,
|
||||
currentHeight: currentHeight,
|
||||
}
|
||||
|
||||
return r.notifyEvent, r.notifyErr
|
||||
return r.notifyResolution, r.notifyErr
|
||||
}
|
||||
|
||||
func (r *mockRegistry) HodlUnsubscribeAll(subscriber chan<- interface{}) {}
|
||||
|
@ -27,7 +27,7 @@ type InvoiceDatabase interface {
|
||||
NotifyExitHopHtlc(payHash lntypes.Hash, paidAmount lnwire.MilliSatoshi,
|
||||
expiry uint32, currentHeight int32,
|
||||
circuitKey channeldb.CircuitKey, hodlChan chan<- interface{},
|
||||
payload invoices.Payload) (*invoices.HodlEvent, error)
|
||||
payload invoices.Payload) (*invoices.HtlcResolution, error)
|
||||
|
||||
// CancelInvoice attempts to cancel the invoice corresponding to the
|
||||
// passed payment hash.
|
||||
@ -36,7 +36,7 @@ type InvoiceDatabase interface {
|
||||
// SettleHodlInvoice settles a hold invoice.
|
||||
SettleHodlInvoice(preimage lntypes.Preimage) error
|
||||
|
||||
// HodlUnsubscribeAll unsubscribes from all hodl events.
|
||||
// HodlUnsubscribeAll unsubscribes from all htlc resolutions.
|
||||
HodlUnsubscribeAll(subscriber chan<- interface{})
|
||||
}
|
||||
|
||||
|
@ -489,8 +489,8 @@ func (l *channelLink) Stop() {
|
||||
|
||||
l.log.Info("stopping")
|
||||
|
||||
// As the link is stopping, we are no longer interested in hodl events
|
||||
// coming from the invoice registry.
|
||||
// As the link is stopping, we are no longer interested in htlc
|
||||
// resolutions coming from the invoice registry.
|
||||
l.cfg.Registry.HodlUnsubscribeAll(l.hodlQueue.ChanIn())
|
||||
|
||||
if l.cfg.ChainEvents.Cancel != nil {
|
||||
@ -1126,11 +1126,11 @@ out:
|
||||
case msg := <-l.upstream:
|
||||
l.handleUpstreamMsg(msg)
|
||||
|
||||
// A hodl event is received. This means that we now have a
|
||||
// A htlc resolution is received. This means that we now have a
|
||||
// resolution for a previously accepted htlc.
|
||||
case hodlItem := <-l.hodlQueue.ChanOut():
|
||||
hodlEvent := hodlItem.(invoices.HodlEvent)
|
||||
err := l.processHodlQueue(hodlEvent)
|
||||
htlcResolution := hodlItem.(invoices.HtlcResolution)
|
||||
err := l.processHodlQueue(htlcResolution)
|
||||
if err != nil {
|
||||
l.fail(LinkFailureError{code: ErrInternalError},
|
||||
fmt.Sprintf("process hodl queue: %v",
|
||||
@ -1145,24 +1145,26 @@ out:
|
||||
}
|
||||
}
|
||||
|
||||
// processHodlQueue processes a received hodl event and continues reading from
|
||||
// the hodl queue until no more events remain. When this function returns
|
||||
// without an error, the commit tx should be updated.
|
||||
func (l *channelLink) processHodlQueue(firstHodlEvent invoices.HodlEvent) error {
|
||||
// processHodlQueue processes a received htlc resolution and continues reading
|
||||
// from the hodl queue until no more resolutions remain. When this function
|
||||
// returns without an error, the commit tx should be updated.
|
||||
func (l *channelLink) processHodlQueue(
|
||||
firstResolution invoices.HtlcResolution) error {
|
||||
|
||||
// Try to read all waiting resolution messages, so that they can all be
|
||||
// processed in a single commitment tx update.
|
||||
hodlEvent := firstHodlEvent
|
||||
htlcResolution := firstResolution
|
||||
loop:
|
||||
for {
|
||||
// Lookup all hodl htlcs that can be failed or settled with this event.
|
||||
// The hodl htlc must be present in the map.
|
||||
circuitKey := hodlEvent.CircuitKey
|
||||
circuitKey := htlcResolution.CircuitKey
|
||||
hodlHtlc, ok := l.hodlMap[circuitKey]
|
||||
if !ok {
|
||||
return fmt.Errorf("hodl htlc not found: %v", circuitKey)
|
||||
}
|
||||
|
||||
if err := l.processHodlEvent(hodlEvent, hodlHtlc); err != nil {
|
||||
if err := l.processHtlcResolution(htlcResolution, hodlHtlc); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@ -1171,7 +1173,7 @@ loop:
|
||||
|
||||
select {
|
||||
case item := <-l.hodlQueue.ChanOut():
|
||||
hodlEvent = item.(invoices.HodlEvent)
|
||||
htlcResolution = item.(invoices.HtlcResolution)
|
||||
default:
|
||||
break loop
|
||||
}
|
||||
@ -1185,30 +1187,30 @@ loop:
|
||||
return nil
|
||||
}
|
||||
|
||||
// processHodlEvent applies a received hodl event to the provided htlc. When
|
||||
// this function returns without an error, the commit tx should be updated.
|
||||
func (l *channelLink) processHodlEvent(hodlEvent invoices.HodlEvent,
|
||||
// processHtlcResolution applies a received htlc resolution to the provided
|
||||
// htlc. When this function returns without an error, the commit tx should be
|
||||
// updated.
|
||||
func (l *channelLink) processHtlcResolution(resolution invoices.HtlcResolution,
|
||||
htlc hodlHtlc) error {
|
||||
|
||||
circuitKey := hodlEvent.CircuitKey
|
||||
circuitKey := resolution.CircuitKey
|
||||
|
||||
// Determine required action for the resolution.
|
||||
if hodlEvent.Preimage != nil {
|
||||
l.log.Debugf("received hodl settle event for %v", circuitKey)
|
||||
// Determine required action for the resolution. If the event's preimage is
|
||||
// non-nil, the htlc must be settled. Otherwise, it should be canceled.
|
||||
if resolution.Preimage != nil {
|
||||
l.log.Debugf("received settle resolution for %v", circuitKey)
|
||||
|
||||
return l.settleHTLC(
|
||||
*hodlEvent.Preimage, htlc.pd.HtlcIndex,
|
||||
*resolution.Preimage, htlc.pd.HtlcIndex,
|
||||
htlc.pd.SourceRef,
|
||||
)
|
||||
}
|
||||
|
||||
l.log.Debugf("received hodl cancel event for %v", circuitKey)
|
||||
l.log.Debugf("received cancel resolution for %v with outcome: %v",
|
||||
circuitKey, resolution.Outcome)
|
||||
|
||||
// In case of a cancel, always return
|
||||
// incorrect_or_unknown_payment_details in order to avoid leaking info.
|
||||
failure := lnwire.NewFailIncorrectDetails(
|
||||
htlc.pd.Amount, uint32(hodlEvent.AcceptHeight),
|
||||
)
|
||||
// Get the lnwire failure message based on the resolution result.
|
||||
failure := getResolutionFailure(resolution, htlc.pd.Amount)
|
||||
|
||||
l.sendHTLCError(
|
||||
htlc.pd.HtlcIndex, failure, htlc.obfuscator,
|
||||
@ -1217,6 +1219,25 @@ func (l *channelLink) processHodlEvent(hodlEvent invoices.HodlEvent,
|
||||
return nil
|
||||
}
|
||||
|
||||
// getResolutionFailure returns the wire message that a htlc resolution should
|
||||
// be failed with.
|
||||
func getResolutionFailure(resolution invoices.HtlcResolution,
|
||||
amount lnwire.MilliSatoshi) lnwire.FailureMessage {
|
||||
|
||||
// If the resolution has been resolved as part of a MPP timeout, we need
|
||||
// to fail the htlc with lnwire.FailMppTimeout.
|
||||
if resolution.Outcome == invoices.ResultMppTimeout {
|
||||
return &lnwire.FailMPPTimeout{}
|
||||
}
|
||||
|
||||
// If the htlc is not a MPP timeout, we fail it with FailIncorrectDetails
|
||||
// This covers hodl cancels (which return it to avoid leaking information
|
||||
// and other invoice failures such as underpayment or expiry too soon.
|
||||
return lnwire.NewFailIncorrectDetails(
|
||||
amount, uint32(resolution.AcceptHeight),
|
||||
)
|
||||
}
|
||||
|
||||
// randomFeeUpdateTimeout returns a random timeout between the bounds defined
|
||||
// within the link's configuration that will be used to determine when the link
|
||||
// should propose an update to its commitment fee rate.
|
||||
@ -2817,21 +2838,7 @@ func (l *channelLink) processExitHop(pd *lnwallet.PaymentDescriptor,
|
||||
invoiceHash, pd.Amount, pd.Timeout, int32(heightNow),
|
||||
circuitKey, l.hodlQueue.ChanIn(), payload,
|
||||
)
|
||||
|
||||
switch err {
|
||||
|
||||
// Cancel htlc if we don't have an invoice for it.
|
||||
case channeldb.ErrInvoiceNotFound:
|
||||
failure := lnwire.NewFailIncorrectDetails(pd.Amount, heightNow)
|
||||
l.sendHTLCError(pd.HtlcIndex, failure, obfuscator, pd.SourceRef)
|
||||
|
||||
return nil
|
||||
|
||||
// No error.
|
||||
case nil:
|
||||
|
||||
// Pass error to caller.
|
||||
default:
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@ -2841,15 +2848,15 @@ func (l *channelLink) processExitHop(pd *lnwallet.PaymentDescriptor,
|
||||
obfuscator: obfuscator,
|
||||
}
|
||||
|
||||
// If the event is nil, the invoice is being held, so we save payment
|
||||
// descriptor for future reference.
|
||||
if event == nil {
|
||||
// Save payment descriptor for future reference.
|
||||
l.hodlMap[circuitKey] = htlc
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Process the received resolution.
|
||||
return l.processHodlEvent(*event, htlc)
|
||||
return l.processHtlcResolution(*event, htlc)
|
||||
}
|
||||
|
||||
// settleHTLC settles the HTLC on the channel.
|
||||
|
@ -819,7 +819,7 @@ func (i *mockInvoiceRegistry) SettleHodlInvoice(preimage lntypes.Preimage) error
|
||||
func (i *mockInvoiceRegistry) NotifyExitHopHtlc(rhash lntypes.Hash,
|
||||
amt lnwire.MilliSatoshi, expiry uint32, currentHeight int32,
|
||||
circuitKey channeldb.CircuitKey, hodlChan chan<- interface{},
|
||||
payload invoices.Payload) (*invoices.HodlEvent, error) {
|
||||
payload invoices.Payload) (*invoices.HtlcResolution, error) {
|
||||
|
||||
event, err := i.registry.NotifyExitHopHtlc(
|
||||
rhash, amt, expiry, currentHeight, circuitKey, hodlChan,
|
||||
|
@ -35,10 +35,10 @@ const (
|
||||
DefaultHtlcHoldDuration = 120 * time.Second
|
||||
)
|
||||
|
||||
// HodlEvent describes how an htlc should be resolved. If HodlEvent.Preimage is
|
||||
// set, the event indicates a settle event. If Preimage is nil, it is a cancel
|
||||
// event.
|
||||
type HodlEvent struct {
|
||||
// HtlcResolution describes how an htlc should be resolved. If the preimage
|
||||
// field is set, the event indicates a settle event. If Preimage is nil, it is
|
||||
// a cancel event.
|
||||
type HtlcResolution struct {
|
||||
// Preimage is the htlc preimage. Its value is nil in case of a cancel.
|
||||
Preimage *lntypes.Preimage
|
||||
|
||||
@ -48,6 +48,33 @@ type HodlEvent struct {
|
||||
|
||||
// AcceptHeight is the original height at which the htlc was accepted.
|
||||
AcceptHeight int32
|
||||
|
||||
// Outcome indicates the outcome of the invoice registry update.
|
||||
Outcome ResolutionResult
|
||||
}
|
||||
|
||||
// NewFailureResolution returns a htlc failure resolution.
|
||||
func NewFailureResolution(key channeldb.CircuitKey,
|
||||
acceptHeight int32, outcome ResolutionResult) *HtlcResolution {
|
||||
|
||||
return &HtlcResolution{
|
||||
CircuitKey: key,
|
||||
AcceptHeight: acceptHeight,
|
||||
Outcome: outcome,
|
||||
}
|
||||
}
|
||||
|
||||
// NewSettleResolution returns a htlc resolution which is associated with a
|
||||
// settle.
|
||||
func NewSettleResolution(preimage lntypes.Preimage, key channeldb.CircuitKey,
|
||||
acceptHeight int32, outcome ResolutionResult) *HtlcResolution {
|
||||
|
||||
return &HtlcResolution{
|
||||
Preimage: &preimage,
|
||||
CircuitKey: key,
|
||||
AcceptHeight: acceptHeight,
|
||||
Outcome: outcome,
|
||||
}
|
||||
}
|
||||
|
||||
// RegistryConfig contains the configuration parameters for invoice registry.
|
||||
@ -325,7 +352,7 @@ func (i *InvoiceRegistry) invoiceEventLoop() {
|
||||
case <-nextReleaseTick:
|
||||
event := autoReleaseHeap.Pop().(*htlcReleaseEvent)
|
||||
err := i.cancelSingleHtlc(
|
||||
event.hash, event.key,
|
||||
event.hash, event.key, ResultMppTimeout,
|
||||
)
|
||||
if err != nil {
|
||||
log.Errorf("HTLC timer: %v", err)
|
||||
@ -574,9 +601,11 @@ func (i *InvoiceRegistry) startHtlcTimer(hash lntypes.Hash,
|
||||
}
|
||||
}
|
||||
|
||||
// cancelSingleHtlc cancels a single accepted htlc on an invoice.
|
||||
// cancelSingleHtlc cancels a single accepted htlc on an invoice. It takes
|
||||
// a resolution result which will be used to notify subscribed links and
|
||||
// resolvers of the details of the htlc cancellation.
|
||||
func (i *InvoiceRegistry) cancelSingleHtlc(hash lntypes.Hash,
|
||||
key channeldb.CircuitKey) error {
|
||||
key channeldb.CircuitKey, result ResolutionResult) error {
|
||||
|
||||
i.Lock()
|
||||
defer i.Unlock()
|
||||
@ -652,11 +681,11 @@ func (i *InvoiceRegistry) cancelSingleHtlc(hash lntypes.Hash,
|
||||
return fmt.Errorf("htlc %v not found", key)
|
||||
}
|
||||
if htlc.State == channeldb.HtlcStateCanceled {
|
||||
i.notifyHodlSubscribers(HodlEvent{
|
||||
CircuitKey: key,
|
||||
AcceptHeight: int32(htlc.AcceptHeight),
|
||||
Preimage: nil,
|
||||
})
|
||||
resolution := *NewFailureResolution(
|
||||
key, int32(htlc.AcceptHeight), result,
|
||||
)
|
||||
|
||||
i.notifyHodlSubscribers(resolution)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@ -679,7 +708,7 @@ func (i *InvoiceRegistry) cancelSingleHtlc(hash lntypes.Hash,
|
||||
func (i *InvoiceRegistry) NotifyExitHopHtlc(rHash lntypes.Hash,
|
||||
amtPaid lnwire.MilliSatoshi, expiry uint32, currentHeight int32,
|
||||
circuitKey channeldb.CircuitKey, hodlChan chan<- interface{},
|
||||
payload Payload) (*HodlEvent, error) {
|
||||
payload Payload) (*HtlcResolution, error) {
|
||||
|
||||
i.Lock()
|
||||
defer i.Unlock()
|
||||
@ -706,7 +735,7 @@ func (i *InvoiceRegistry) NotifyExitHopHtlc(rHash lntypes.Hash,
|
||||
// We'll attempt to settle an invoice matching this rHash on disk (if
|
||||
// one exists). The callback will update the invoice state and/or htlcs.
|
||||
var (
|
||||
result updateResult
|
||||
result ResolutionResult
|
||||
updateSubscribers bool
|
||||
)
|
||||
invoice, err := i.cdb.UpdateInvoice(
|
||||
@ -729,11 +758,21 @@ func (i *InvoiceRegistry) NotifyExitHopHtlc(rHash lntypes.Hash,
|
||||
return updateDesc, nil
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
debugLog(err.Error())
|
||||
switch err {
|
||||
case channeldb.ErrInvoiceNotFound:
|
||||
// If the invoice was not found, return a failure resolution
|
||||
// with an invoice not found result.
|
||||
return NewFailureResolution(
|
||||
circuitKey, currentHeight, ResultInvoiceNotFound,
|
||||
), nil
|
||||
|
||||
case nil:
|
||||
|
||||
default:
|
||||
debugLog(err.Error())
|
||||
return nil, err
|
||||
}
|
||||
|
||||
debugLog(result.String())
|
||||
|
||||
if updateSubscribers {
|
||||
@ -745,10 +784,9 @@ func (i *InvoiceRegistry) NotifyExitHopHtlc(rHash lntypes.Hash,
|
||||
|
||||
// If it isn't recorded, cancel htlc.
|
||||
if !ok {
|
||||
return &HodlEvent{
|
||||
CircuitKey: circuitKey,
|
||||
AcceptHeight: currentHeight,
|
||||
}, nil
|
||||
return NewFailureResolution(
|
||||
circuitKey, currentHeight, result,
|
||||
), nil
|
||||
}
|
||||
|
||||
// Determine accepted height of this htlc. If the htlc reached the
|
||||
@ -759,10 +797,9 @@ func (i *InvoiceRegistry) NotifyExitHopHtlc(rHash lntypes.Hash,
|
||||
|
||||
switch invoiceHtlc.State {
|
||||
case channeldb.HtlcStateCanceled:
|
||||
return &HodlEvent{
|
||||
CircuitKey: circuitKey,
|
||||
AcceptHeight: acceptHeight,
|
||||
}, nil
|
||||
return NewFailureResolution(
|
||||
circuitKey, acceptHeight, result,
|
||||
), nil
|
||||
|
||||
case channeldb.HtlcStateSettled:
|
||||
// Also settle any previously accepted htlcs. The invoice state
|
||||
@ -773,18 +810,24 @@ func (i *InvoiceRegistry) NotifyExitHopHtlc(rHash lntypes.Hash,
|
||||
continue
|
||||
}
|
||||
|
||||
i.notifyHodlSubscribers(HodlEvent{
|
||||
CircuitKey: key,
|
||||
Preimage: &invoice.Terms.PaymentPreimage,
|
||||
AcceptHeight: int32(htlc.AcceptHeight),
|
||||
})
|
||||
// Notify subscribers that the htlcs should be settled
|
||||
// with our peer. Note that the outcome of the
|
||||
// resolution is set based on the outcome of the single
|
||||
// htlc that we just settled, so may not be accurate
|
||||
// for all htlcs.
|
||||
resolution := *NewSettleResolution(
|
||||
invoice.Terms.PaymentPreimage, key,
|
||||
acceptHeight, result,
|
||||
)
|
||||
|
||||
i.notifyHodlSubscribers(resolution)
|
||||
}
|
||||
|
||||
return &HodlEvent{
|
||||
CircuitKey: circuitKey,
|
||||
Preimage: &invoice.Terms.PaymentPreimage,
|
||||
AcceptHeight: acceptHeight,
|
||||
}, nil
|
||||
resolution := NewSettleResolution(
|
||||
invoice.Terms.PaymentPreimage, circuitKey,
|
||||
acceptHeight, result,
|
||||
)
|
||||
return resolution, nil
|
||||
|
||||
case channeldb.HtlcStateAccepted:
|
||||
// (Re)start the htlc timer if the invoice is still open. It can
|
||||
@ -836,7 +879,9 @@ func (i *InvoiceRegistry) SettleHodlInvoice(preimage lntypes.Preimage) error {
|
||||
hash := preimage.Hash()
|
||||
invoice, err := i.cdb.UpdateInvoice(hash, updateInvoice)
|
||||
if err != nil {
|
||||
log.Errorf("SettleHodlInvoice with preimage %v: %v", preimage, err)
|
||||
log.Errorf("SettleHodlInvoice with preimage %v: %v",
|
||||
preimage, err)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
@ -854,11 +899,11 @@ func (i *InvoiceRegistry) SettleHodlInvoice(preimage lntypes.Preimage) error {
|
||||
continue
|
||||
}
|
||||
|
||||
i.notifyHodlSubscribers(HodlEvent{
|
||||
CircuitKey: key,
|
||||
Preimage: &preimage,
|
||||
AcceptHeight: int32(htlc.AcceptHeight),
|
||||
})
|
||||
resolution := *NewSettleResolution(
|
||||
preimage, key, int32(htlc.AcceptHeight), ResultSettled,
|
||||
)
|
||||
|
||||
i.notifyHodlSubscribers(resolution)
|
||||
}
|
||||
i.notifyClients(hash, invoice, invoice.State)
|
||||
|
||||
@ -873,7 +918,8 @@ func (i *InvoiceRegistry) CancelInvoice(payHash lntypes.Hash) error {
|
||||
|
||||
// cancelInvoice attempts to cancel the invoice corresponding to the passed
|
||||
// payment hash. Accepted invoices will only be canceled if explicitly
|
||||
// requested to do so.
|
||||
// requested to do so. It notifies subscribing links and resolvers that
|
||||
// the associated htlcs were canceled if they change state.
|
||||
func (i *InvoiceRegistry) cancelInvoiceImpl(payHash lntypes.Hash,
|
||||
cancelAccepted bool) error {
|
||||
|
||||
@ -932,10 +978,11 @@ func (i *InvoiceRegistry) cancelInvoiceImpl(payHash lntypes.Hash,
|
||||
continue
|
||||
}
|
||||
|
||||
i.notifyHodlSubscribers(HodlEvent{
|
||||
CircuitKey: key,
|
||||
AcceptHeight: int32(htlc.AcceptHeight),
|
||||
})
|
||||
i.notifyHodlSubscribers(
|
||||
*NewFailureResolution(
|
||||
key, int32(htlc.AcceptHeight), ResultCanceled,
|
||||
),
|
||||
)
|
||||
}
|
||||
i.notifyClients(payHash, invoice, channeldb.ContractCanceled)
|
||||
|
||||
@ -1201,9 +1248,10 @@ func (i *InvoiceRegistry) SubscribeSingleInvoice(
|
||||
return client, nil
|
||||
}
|
||||
|
||||
// notifyHodlSubscribers sends out the hodl event to all current subscribers.
|
||||
func (i *InvoiceRegistry) notifyHodlSubscribers(hodlEvent HodlEvent) {
|
||||
subscribers, ok := i.hodlSubscriptions[hodlEvent.CircuitKey]
|
||||
// notifyHodlSubscribers sends out the htlc resolution to all current
|
||||
// subscribers.
|
||||
func (i *InvoiceRegistry) notifyHodlSubscribers(htlcResolution HtlcResolution) {
|
||||
subscribers, ok := i.hodlSubscriptions[htlcResolution.CircuitKey]
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
@ -1213,18 +1261,18 @@ func (i *InvoiceRegistry) notifyHodlSubscribers(hodlEvent HodlEvent) {
|
||||
// single resolution for each hash.
|
||||
for subscriber := range subscribers {
|
||||
select {
|
||||
case subscriber <- hodlEvent:
|
||||
case subscriber <- htlcResolution:
|
||||
case <-i.quit:
|
||||
return
|
||||
}
|
||||
|
||||
delete(
|
||||
i.hodlReverseSubscriptions[subscriber],
|
||||
hodlEvent.CircuitKey,
|
||||
htlcResolution.CircuitKey,
|
||||
)
|
||||
}
|
||||
|
||||
delete(i.hodlSubscriptions, hodlEvent.CircuitKey)
|
||||
delete(i.hodlSubscriptions, htlcResolution.CircuitKey)
|
||||
}
|
||||
|
||||
// hodlSubscribe adds a new invoice subscription.
|
||||
|
@ -66,7 +66,7 @@ func TestSettleInvoice(t *testing.T) {
|
||||
hodlChan := make(chan interface{}, 1)
|
||||
|
||||
// Try to settle invoice with an htlc that expires too soon.
|
||||
event, err := ctx.registry.NotifyExitHopHtlc(
|
||||
resolution, err := ctx.registry.NotifyExitHopHtlc(
|
||||
testInvoicePaymentHash, testInvoice.Terms.Value,
|
||||
uint32(testCurrentHeight)+testInvoiceCltvDelta-1,
|
||||
testCurrentHeight, getCircuitKey(10), hodlChan, testPayload,
|
||||
@ -74,23 +74,30 @@ func TestSettleInvoice(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if event.Preimage != nil {
|
||||
t.Fatal("expected cancel event")
|
||||
if resolution.Preimage != nil {
|
||||
t.Fatal("expected cancel resolution")
|
||||
}
|
||||
if event.AcceptHeight != testCurrentHeight {
|
||||
if resolution.AcceptHeight != testCurrentHeight {
|
||||
t.Fatalf("expected acceptHeight %v, but got %v",
|
||||
testCurrentHeight, event.AcceptHeight)
|
||||
testCurrentHeight, resolution.AcceptHeight)
|
||||
}
|
||||
if resolution.Outcome != ResultExpiryTooSoon {
|
||||
t.Fatalf("expected expiry too soon, got: %v",
|
||||
resolution.Outcome)
|
||||
}
|
||||
|
||||
// Settle invoice with a slightly higher amount.
|
||||
amtPaid := lnwire.MilliSatoshi(100500)
|
||||
_, err = ctx.registry.NotifyExitHopHtlc(
|
||||
resolution, err = ctx.registry.NotifyExitHopHtlc(
|
||||
testInvoicePaymentHash, amtPaid, testHtlcExpiry, testCurrentHeight,
|
||||
getCircuitKey(0), hodlChan, testPayload,
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if resolution.Outcome != ResultSettled {
|
||||
t.Fatalf("expected settled, got: %v", resolution.Outcome)
|
||||
}
|
||||
|
||||
// We expect the settled state to be sent to the single invoice
|
||||
// subscriber.
|
||||
@ -120,42 +127,54 @@ func TestSettleInvoice(t *testing.T) {
|
||||
|
||||
// Try to settle again with the same htlc id. We need this idempotent
|
||||
// behaviour after a restart.
|
||||
event, err = ctx.registry.NotifyExitHopHtlc(
|
||||
resolution, err = ctx.registry.NotifyExitHopHtlc(
|
||||
testInvoicePaymentHash, amtPaid, testHtlcExpiry, testCurrentHeight,
|
||||
getCircuitKey(0), hodlChan, testPayload,
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected NotifyExitHopHtlc error: %v", err)
|
||||
}
|
||||
if event.Preimage == nil {
|
||||
t.Fatal("expected settle event")
|
||||
if resolution.Preimage == nil {
|
||||
t.Fatal("expected settle resolution")
|
||||
}
|
||||
if resolution.Outcome != ResultReplayToSettled {
|
||||
t.Fatalf("expected replay settled, got: %v",
|
||||
resolution.Outcome)
|
||||
}
|
||||
|
||||
// Try to settle again with a new higher-valued htlc. This payment
|
||||
// should also be accepted, to prevent any change in behaviour for a
|
||||
// paid invoice that may open up a probe vector.
|
||||
event, err = ctx.registry.NotifyExitHopHtlc(
|
||||
resolution, err = ctx.registry.NotifyExitHopHtlc(
|
||||
testInvoicePaymentHash, amtPaid+600, testHtlcExpiry, testCurrentHeight,
|
||||
getCircuitKey(1), hodlChan, testPayload,
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected NotifyExitHopHtlc error: %v", err)
|
||||
}
|
||||
if event.Preimage == nil {
|
||||
t.Fatal("expected settle event")
|
||||
if resolution.Preimage == nil {
|
||||
t.Fatal("expected settle resolution")
|
||||
}
|
||||
if resolution.Outcome != ResultDuplicateToSettled {
|
||||
t.Fatalf("expected duplicate settled, got: %v",
|
||||
resolution.Outcome)
|
||||
}
|
||||
|
||||
// Try to settle again with a lower amount. This should fail just as it
|
||||
// would have failed if it were the first payment.
|
||||
event, err = ctx.registry.NotifyExitHopHtlc(
|
||||
resolution, err = ctx.registry.NotifyExitHopHtlc(
|
||||
testInvoicePaymentHash, amtPaid-600, testHtlcExpiry, testCurrentHeight,
|
||||
getCircuitKey(2), hodlChan, testPayload,
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected NotifyExitHopHtlc error: %v", err)
|
||||
}
|
||||
if event.Preimage != nil {
|
||||
t.Fatal("expected cancel event")
|
||||
if resolution.Preimage != nil {
|
||||
t.Fatal("expected cancel resolution")
|
||||
}
|
||||
if resolution.Outcome != ResultAmountTooLow {
|
||||
t.Fatalf("expected amount too low, got: %v",
|
||||
resolution.Outcome)
|
||||
}
|
||||
|
||||
// Check that settled amount is equal to the sum of values of the htlcs
|
||||
@ -177,7 +196,7 @@ func TestSettleInvoice(t *testing.T) {
|
||||
// As this is a direct sette, we expect nothing on the hodl chan.
|
||||
select {
|
||||
case <-hodlChan:
|
||||
t.Fatal("unexpected event")
|
||||
t.Fatal("unexpected resolution")
|
||||
default:
|
||||
}
|
||||
}
|
||||
@ -270,9 +289,9 @@ func TestCancelInvoice(t *testing.T) {
|
||||
}
|
||||
|
||||
// Notify arrival of a new htlc paying to this invoice. This should
|
||||
// result in a cancel event.
|
||||
// result in a cancel resolution.
|
||||
hodlChan := make(chan interface{})
|
||||
event, err := ctx.registry.NotifyExitHopHtlc(
|
||||
resolution, err := ctx.registry.NotifyExitHopHtlc(
|
||||
testInvoicePaymentHash, amt, testHtlcExpiry, testCurrentHeight,
|
||||
getCircuitKey(0), hodlChan, testPayload,
|
||||
)
|
||||
@ -280,12 +299,16 @@ func TestCancelInvoice(t *testing.T) {
|
||||
t.Fatal("expected settlement of a canceled invoice to succeed")
|
||||
}
|
||||
|
||||
if event.Preimage != nil {
|
||||
t.Fatal("expected cancel hodl event")
|
||||
if resolution.Preimage != nil {
|
||||
t.Fatal("expected cancel htlc resolution")
|
||||
}
|
||||
if event.AcceptHeight != testCurrentHeight {
|
||||
if resolution.AcceptHeight != testCurrentHeight {
|
||||
t.Fatalf("expected acceptHeight %v, but got %v",
|
||||
testCurrentHeight, event.AcceptHeight)
|
||||
testCurrentHeight, resolution.AcceptHeight)
|
||||
}
|
||||
if resolution.Outcome != ResultInvoiceAlreadyCanceled {
|
||||
t.Fatalf("expected invoice already canceled, got: %v",
|
||||
resolution.Outcome)
|
||||
}
|
||||
}
|
||||
|
||||
@ -354,54 +377,58 @@ func TestSettleHoldInvoice(t *testing.T) {
|
||||
|
||||
// NotifyExitHopHtlc without a preimage present in the invoice registry
|
||||
// should be possible.
|
||||
event, err := registry.NotifyExitHopHtlc(
|
||||
resolution, err := registry.NotifyExitHopHtlc(
|
||||
testInvoicePaymentHash, amtPaid, testHtlcExpiry, testCurrentHeight,
|
||||
getCircuitKey(0), hodlChan, testPayload,
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("expected settle to succeed but got %v", err)
|
||||
}
|
||||
if event != nil {
|
||||
if resolution != nil {
|
||||
t.Fatalf("expected htlc to be held")
|
||||
}
|
||||
|
||||
// Test idempotency.
|
||||
event, err = registry.NotifyExitHopHtlc(
|
||||
resolution, err = registry.NotifyExitHopHtlc(
|
||||
testInvoicePaymentHash, amtPaid, testHtlcExpiry, testCurrentHeight,
|
||||
getCircuitKey(0), hodlChan, testPayload,
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("expected settle to succeed but got %v", err)
|
||||
}
|
||||
if event != nil {
|
||||
if resolution != nil {
|
||||
t.Fatalf("expected htlc to be held")
|
||||
}
|
||||
|
||||
// Test replay at a higher height. We expect the same result because it
|
||||
// is a replay.
|
||||
event, err = registry.NotifyExitHopHtlc(
|
||||
resolution, err = registry.NotifyExitHopHtlc(
|
||||
testInvoicePaymentHash, amtPaid, testHtlcExpiry, testCurrentHeight+10,
|
||||
getCircuitKey(0), hodlChan, testPayload,
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("expected settle to succeed but got %v", err)
|
||||
}
|
||||
if event != nil {
|
||||
if resolution != nil {
|
||||
t.Fatalf("expected htlc to be held")
|
||||
}
|
||||
|
||||
// Test a new htlc coming in that doesn't meet the final cltv delta
|
||||
// requirement. It should be rejected.
|
||||
event, err = registry.NotifyExitHopHtlc(
|
||||
resolution, err = registry.NotifyExitHopHtlc(
|
||||
testInvoicePaymentHash, amtPaid, 1, testCurrentHeight,
|
||||
getCircuitKey(1), hodlChan, testPayload,
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("expected settle to succeed but got %v", err)
|
||||
}
|
||||
if event == nil || event.Preimage != nil {
|
||||
if resolution == nil || resolution.Preimage != nil {
|
||||
t.Fatalf("expected htlc to be canceled")
|
||||
}
|
||||
if resolution.Outcome != ResultExpiryTooSoon {
|
||||
t.Fatalf("expected expiry too soon, got: %v",
|
||||
resolution.Outcome)
|
||||
}
|
||||
|
||||
// We expect the accepted state to be sent to the single invoice
|
||||
// subscriber. For all invoice subscribers, we don't expect an update.
|
||||
@ -421,13 +448,17 @@ func TestSettleHoldInvoice(t *testing.T) {
|
||||
t.Fatal("expected set preimage to succeed")
|
||||
}
|
||||
|
||||
hodlEvent := (<-hodlChan).(HodlEvent)
|
||||
if *hodlEvent.Preimage != testInvoicePreimage {
|
||||
t.Fatal("unexpected preimage in hodl event")
|
||||
htlcResolution := (<-hodlChan).(HtlcResolution)
|
||||
if *htlcResolution.Preimage != testInvoicePreimage {
|
||||
t.Fatal("unexpected preimage in hodl resolution")
|
||||
}
|
||||
if hodlEvent.AcceptHeight != testCurrentHeight {
|
||||
if htlcResolution.AcceptHeight != testCurrentHeight {
|
||||
t.Fatalf("expected acceptHeight %v, but got %v",
|
||||
testCurrentHeight, event.AcceptHeight)
|
||||
testCurrentHeight, resolution.AcceptHeight)
|
||||
}
|
||||
if htlcResolution.Outcome != ResultSettled {
|
||||
t.Fatalf("expected result settled, got: %v",
|
||||
htlcResolution.Outcome)
|
||||
}
|
||||
|
||||
// We expect a settled notification to be sent out for both all and
|
||||
@ -496,14 +527,14 @@ func TestCancelHoldInvoice(t *testing.T) {
|
||||
|
||||
// NotifyExitHopHtlc without a preimage present in the invoice registry
|
||||
// should be possible.
|
||||
event, err := registry.NotifyExitHopHtlc(
|
||||
resolution, err := registry.NotifyExitHopHtlc(
|
||||
testInvoicePaymentHash, amtPaid, testHtlcExpiry, testCurrentHeight,
|
||||
getCircuitKey(0), hodlChan, testPayload,
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("expected settle to succeed but got %v", err)
|
||||
}
|
||||
if event != nil {
|
||||
if resolution != nil {
|
||||
t.Fatalf("expected htlc to be held")
|
||||
}
|
||||
|
||||
@ -513,35 +544,39 @@ func TestCancelHoldInvoice(t *testing.T) {
|
||||
t.Fatal("cancel invoice failed")
|
||||
}
|
||||
|
||||
hodlEvent := (<-hodlChan).(HodlEvent)
|
||||
if hodlEvent.Preimage != nil {
|
||||
t.Fatal("expected cancel hodl event")
|
||||
htlcResolution := (<-hodlChan).(HtlcResolution)
|
||||
if htlcResolution.Preimage != nil {
|
||||
t.Fatal("expected cancel htlc resolution")
|
||||
}
|
||||
|
||||
// Offering the same htlc again at a higher height should still result
|
||||
// in a rejection. The accept height is expected to be the original
|
||||
// accept height.
|
||||
event, err = registry.NotifyExitHopHtlc(
|
||||
resolution, err = registry.NotifyExitHopHtlc(
|
||||
testInvoicePaymentHash, amtPaid, testHtlcExpiry, testCurrentHeight+1,
|
||||
getCircuitKey(0), hodlChan, testPayload,
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("expected settle to succeed but got %v", err)
|
||||
}
|
||||
if event.Preimage != nil {
|
||||
if resolution.Preimage != nil {
|
||||
t.Fatalf("expected htlc to be canceled")
|
||||
}
|
||||
if event.AcceptHeight != testCurrentHeight {
|
||||
if resolution.AcceptHeight != testCurrentHeight {
|
||||
t.Fatalf("expected acceptHeight %v, but got %v",
|
||||
testCurrentHeight, event.AcceptHeight)
|
||||
testCurrentHeight, resolution.AcceptHeight)
|
||||
}
|
||||
if resolution.Outcome != ResultReplayToCanceled {
|
||||
t.Fatalf("expected replay to canceled, got %v",
|
||||
resolution.Outcome)
|
||||
}
|
||||
}
|
||||
|
||||
// TestUnknownInvoice tests that invoice registry returns an error when the
|
||||
// invoice is unknown. This is to guard against returning a cancel hodl event
|
||||
// for forwarded htlcs. In the link, NotifyExitHopHtlc is only called if we are
|
||||
// the exit hop, but in htlcIncomingContestResolver it is called with forwarded
|
||||
// htlc hashes as well.
|
||||
// invoice is unknown. This is to guard against returning a cancel htlc
|
||||
// resolution for forwarded htlcs. In the link, NotifyExitHopHtlc is only called
|
||||
// if we are the exit hop, but in htlcIncomingContestResolver it is called with
|
||||
// forwarded htlc hashes as well.
|
||||
func TestUnknownInvoice(t *testing.T) {
|
||||
ctx := newTestContext(t)
|
||||
defer ctx.cleanup()
|
||||
@ -550,17 +585,23 @@ func TestUnknownInvoice(t *testing.T) {
|
||||
// succeed.
|
||||
hodlChan := make(chan interface{})
|
||||
amt := lnwire.MilliSatoshi(100000)
|
||||
_, err := ctx.registry.NotifyExitHopHtlc(
|
||||
result, err := ctx.registry.NotifyExitHopHtlc(
|
||||
testInvoicePaymentHash, amt, testHtlcExpiry, testCurrentHeight,
|
||||
getCircuitKey(0), hodlChan, testPayload,
|
||||
)
|
||||
if err != channeldb.ErrInvoiceNotFound {
|
||||
t.Fatal("expected invoice not found error")
|
||||
if err != nil {
|
||||
t.Fatal("unexpected error")
|
||||
}
|
||||
if result.Outcome != ResultInvoiceNotFound {
|
||||
t.Fatalf("expected ResultInvoiceNotFound, got: %v",
|
||||
result.Outcome)
|
||||
}
|
||||
}
|
||||
|
||||
// TestSettleMpp tests settling of an invoice with multiple partial payments.
|
||||
func TestSettleMpp(t *testing.T) {
|
||||
// TestMppPayment tests settling of an invoice with multiple partial payments.
|
||||
// It covers the case where there is a mpp timeout before the whole invoice is
|
||||
// paid and the case where the invoice is settled in time.
|
||||
func TestMppPayment(t *testing.T) {
|
||||
defer timeout()()
|
||||
|
||||
ctx := newTestContext(t)
|
||||
@ -578,7 +619,7 @@ func TestSettleMpp(t *testing.T) {
|
||||
|
||||
// Send htlc 1.
|
||||
hodlChan1 := make(chan interface{}, 1)
|
||||
event, err := ctx.registry.NotifyExitHopHtlc(
|
||||
resolution, err := ctx.registry.NotifyExitHopHtlc(
|
||||
testInvoicePaymentHash, testInvoice.Terms.Value/2,
|
||||
testHtlcExpiry,
|
||||
testCurrentHeight, getCircuitKey(10), hodlChan1, mppPayload,
|
||||
@ -586,21 +627,25 @@ func TestSettleMpp(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if event != nil {
|
||||
if resolution != nil {
|
||||
t.Fatal("expected no direct resolution")
|
||||
}
|
||||
|
||||
// Simulate mpp timeout releasing htlc 1.
|
||||
ctx.clock.SetTime(testTime.Add(30 * time.Second))
|
||||
|
||||
hodlEvent := (<-hodlChan1).(HodlEvent)
|
||||
if hodlEvent.Preimage != nil {
|
||||
t.Fatal("expected cancel event")
|
||||
htlcResolution := (<-hodlChan1).(HtlcResolution)
|
||||
if htlcResolution.Preimage != nil {
|
||||
t.Fatal("expected cancel resolution")
|
||||
}
|
||||
if htlcResolution.Outcome != ResultMppTimeout {
|
||||
t.Fatalf("expected mpp timeout, got: %v",
|
||||
htlcResolution.Outcome)
|
||||
}
|
||||
|
||||
// Send htlc 2.
|
||||
hodlChan2 := make(chan interface{}, 1)
|
||||
event, err = ctx.registry.NotifyExitHopHtlc(
|
||||
resolution, err = ctx.registry.NotifyExitHopHtlc(
|
||||
testInvoicePaymentHash, testInvoice.Terms.Value/2,
|
||||
testHtlcExpiry,
|
||||
testCurrentHeight, getCircuitKey(11), hodlChan2, mppPayload,
|
||||
@ -608,13 +653,13 @@ func TestSettleMpp(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if event != nil {
|
||||
if resolution != nil {
|
||||
t.Fatal("expected no direct resolution")
|
||||
}
|
||||
|
||||
// Send htlc 3.
|
||||
hodlChan3 := make(chan interface{}, 1)
|
||||
event, err = ctx.registry.NotifyExitHopHtlc(
|
||||
resolution, err = ctx.registry.NotifyExitHopHtlc(
|
||||
testInvoicePaymentHash, testInvoice.Terms.Value/2,
|
||||
testHtlcExpiry,
|
||||
testCurrentHeight, getCircuitKey(12), hodlChan3, mppPayload,
|
||||
@ -622,12 +667,16 @@ func TestSettleMpp(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if event == nil {
|
||||
t.Fatal("expected a settle event")
|
||||
if resolution == nil {
|
||||
t.Fatal("expected a settle resolution")
|
||||
}
|
||||
if resolution.Outcome != ResultSettled {
|
||||
t.Fatalf("expected result settled, got: %v",
|
||||
resolution.Outcome)
|
||||
}
|
||||
|
||||
// Check that settled amount is equal to the sum of values of the htlcs
|
||||
// 0 and 1.
|
||||
// 2 and 3.
|
||||
inv, err := ctx.registry.LookupInvoice(testInvoicePaymentHash)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
|
@ -8,86 +8,141 @@ import (
|
||||
"github.com/lightningnetwork/lnd/record"
|
||||
)
|
||||
|
||||
// updateResult is the result of the invoice update call.
|
||||
type updateResult uint8
|
||||
// ResolutionResult provides metadata which about an invoice update which can
|
||||
// be used to take custom actions on resolution of the htlc. Only results which
|
||||
// are actionable by the link are exported.
|
||||
type ResolutionResult uint8
|
||||
|
||||
const (
|
||||
resultInvalid updateResult = iota
|
||||
resultReplayToCanceled
|
||||
resultReplayToAccepted
|
||||
resultReplayToSettled
|
||||
resultInvoiceAlreadyCanceled
|
||||
resultAmountTooLow
|
||||
resultExpiryTooSoon
|
||||
resultDuplicateToAccepted
|
||||
resultDuplicateToSettled
|
||||
resultAccepted
|
||||
resultSettled
|
||||
resultInvoiceNotOpen
|
||||
resultPartialAccepted
|
||||
resultMppInProgress
|
||||
resultAddressMismatch
|
||||
resultHtlcSetTotalMismatch
|
||||
resultHtlcSetTotalTooLow
|
||||
resultHtlcSetOverpayment
|
||||
resultInvalid ResolutionResult = iota
|
||||
|
||||
// ResultReplayToCanceled is returned when we replay a canceled invoice.
|
||||
ResultReplayToCanceled
|
||||
|
||||
// ResultReplayToAccepted is returned when we replay an accepted invoice.
|
||||
ResultReplayToAccepted
|
||||
|
||||
// ResultReplayToSettled is returned when we replay a settled invoice.
|
||||
ResultReplayToSettled
|
||||
|
||||
// ResultInvoiceAlreadyCanceled is returned when trying to pay an invoice
|
||||
// that is already canceled.
|
||||
ResultInvoiceAlreadyCanceled
|
||||
|
||||
// ResultAmountTooLow is returned when an invoice is underpaid.
|
||||
ResultAmountTooLow
|
||||
|
||||
// ResultExpiryTooSoon is returned when we do not accept an invoice payment
|
||||
// because it expires too soon.
|
||||
ResultExpiryTooSoon
|
||||
|
||||
// ResultDuplicateToAccepted is returned when we accept a duplicate htlc.
|
||||
ResultDuplicateToAccepted
|
||||
|
||||
// ResultDuplicateToSettled is returned when we settle an invoice which has
|
||||
// already been settled at least once.
|
||||
ResultDuplicateToSettled
|
||||
|
||||
// ResultAccepted is returned when we accept a hodl invoice.
|
||||
ResultAccepted
|
||||
|
||||
// ResultSettled is returned when we settle an invoice.
|
||||
ResultSettled
|
||||
|
||||
// ResultCanceled is returned when we cancel an invoice and its associated
|
||||
// htlcs.
|
||||
ResultCanceled
|
||||
|
||||
// ResultInvoiceNotOpen is returned when a mpp invoice is not open.
|
||||
ResultInvoiceNotOpen
|
||||
|
||||
// ResultPartialAccepted is returned when we have partially received
|
||||
// payment.
|
||||
ResultPartialAccepted
|
||||
|
||||
// ResultMppInProgress is returned when we are busy receiving a mpp payment.
|
||||
ResultMppInProgress
|
||||
|
||||
// ResultMppTimeout is returned when an invoice paid with multiple partial
|
||||
// payments times out before it is fully paid.
|
||||
ResultMppTimeout
|
||||
|
||||
// ResultAddressMismatch is returned when the payment address for a mpp
|
||||
// invoice does not match.
|
||||
ResultAddressMismatch
|
||||
|
||||
// ResultHtlcSetTotalMismatch is returned when the amount paid by a htlc
|
||||
// does not match its set total.
|
||||
ResultHtlcSetTotalMismatch
|
||||
|
||||
// ResultHtlcSetTotalTooLow is returned when a mpp set total is too low for
|
||||
// an invoice.
|
||||
ResultHtlcSetTotalTooLow
|
||||
|
||||
// ResultHtlcSetOverpayment is returned when a mpp set is overpaid.
|
||||
ResultHtlcSetOverpayment
|
||||
|
||||
// ResultInvoiceNotFound is returned when an attempt is made to pay an
|
||||
// invoice that is unknown to us.
|
||||
ResultInvoiceNotFound
|
||||
)
|
||||
|
||||
// String returns a human-readable representation of the invoice update result.
|
||||
func (u updateResult) String() string {
|
||||
func (u ResolutionResult) String() string {
|
||||
switch u {
|
||||
|
||||
case resultInvalid:
|
||||
return "invalid"
|
||||
|
||||
case resultReplayToCanceled:
|
||||
case ResultReplayToCanceled:
|
||||
return "replayed htlc to canceled invoice"
|
||||
|
||||
case resultReplayToAccepted:
|
||||
case ResultReplayToAccepted:
|
||||
return "replayed htlc to accepted invoice"
|
||||
|
||||
case resultReplayToSettled:
|
||||
case ResultReplayToSettled:
|
||||
return "replayed htlc to settled invoice"
|
||||
|
||||
case resultInvoiceAlreadyCanceled:
|
||||
case ResultInvoiceAlreadyCanceled:
|
||||
return "invoice already canceled"
|
||||
|
||||
case resultAmountTooLow:
|
||||
case ResultAmountTooLow:
|
||||
return "amount too low"
|
||||
|
||||
case resultExpiryTooSoon:
|
||||
case ResultExpiryTooSoon:
|
||||
return "expiry too soon"
|
||||
|
||||
case resultDuplicateToAccepted:
|
||||
case ResultDuplicateToAccepted:
|
||||
return "accepting duplicate payment to accepted invoice"
|
||||
|
||||
case resultDuplicateToSettled:
|
||||
case ResultDuplicateToSettled:
|
||||
return "accepting duplicate payment to settled invoice"
|
||||
|
||||
case resultAccepted:
|
||||
case ResultAccepted:
|
||||
return "accepted"
|
||||
|
||||
case resultSettled:
|
||||
case ResultSettled:
|
||||
return "settled"
|
||||
|
||||
case resultInvoiceNotOpen:
|
||||
case ResultInvoiceNotOpen:
|
||||
return "invoice no longer open"
|
||||
|
||||
case resultPartialAccepted:
|
||||
case ResultPartialAccepted:
|
||||
return "partial payment accepted"
|
||||
|
||||
case resultMppInProgress:
|
||||
case ResultMppInProgress:
|
||||
return "mpp reception in progress"
|
||||
|
||||
case resultAddressMismatch:
|
||||
case ResultAddressMismatch:
|
||||
return "payment address mismatch"
|
||||
|
||||
case resultHtlcSetTotalMismatch:
|
||||
case ResultHtlcSetTotalMismatch:
|
||||
return "htlc total amt doesn't match set total"
|
||||
|
||||
case resultHtlcSetTotalTooLow:
|
||||
case ResultHtlcSetTotalTooLow:
|
||||
return "set total too low for invoice"
|
||||
|
||||
case resultHtlcSetOverpayment:
|
||||
case ResultHtlcSetOverpayment:
|
||||
return "mpp is overpaying set total"
|
||||
|
||||
default:
|
||||
@ -110,20 +165,20 @@ type invoiceUpdateCtx struct {
|
||||
// updateInvoice is a callback for DB.UpdateInvoice that contains the invoice
|
||||
// settlement logic.
|
||||
func updateInvoice(ctx *invoiceUpdateCtx, inv *channeldb.Invoice) (
|
||||
*channeldb.InvoiceUpdateDesc, updateResult, error) {
|
||||
*channeldb.InvoiceUpdateDesc, ResolutionResult, error) {
|
||||
|
||||
// Don't update the invoice when this is a replayed htlc.
|
||||
htlc, ok := inv.Htlcs[ctx.circuitKey]
|
||||
if ok {
|
||||
switch htlc.State {
|
||||
case channeldb.HtlcStateCanceled:
|
||||
return nil, resultReplayToCanceled, nil
|
||||
return nil, ResultReplayToCanceled, nil
|
||||
|
||||
case channeldb.HtlcStateAccepted:
|
||||
return nil, resultReplayToAccepted, nil
|
||||
return nil, ResultReplayToAccepted, nil
|
||||
|
||||
case channeldb.HtlcStateSettled:
|
||||
return nil, resultReplayToSettled, nil
|
||||
return nil, ResultReplayToSettled, nil
|
||||
|
||||
default:
|
||||
return nil, 0, errors.New("unknown htlc state")
|
||||
@ -140,7 +195,7 @@ func updateInvoice(ctx *invoiceUpdateCtx, inv *channeldb.Invoice) (
|
||||
// updateMpp is a callback for DB.UpdateInvoice that contains the invoice
|
||||
// settlement logic for mpp payments.
|
||||
func updateMpp(ctx *invoiceUpdateCtx, inv *channeldb.Invoice) (
|
||||
*channeldb.InvoiceUpdateDesc, updateResult, error) {
|
||||
*channeldb.InvoiceUpdateDesc, ResolutionResult, error) {
|
||||
|
||||
// Start building the accept descriptor.
|
||||
acceptDesc := &channeldb.HtlcAcceptDesc{
|
||||
@ -156,23 +211,23 @@ func updateMpp(ctx *invoiceUpdateCtx, inv *channeldb.Invoice) (
|
||||
// Because non-mpp payments don't have a payment address, this is needed
|
||||
// to thwart probing.
|
||||
if inv.State != channeldb.ContractOpen {
|
||||
return nil, resultInvoiceNotOpen, nil
|
||||
return nil, ResultInvoiceNotOpen, nil
|
||||
}
|
||||
|
||||
// Check the payment address that authorizes the payment.
|
||||
if ctx.mpp.PaymentAddr() != inv.Terms.PaymentAddr {
|
||||
return nil, resultAddressMismatch, nil
|
||||
return nil, ResultAddressMismatch, nil
|
||||
}
|
||||
|
||||
// Don't accept zero-valued sets.
|
||||
if ctx.mpp.TotalMsat() == 0 {
|
||||
return nil, resultHtlcSetTotalTooLow, nil
|
||||
return nil, ResultHtlcSetTotalTooLow, nil
|
||||
}
|
||||
|
||||
// Check that the total amt of the htlc set is high enough. In case this
|
||||
// is a zero-valued invoice, it will always be enough.
|
||||
if ctx.mpp.TotalMsat() < inv.Terms.Value {
|
||||
return nil, resultHtlcSetTotalTooLow, nil
|
||||
return nil, ResultHtlcSetTotalTooLow, nil
|
||||
}
|
||||
|
||||
// Check whether total amt matches other htlcs in the set.
|
||||
@ -186,7 +241,7 @@ func updateMpp(ctx *invoiceUpdateCtx, inv *channeldb.Invoice) (
|
||||
}
|
||||
|
||||
if ctx.mpp.TotalMsat() != htlc.MppTotalAmt {
|
||||
return nil, resultHtlcSetTotalMismatch, nil
|
||||
return nil, ResultHtlcSetTotalMismatch, nil
|
||||
}
|
||||
|
||||
newSetTotal += htlc.Amt
|
||||
@ -197,16 +252,16 @@ func updateMpp(ctx *invoiceUpdateCtx, inv *channeldb.Invoice) (
|
||||
|
||||
// Make sure the communicated set total isn't overpaid.
|
||||
if newSetTotal > ctx.mpp.TotalMsat() {
|
||||
return nil, resultHtlcSetOverpayment, nil
|
||||
return nil, ResultHtlcSetOverpayment, nil
|
||||
}
|
||||
|
||||
// The invoice is still open. Check the expiry.
|
||||
if ctx.expiry < uint32(ctx.currentHeight+ctx.finalCltvRejectDelta) {
|
||||
return nil, resultExpiryTooSoon, nil
|
||||
return nil, ResultExpiryTooSoon, nil
|
||||
}
|
||||
|
||||
if ctx.expiry < uint32(ctx.currentHeight+inv.Terms.FinalCltvDelta) {
|
||||
return nil, resultExpiryTooSoon, nil
|
||||
return nil, ResultExpiryTooSoon, nil
|
||||
}
|
||||
|
||||
// Record HTLC in the invoice database.
|
||||
@ -221,7 +276,7 @@ func updateMpp(ctx *invoiceUpdateCtx, inv *channeldb.Invoice) (
|
||||
// If the invoice cannot be settled yet, only record the htlc.
|
||||
setComplete := newSetTotal == ctx.mpp.TotalMsat()
|
||||
if !setComplete {
|
||||
return &update, resultPartialAccepted, nil
|
||||
return &update, ResultPartialAccepted, nil
|
||||
}
|
||||
|
||||
// Check to see if we can settle or this is an hold invoice and
|
||||
@ -231,7 +286,7 @@ func updateMpp(ctx *invoiceUpdateCtx, inv *channeldb.Invoice) (
|
||||
update.State = &channeldb.InvoiceStateUpdateDesc{
|
||||
NewState: channeldb.ContractAccepted,
|
||||
}
|
||||
return &update, resultAccepted, nil
|
||||
return &update, ResultAccepted, nil
|
||||
}
|
||||
|
||||
update.State = &channeldb.InvoiceStateUpdateDesc{
|
||||
@ -239,18 +294,18 @@ func updateMpp(ctx *invoiceUpdateCtx, inv *channeldb.Invoice) (
|
||||
Preimage: inv.Terms.PaymentPreimage,
|
||||
}
|
||||
|
||||
return &update, resultSettled, nil
|
||||
return &update, ResultSettled, nil
|
||||
}
|
||||
|
||||
// updateLegacy is a callback for DB.UpdateInvoice that contains the invoice
|
||||
// settlement logic for legacy payments.
|
||||
func updateLegacy(ctx *invoiceUpdateCtx, inv *channeldb.Invoice) (
|
||||
*channeldb.InvoiceUpdateDesc, updateResult, error) {
|
||||
*channeldb.InvoiceUpdateDesc, ResolutionResult, error) {
|
||||
|
||||
// If the invoice is already canceled, there is no further
|
||||
// checking to do.
|
||||
if inv.State == channeldb.ContractCanceled {
|
||||
return nil, resultInvoiceAlreadyCanceled, nil
|
||||
return nil, ResultInvoiceAlreadyCanceled, nil
|
||||
}
|
||||
|
||||
// If an invoice amount is specified, check that enough is paid. Also
|
||||
@ -258,7 +313,7 @@ func updateLegacy(ctx *invoiceUpdateCtx, inv *channeldb.Invoice) (
|
||||
// or accepted. In case this is a zero-valued invoice, it will always be
|
||||
// enough.
|
||||
if ctx.amtPaid < inv.Terms.Value {
|
||||
return nil, resultAmountTooLow, nil
|
||||
return nil, ResultAmountTooLow, nil
|
||||
}
|
||||
|
||||
// TODO(joostjager): Check invoice mpp required feature
|
||||
@ -271,17 +326,17 @@ func updateLegacy(ctx *invoiceUpdateCtx, inv *channeldb.Invoice) (
|
||||
if htlc.State == channeldb.HtlcStateAccepted &&
|
||||
htlc.MppTotalAmt > 0 {
|
||||
|
||||
return nil, resultMppInProgress, nil
|
||||
return nil, ResultMppInProgress, nil
|
||||
}
|
||||
}
|
||||
|
||||
// The invoice is still open. Check the expiry.
|
||||
if ctx.expiry < uint32(ctx.currentHeight+ctx.finalCltvRejectDelta) {
|
||||
return nil, resultExpiryTooSoon, nil
|
||||
return nil, ResultExpiryTooSoon, nil
|
||||
}
|
||||
|
||||
if ctx.expiry < uint32(ctx.currentHeight+inv.Terms.FinalCltvDelta) {
|
||||
return nil, resultExpiryTooSoon, nil
|
||||
return nil, ResultExpiryTooSoon, nil
|
||||
}
|
||||
|
||||
// Record HTLC in the invoice database.
|
||||
@ -302,10 +357,10 @@ func updateLegacy(ctx *invoiceUpdateCtx, inv *channeldb.Invoice) (
|
||||
// We do accept or settle the HTLC.
|
||||
switch inv.State {
|
||||
case channeldb.ContractAccepted:
|
||||
return &update, resultDuplicateToAccepted, nil
|
||||
return &update, ResultDuplicateToAccepted, nil
|
||||
|
||||
case channeldb.ContractSettled:
|
||||
return &update, resultDuplicateToSettled, nil
|
||||
return &update, ResultDuplicateToSettled, nil
|
||||
}
|
||||
|
||||
// Check to see if we can settle or this is an hold invoice and we need
|
||||
@ -315,7 +370,7 @@ func updateLegacy(ctx *invoiceUpdateCtx, inv *channeldb.Invoice) (
|
||||
update.State = &channeldb.InvoiceStateUpdateDesc{
|
||||
NewState: channeldb.ContractAccepted,
|
||||
}
|
||||
return &update, resultAccepted, nil
|
||||
return &update, ResultAccepted, nil
|
||||
}
|
||||
|
||||
update.State = &channeldb.InvoiceStateUpdateDesc{
|
||||
@ -323,5 +378,5 @@ func updateLegacy(ctx *invoiceUpdateCtx, inv *channeldb.Invoice) (
|
||||
Preimage: inv.Terms.PaymentPreimage,
|
||||
}
|
||||
|
||||
return &update, resultSettled, nil
|
||||
return &update, ResultSettled, nil
|
||||
}
|
||||
|
@ -109,6 +109,7 @@ const (
|
||||
Failure_PERMANENT_NODE_FAILURE Failure_FailureCode = 20
|
||||
Failure_PERMANENT_CHANNEL_FAILURE Failure_FailureCode = 21
|
||||
Failure_EXPIRY_TOO_FAR Failure_FailureCode = 22
|
||||
Failure_MPP_TIMEOUT Failure_FailureCode = 23
|
||||
//*
|
||||
//The error source is known, but the failure itself couldn't be decoded.
|
||||
Failure_UNKNOWN_FAILURE Failure_FailureCode = 998
|
||||
@ -142,6 +143,7 @@ var Failure_FailureCode_name = map[int32]string{
|
||||
20: "PERMANENT_NODE_FAILURE",
|
||||
21: "PERMANENT_CHANNEL_FAILURE",
|
||||
22: "EXPIRY_TOO_FAR",
|
||||
23: "MPP_TIMEOUT",
|
||||
998: "UNKNOWN_FAILURE",
|
||||
999: "UNREADABLE_FAILURE",
|
||||
}
|
||||
@ -170,6 +172,7 @@ var Failure_FailureCode_value = map[string]int32{
|
||||
"PERMANENT_NODE_FAILURE": 20,
|
||||
"PERMANENT_CHANNEL_FAILURE": 21,
|
||||
"EXPIRY_TOO_FAR": 22,
|
||||
"MPP_TIMEOUT": 23,
|
||||
"UNKNOWN_FAILURE": 998,
|
||||
"UNREADABLE_FAILURE": 999,
|
||||
}
|
||||
@ -1526,138 +1529,139 @@ func init() {
|
||||
func init() { proto.RegisterFile("routerrpc/router.proto", fileDescriptor_7a0613f69d37b0a5) }
|
||||
|
||||
var fileDescriptor_7a0613f69d37b0a5 = []byte{
|
||||
// 2093 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x58, 0x4f, 0x73, 0xdb, 0xb8,
|
||||
0x15, 0x5f, 0x5a, 0xff, 0x9f, 0xfe, 0xd1, 0xb0, 0xe3, 0x30, 0x72, 0xbc, 0xf1, 0x32, 0x69, 0x56,
|
||||
0x93, 0xc9, 0xda, 0xa9, 0xdb, 0xcd, 0x64, 0x7a, 0x68, 0x47, 0x96, 0xa8, 0x35, 0x1d, 0x89, 0x72,
|
||||
0x20, 0x29, 0xbb, 0xe9, 0x1e, 0x30, 0xb4, 0x04, 0x5b, 0x1c, 0x53, 0xa4, 0x96, 0x84, 0xb2, 0xf1,
|
||||
0x77, 0xe8, 0xf7, 0x68, 0x0f, 0x6d, 0x2f, 0xfd, 0x4e, 0xed, 0xbd, 0x33, 0x3d, 0xf4, 0xd6, 0x01,
|
||||
0x40, 0x4a, 0x94, 0x2c, 0x67, 0x7b, 0xb2, 0xf0, 0x7b, 0x3f, 0xbc, 0x07, 0xe2, 0xe1, 0xfd, 0xf0,
|
||||
0x60, 0xd8, 0x0b, 0xfc, 0x39, 0xa3, 0x41, 0x30, 0x1b, 0x1d, 0xcb, 0x5f, 0x47, 0xb3, 0xc0, 0x67,
|
||||
0x3e, 0x2a, 0x2c, 0xf0, 0x5a, 0x21, 0x98, 0x8d, 0x24, 0xaa, 0xff, 0x37, 0x03, 0xa8, 0x4f, 0xbd,
|
||||
0xf1, 0x85, 0x7d, 0x3b, 0xa5, 0x1e, 0xc3, 0xf4, 0xa7, 0x39, 0x0d, 0x19, 0x42, 0x90, 0x1e, 0xd3,
|
||||
0x90, 0x69, 0xca, 0xa1, 0x52, 0x2f, 0x61, 0xf1, 0x1b, 0xa9, 0x90, 0xb2, 0xa7, 0x4c, 0xdb, 0x3a,
|
||||
0x54, 0xea, 0x29, 0xcc, 0x7f, 0xa2, 0x47, 0x90, 0xb7, 0xa7, 0x8c, 0x4c, 0x43, 0x9b, 0x69, 0x25,
|
||||
0x01, 0xe7, 0xec, 0x29, 0xeb, 0x86, 0x36, 0x43, 0x5f, 0x41, 0x69, 0x26, 0x5d, 0x92, 0x89, 0x1d,
|
||||
0x4e, 0xb4, 0x94, 0x70, 0x54, 0x8c, 0xb0, 0x33, 0x3b, 0x9c, 0xa0, 0x3a, 0xa8, 0x57, 0x8e, 0x67,
|
||||
0xbb, 0x64, 0xe4, 0xb2, 0x8f, 0x64, 0x4c, 0x5d, 0x66, 0x6b, 0xe9, 0x43, 0xa5, 0x9e, 0xc1, 0x15,
|
||||
0x81, 0x37, 0x5d, 0xf6, 0xb1, 0xc5, 0x51, 0xf4, 0x35, 0x54, 0x63, 0x67, 0x81, 0x5c, 0xa0, 0x96,
|
||||
0x39, 0x54, 0xea, 0x05, 0x5c, 0x99, 0xad, 0x2e, 0xfb, 0x6b, 0xa8, 0x32, 0x67, 0x4a, 0xfd, 0x39,
|
||||
0x23, 0x21, 0x1d, 0xf9, 0xde, 0x38, 0xd4, 0xb2, 0xd2, 0x63, 0x04, 0xf7, 0x25, 0x8a, 0x74, 0x28,
|
||||
0x5f, 0x51, 0x4a, 0x5c, 0x67, 0xea, 0x30, 0xc2, 0x97, 0x9f, 0x13, 0xcb, 0x2f, 0x5e, 0x51, 0xda,
|
||||
0xe1, 0x58, 0xdf, 0x66, 0xe8, 0x19, 0x54, 0x96, 0x1c, 0xf1, 0x8d, 0x65, 0x41, 0x2a, 0xc5, 0x24,
|
||||
0xf1, 0xa1, 0x2f, 0x41, 0xf5, 0xe7, 0xec, 0xda, 0x77, 0xbc, 0x6b, 0x32, 0x9a, 0xd8, 0x1e, 0x71,
|
||||
0xc6, 0x5a, 0xfe, 0x50, 0xa9, 0xa7, 0x4f, 0xb7, 0x5e, 0x29, 0xb8, 0x12, 0xdb, 0x9a, 0x13, 0xdb,
|
||||
0x33, 0xc7, 0xe8, 0x39, 0x54, 0x5d, 0x3b, 0x64, 0x64, 0xe2, 0xcf, 0xc8, 0x6c, 0x7e, 0x79, 0x43,
|
||||
0x6f, 0xb5, 0x8a, 0xd8, 0x99, 0x32, 0x87, 0xcf, 0xfc, 0xd9, 0x85, 0x00, 0xd1, 0x01, 0x80, 0xd8,
|
||||
0x15, 0x11, 0x5c, 0x2b, 0x88, 0x6f, 0x28, 0x70, 0x44, 0x04, 0x46, 0x27, 0x50, 0x14, 0xd9, 0x24,
|
||||
0x13, 0xc7, 0x63, 0xa1, 0x06, 0x87, 0xa9, 0x7a, 0xf1, 0x44, 0x3d, 0x72, 0x3d, 0x9e, 0x58, 0xcc,
|
||||
0x2d, 0x67, 0x8e, 0xc7, 0x70, 0x92, 0x84, 0xc6, 0xb0, 0xc3, 0xd3, 0x48, 0x46, 0xf3, 0x90, 0xf9,
|
||||
0x53, 0x12, 0xd0, 0x91, 0x1f, 0x8c, 0x43, 0xad, 0x28, 0xe6, 0xfe, 0xf6, 0x68, 0x71, 0x3a, 0x8e,
|
||||
0xee, 0x1e, 0x87, 0xa3, 0x16, 0x0d, 0x59, 0x53, 0xcc, 0xc3, 0x72, 0x9a, 0xe1, 0xb1, 0xe0, 0x16,
|
||||
0x6f, 0x8f, 0xd7, 0x71, 0xf4, 0x12, 0x90, 0xed, 0xba, 0xfe, 0xcf, 0x24, 0xa4, 0xee, 0x15, 0x89,
|
||||
0xd2, 0xa3, 0x55, 0x0f, 0x95, 0x7a, 0x1e, 0xab, 0xc2, 0xd2, 0xa7, 0xee, 0x55, 0xe4, 0x1e, 0xbd,
|
||||
0x86, 0xb2, 0x58, 0xd3, 0x15, 0xb5, 0xd9, 0x3c, 0xa0, 0xa1, 0xa6, 0x1e, 0xa6, 0xea, 0x95, 0x93,
|
||||
0xed, 0xe8, 0x4b, 0xda, 0x12, 0x3e, 0x75, 0x18, 0x2e, 0x71, 0x5e, 0x34, 0x0e, 0x6b, 0x2d, 0xd8,
|
||||
0xdb, 0xbc, 0x24, 0x7e, 0x48, 0xf9, 0xa6, 0xf2, 0x73, 0x9b, 0xc6, 0xfc, 0x27, 0xda, 0x85, 0xcc,
|
||||
0x47, 0xdb, 0x9d, 0x53, 0x71, 0x70, 0x4b, 0x58, 0x0e, 0x7e, 0xb7, 0xf5, 0x46, 0xd1, 0xdf, 0xc0,
|
||||
0xce, 0x20, 0xb0, 0x47, 0x37, 0x6b, 0x67, 0x7f, 0xfd, 0xe8, 0x2a, 0x77, 0x8e, 0xae, 0xfe, 0x17,
|
||||
0x05, 0xca, 0xd1, 0xac, 0x3e, 0xb3, 0xd9, 0x3c, 0x44, 0xdf, 0x40, 0x26, 0x64, 0x36, 0xa3, 0x82,
|
||||
0x5d, 0x39, 0x79, 0x98, 0xd8, 0xcf, 0x04, 0x91, 0x62, 0xc9, 0x42, 0x35, 0xc8, 0xcf, 0x02, 0xea,
|
||||
0x4c, 0xed, 0xeb, 0x78, 0x5d, 0x8b, 0x31, 0xd2, 0x21, 0x23, 0x26, 0x8b, 0x9a, 0x29, 0x9e, 0x94,
|
||||
0x92, 0x69, 0xc5, 0xd2, 0x84, 0xea, 0x90, 0x99, 0x30, 0x77, 0x14, 0x6a, 0x69, 0x91, 0x3e, 0x14,
|
||||
0x71, 0xce, 0x06, 0x9d, 0x66, 0x83, 0x31, 0x3a, 0x9d, 0x31, 0x2c, 0x09, 0xfa, 0xef, 0xa1, 0x2a,
|
||||
0x66, 0xb6, 0x29, 0xfd, 0x5c, 0x71, 0x3f, 0x04, 0x5e, 0xba, 0xa2, 0x14, 0x64, 0x81, 0x67, 0xed,
|
||||
0x29, 0xaf, 0x02, 0x7d, 0x0c, 0xea, 0x72, 0x7e, 0x38, 0xf3, 0xbd, 0x90, 0x47, 0x57, 0xf9, 0x32,
|
||||
0xf8, 0x91, 0xe7, 0x15, 0x22, 0x6a, 0x43, 0x11, 0xb3, 0x2a, 0x11, 0xde, 0xa6, 0x54, 0x54, 0xc7,
|
||||
0x73, 0x59, 0x90, 0xc4, 0xf5, 0x47, 0x37, 0xbc, 0xc4, 0xed, 0xdb, 0xc8, 0x7d, 0x99, 0xc3, 0x1d,
|
||||
0x7f, 0x74, 0xd3, 0xe2, 0xa0, 0xfe, 0xa3, 0x54, 0xa1, 0x81, 0x2f, 0xbf, 0xf2, 0xff, 0xce, 0xc4,
|
||||
0x72, 0xb3, 0xb6, 0xee, 0xdd, 0x2c, 0x9d, 0xc0, 0xce, 0x8a, 0xf3, 0xe8, 0x2b, 0x92, 0x39, 0x50,
|
||||
0xd6, 0x72, 0xf0, 0x12, 0x72, 0x57, 0xb6, 0xe3, 0xce, 0x83, 0xd8, 0x31, 0x4a, 0x24, 0xb4, 0x2d,
|
||||
0x2d, 0x38, 0xa6, 0xe8, 0xff, 0xc9, 0x41, 0x2e, 0x02, 0xd1, 0x09, 0xa4, 0x47, 0xfe, 0x38, 0x3e,
|
||||
0x07, 0x5f, 0xde, 0x9d, 0x16, 0xff, 0x6d, 0xfa, 0x63, 0x8a, 0x05, 0x17, 0xfd, 0x01, 0x2a, 0x5c,
|
||||
0x3a, 0x3c, 0xea, 0x92, 0xf9, 0x6c, 0x6c, 0x2f, 0x52, 0xaf, 0x25, 0x66, 0x37, 0x25, 0x61, 0x28,
|
||||
0xec, 0xb8, 0x3c, 0x4a, 0x0e, 0xd1, 0x3e, 0x14, 0x78, 0xb6, 0x65, 0x26, 0xd2, 0xe2, 0xec, 0xe7,
|
||||
0x39, 0x20, 0x72, 0xa0, 0x43, 0xd9, 0xf7, 0x1c, 0xdf, 0x23, 0xe1, 0xc4, 0x26, 0x27, 0xdf, 0xbe,
|
||||
0x16, 0xda, 0x59, 0xc2, 0x45, 0x01, 0xf6, 0x27, 0xf6, 0xc9, 0xb7, 0xaf, 0xd1, 0x13, 0x28, 0x0a,
|
||||
0xbd, 0xa1, 0x9f, 0x66, 0x4e, 0x70, 0x2b, 0x44, 0xb3, 0x8c, 0x85, 0x04, 0x19, 0x02, 0xe1, 0x55,
|
||||
0x74, 0xe5, 0xda, 0xd7, 0xa1, 0x10, 0xca, 0x32, 0x96, 0x03, 0xf4, 0x0a, 0x76, 0xa3, 0x3d, 0x20,
|
||||
0xa1, 0x3f, 0x0f, 0x46, 0x94, 0x38, 0xde, 0x98, 0x7e, 0x12, 0x02, 0x58, 0xc6, 0x28, 0xb2, 0xf5,
|
||||
0x85, 0xc9, 0xe4, 0x16, 0xb4, 0x07, 0xd9, 0x09, 0x75, 0xae, 0x27, 0x52, 0xd4, 0xca, 0x38, 0x1a,
|
||||
0xe9, 0x7f, 0xcd, 0x40, 0x31, 0xb1, 0x31, 0xa8, 0x04, 0x79, 0x6c, 0xf4, 0x0d, 0xfc, 0xde, 0x68,
|
||||
0xa9, 0x5f, 0xa0, 0x3a, 0x3c, 0x33, 0xad, 0x66, 0x0f, 0x63, 0xa3, 0x39, 0x20, 0x3d, 0x4c, 0x86,
|
||||
0xd6, 0x5b, 0xab, 0xf7, 0xbd, 0x45, 0x2e, 0x1a, 0x1f, 0xba, 0x86, 0x35, 0x20, 0x2d, 0x63, 0xd0,
|
||||
0x30, 0x3b, 0x7d, 0x55, 0x41, 0x8f, 0x41, 0x5b, 0x32, 0x63, 0x73, 0xa3, 0xdb, 0x1b, 0x5a, 0x03,
|
||||
0x75, 0x0b, 0x3d, 0x81, 0xfd, 0xb6, 0x69, 0x35, 0x3a, 0x64, 0xc9, 0x69, 0x76, 0x06, 0xef, 0x89,
|
||||
0xf1, 0xc3, 0x85, 0x89, 0x3f, 0xa8, 0xa9, 0x4d, 0x04, 0x5e, 0x53, 0xb1, 0x87, 0x34, 0x7a, 0x04,
|
||||
0x0f, 0x24, 0x41, 0x4e, 0x21, 0x83, 0x5e, 0x8f, 0xf4, 0x7b, 0x3d, 0x4b, 0xcd, 0xa0, 0x6d, 0x28,
|
||||
0x9b, 0xd6, 0xfb, 0x46, 0xc7, 0x6c, 0x11, 0x6c, 0x34, 0x3a, 0x5d, 0x35, 0x8b, 0x76, 0xa0, 0xba,
|
||||
0xce, 0xcb, 0x71, 0x17, 0x31, 0xaf, 0x67, 0x99, 0x3d, 0x8b, 0xbc, 0x37, 0x70, 0xdf, 0xec, 0x59,
|
||||
0x6a, 0x1e, 0xed, 0x01, 0x5a, 0x35, 0x9d, 0x75, 0x1b, 0x4d, 0xb5, 0x80, 0x1e, 0xc0, 0xf6, 0x2a,
|
||||
0xfe, 0xd6, 0xf8, 0xa0, 0x02, 0xd2, 0x60, 0x57, 0x2e, 0x8c, 0x9c, 0x1a, 0x9d, 0xde, 0xf7, 0xa4,
|
||||
0x6b, 0x5a, 0x66, 0x77, 0xd8, 0x55, 0x8b, 0x68, 0x17, 0xd4, 0xb6, 0x61, 0x10, 0xd3, 0xea, 0x0f,
|
||||
0xdb, 0x6d, 0xb3, 0x69, 0x1a, 0xd6, 0x40, 0x2d, 0xc9, 0xc8, 0x9b, 0x3e, 0xbc, 0xcc, 0x27, 0x34,
|
||||
0xcf, 0x1a, 0x96, 0x65, 0x74, 0x48, 0xcb, 0xec, 0x37, 0x4e, 0x3b, 0x46, 0x4b, 0xad, 0xa0, 0x03,
|
||||
0x78, 0x34, 0x30, 0xba, 0x17, 0x3d, 0xdc, 0xc0, 0x1f, 0x48, 0x6c, 0x6f, 0x37, 0xcc, 0xce, 0x10,
|
||||
0x1b, 0x6a, 0x15, 0x7d, 0x05, 0x07, 0xd8, 0x78, 0x37, 0x34, 0xb1, 0xd1, 0x22, 0x56, 0xaf, 0x65,
|
||||
0x90, 0xb6, 0xd1, 0x18, 0x0c, 0xb1, 0x41, 0xba, 0x66, 0xbf, 0x6f, 0x5a, 0xdf, 0xa9, 0x2a, 0x7a,
|
||||
0x06, 0x87, 0x0b, 0xca, 0xc2, 0xc1, 0x1a, 0x6b, 0x9b, 0x7f, 0x5f, 0x9c, 0x52, 0xcb, 0xf8, 0x61,
|
||||
0x40, 0x2e, 0x0c, 0x03, 0xab, 0x08, 0xd5, 0x60, 0x6f, 0x19, 0x5e, 0x06, 0x88, 0x62, 0xef, 0x70,
|
||||
0xdb, 0x85, 0x81, 0xbb, 0x0d, 0x8b, 0x27, 0x78, 0xc5, 0xb6, 0xcb, 0x97, 0xbd, 0xb4, 0xad, 0x2f,
|
||||
0xfb, 0x01, 0x42, 0x50, 0x49, 0x64, 0xa5, 0xdd, 0xc0, 0xea, 0x1e, 0xda, 0x85, 0x6a, 0xbc, 0x82,
|
||||
0x98, 0xf8, 0xcf, 0x1c, 0x7a, 0x08, 0x68, 0x68, 0x61, 0xa3, 0xd1, 0xe2, 0x1b, 0xb2, 0x30, 0xfc,
|
||||
0x2b, 0x77, 0x9e, 0xce, 0x6f, 0xa9, 0x29, 0xfd, 0x1f, 0x29, 0x28, 0xaf, 0xd4, 0x25, 0x7a, 0x0c,
|
||||
0x85, 0xd0, 0xb9, 0xf6, 0xc4, 0x35, 0x15, 0x89, 0xca, 0x12, 0x10, 0xb7, 0xfa, 0xc4, 0x76, 0x3c,
|
||||
0xa9, 0x66, 0x52, 0xf7, 0x0b, 0x02, 0x11, 0x5a, 0xb6, 0x0f, 0xb9, 0xb8, 0x83, 0x48, 0x2d, 0x3a,
|
||||
0x88, 0xec, 0x48, 0x76, 0x0e, 0x8f, 0xa1, 0xc0, 0x25, 0x33, 0x64, 0xf6, 0x74, 0x26, 0x4a, 0xbc,
|
||||
0x8c, 0x97, 0x00, 0x7a, 0x0a, 0xe5, 0x29, 0x0d, 0x43, 0xfb, 0x9a, 0x12, 0x59, 0xa6, 0x20, 0x18,
|
||||
0xa5, 0x08, 0x6c, 0x8b, 0x6a, 0x7d, 0x0a, 0xb1, 0x6c, 0x44, 0xa4, 0x8c, 0x24, 0x45, 0xa0, 0x24,
|
||||
0xad, 0x2b, 0x36, 0xb3, 0x23, 0x35, 0x48, 0x2a, 0x36, 0xb3, 0xd1, 0x0b, 0xd8, 0x96, 0x92, 0xe3,
|
||||
0x78, 0xce, 0x74, 0x3e, 0x95, 0xd2, 0x93, 0x13, 0xd2, 0x53, 0x15, 0xd2, 0x23, 0x71, 0xa1, 0x40,
|
||||
0x8f, 0x20, 0x7f, 0x69, 0x87, 0x94, 0x5f, 0x16, 0x91, 0x34, 0xe4, 0xf8, 0xb8, 0x4d, 0x29, 0x37,
|
||||
0xf1, 0x2b, 0x24, 0xe0, 0xa2, 0x27, 0x15, 0x21, 0x77, 0x45, 0x29, 0xe6, 0x7b, 0xb9, 0x88, 0x60,
|
||||
0x7f, 0x5a, 0x46, 0x28, 0x26, 0x22, 0x48, 0x5c, 0x44, 0x78, 0x01, 0xdb, 0xf4, 0x13, 0x0b, 0x6c,
|
||||
0xe2, 0xcf, 0xec, 0x9f, 0xe6, 0x94, 0x8c, 0x6d, 0x66, 0x8b, 0x96, 0xb4, 0x84, 0xab, 0xc2, 0xd0,
|
||||
0x13, 0x78, 0xcb, 0x66, 0xb6, 0xfe, 0x18, 0x6a, 0x98, 0x86, 0x94, 0x75, 0x9d, 0x30, 0x74, 0x7c,
|
||||
0xaf, 0xe9, 0x7b, 0x2c, 0xf0, 0xdd, 0xe8, 0xce, 0xd1, 0x0f, 0x60, 0x7f, 0xa3, 0x55, 0x5e, 0x1a,
|
||||
0x7c, 0xf2, 0xbb, 0x39, 0x0d, 0x6e, 0x37, 0x4f, 0x7e, 0x07, 0xfb, 0x1b, 0xad, 0xd1, 0x8d, 0xf3,
|
||||
0x12, 0x32, 0x33, 0xdb, 0x09, 0x42, 0x6d, 0x4b, 0xdc, 0xda, 0x7b, 0x2b, 0x4d, 0x82, 0x13, 0x9c,
|
||||
0x39, 0x21, 0xf3, 0x83, 0x5b, 0x2c, 0x49, 0xe7, 0xe9, 0xbc, 0xa2, 0x6e, 0xe9, 0x7f, 0x52, 0xa0,
|
||||
0x98, 0x30, 0xf2, 0x73, 0xe0, 0xf9, 0x63, 0x4a, 0xae, 0x02, 0x7f, 0x1a, 0x9f, 0xb0, 0x05, 0x80,
|
||||
0x34, 0xc8, 0x89, 0x01, 0xf3, 0xa3, 0xe3, 0x15, 0x0f, 0xd1, 0x37, 0x90, 0x9b, 0x48, 0x17, 0x22,
|
||||
0x4b, 0xc5, 0x93, 0x9d, 0xb5, 0xe8, 0x7c, 0x6f, 0x70, 0xcc, 0x39, 0x4f, 0xe7, 0x53, 0x6a, 0xfa,
|
||||
0x3c, 0x9d, 0x4f, 0xab, 0x99, 0xf3, 0x74, 0x3e, 0xa3, 0x66, 0xcf, 0xd3, 0xf9, 0xac, 0x9a, 0xd3,
|
||||
0xff, 0xad, 0x40, 0x3e, 0x66, 0xf3, 0xb5, 0x70, 0x89, 0x27, 0xfc, 0x64, 0x44, 0x0d, 0xc0, 0x12,
|
||||
0x40, 0x3a, 0x94, 0xc4, 0x60, 0xb5, 0xaf, 0x58, 0xc1, 0xd0, 0x33, 0x28, 0x2f, 0xc6, 0x8b, 0xcb,
|
||||
0x2b, 0x85, 0x57, 0x41, 0xee, 0x29, 0x9c, 0x8f, 0x46, 0x34, 0x0c, 0x65, 0xa8, 0x8c, 0xf4, 0x94,
|
||||
0xc4, 0x50, 0x1d, 0xaa, 0xf1, 0x38, 0x0e, 0x98, 0x15, 0xb4, 0x75, 0x18, 0xbd, 0x00, 0x35, 0x09,
|
||||
0x4d, 0x97, 0xed, 0xff, 0x1d, 0x5c, 0x6e, 0x83, 0x3e, 0x85, 0x87, 0x22, 0xad, 0x17, 0x81, 0x7f,
|
||||
0x69, 0x5f, 0x3a, 0xae, 0xc3, 0x6e, 0xe3, 0x16, 0x85, 0x6f, 0x41, 0xe0, 0x4f, 0x89, 0x17, 0xdf,
|
||||
0xf9, 0x25, 0xbc, 0x04, 0x78, 0x3a, 0x98, 0x2f, 0x6d, 0x51, 0x3a, 0xa2, 0x21, 0x6f, 0x3e, 0x16,
|
||||
0xc1, 0x53, 0x22, 0xf8, 0x62, 0xac, 0xdf, 0x80, 0x76, 0x37, 0x5c, 0x74, 0x84, 0x0e, 0xa1, 0x38,
|
||||
0x5b, 0xc2, 0x22, 0xa2, 0x82, 0x93, 0x50, 0x32, 0xd1, 0x5b, 0xbf, 0x9c, 0x68, 0xfd, 0xcf, 0x0a,
|
||||
0x6c, 0x9f, 0xce, 0x1d, 0x77, 0xbc, 0xd2, 0x79, 0x25, 0x5f, 0x76, 0xca, 0xea, 0xcb, 0x6e, 0xd3,
|
||||
0xb3, 0x6d, 0x6b, 0xe3, 0xb3, 0x6d, 0xd3, 0xd3, 0x28, 0x75, 0xef, 0xd3, 0xe8, 0x09, 0x14, 0x97,
|
||||
0xaf, 0x22, 0xd9, 0xd8, 0x96, 0x30, 0x4c, 0xe2, 0x27, 0x51, 0xa8, 0xbf, 0x01, 0x94, 0x5c, 0x68,
|
||||
0xb4, 0x21, 0x8b, 0x06, 0x50, 0xb9, 0xb7, 0x01, 0x7c, 0xf1, 0x77, 0x05, 0x4a, 0xc9, 0x2e, 0x1c,
|
||||
0x95, 0xa1, 0x60, 0x5a, 0xa4, 0xdd, 0x31, 0xbf, 0x3b, 0x1b, 0xa8, 0x5f, 0xf0, 0x61, 0x7f, 0xd8,
|
||||
0x6c, 0x1a, 0x46, 0xcb, 0x68, 0xa9, 0x0a, 0xbf, 0x1f, 0xb8, 0xd4, 0x1b, 0x2d, 0x32, 0x30, 0xbb,
|
||||
0x46, 0x6f, 0xc8, 0x3b, 0x87, 0x1d, 0xa8, 0x46, 0x98, 0xd5, 0x23, 0xb8, 0x37, 0x1c, 0x18, 0x6a,
|
||||
0x0a, 0xa9, 0x50, 0x8a, 0x40, 0x03, 0xe3, 0x1e, 0x56, 0xd3, 0xfc, 0xba, 0x8b, 0x90, 0xbb, 0x5d,
|
||||
0x48, 0xdc, 0xa4, 0x64, 0x44, 0x97, 0x11, 0xb3, 0x96, 0x17, 0x34, 0x39, 0x6d, 0x74, 0x1a, 0x56,
|
||||
0xd3, 0x50, 0xb3, 0x27, 0x7f, 0xcb, 0x40, 0x56, 0x7c, 0x41, 0x80, 0xce, 0xa0, 0x98, 0x78, 0x90,
|
||||
0xa1, 0x83, 0xcf, 0x3e, 0xd4, 0x6a, 0xda, 0xe6, 0x77, 0xc7, 0x3c, 0x7c, 0xa5, 0xa0, 0x73, 0x28,
|
||||
0x25, 0x9f, 0x3b, 0x28, 0xd9, 0x9b, 0x6e, 0x78, 0x07, 0x7d, 0xd6, 0xd7, 0x5b, 0x50, 0x8d, 0x90,
|
||||
0x39, 0x53, 0xde, 0x8b, 0x46, 0xaf, 0x03, 0x54, 0x4b, 0xf0, 0xd7, 0x9e, 0x1c, 0xb5, 0xfd, 0x8d,
|
||||
0xb6, 0x28, 0x85, 0x1d, 0xf9, 0x89, 0x51, 0x7f, 0x7e, 0xe7, 0x13, 0x57, 0x1f, 0x05, 0xb5, 0x2f,
|
||||
0xef, 0x33, 0x47, 0xde, 0xc6, 0xb0, 0xb3, 0x41, 0xc0, 0xd1, 0xaf, 0x92, 0x2b, 0xb8, 0x57, 0xfe,
|
||||
0x6b, 0xcf, 0x7f, 0x89, 0xb6, 0x8c, 0xb2, 0x41, 0xe9, 0x57, 0xa2, 0xdc, 0x7f, 0x4f, 0xac, 0x44,
|
||||
0xf9, 0xdc, 0x85, 0xf1, 0x23, 0xa8, 0xeb, 0x4a, 0x80, 0xf4, 0xf5, 0xb9, 0x77, 0x55, 0xa9, 0xf6,
|
||||
0xf4, 0xb3, 0x9c, 0xc8, 0xb9, 0x09, 0xb0, 0xac, 0x27, 0xf4, 0x38, 0x31, 0xe5, 0x8e, 0x1e, 0xd4,
|
||||
0x0e, 0xee, 0xb1, 0x4a, 0x57, 0xa7, 0xbf, 0xfe, 0xe3, 0xf1, 0xb5, 0xc3, 0x26, 0xf3, 0xcb, 0xa3,
|
||||
0x91, 0x3f, 0x3d, 0x76, 0x79, 0x47, 0xef, 0x39, 0xde, 0xb5, 0x47, 0xd9, 0xcf, 0x7e, 0x70, 0x73,
|
||||
0xec, 0x7a, 0xe3, 0x63, 0x51, 0x96, 0xc7, 0x0b, 0x2f, 0x97, 0x59, 0xf1, 0xff, 0xa7, 0xdf, 0xfc,
|
||||
0x2f, 0x00, 0x00, 0xff, 0xff, 0x42, 0x87, 0x8c, 0xeb, 0xaf, 0x12, 0x00, 0x00,
|
||||
// 2102 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x58, 0xcb, 0x73, 0xdb, 0xc6,
|
||||
0x19, 0x0f, 0xc4, 0xf7, 0xc7, 0x17, 0xb4, 0x92, 0x65, 0x98, 0xb2, 0x62, 0x05, 0x76, 0x1d, 0x8e,
|
||||
0xc7, 0x91, 0x5c, 0xb5, 0xf1, 0x78, 0x7a, 0x68, 0x87, 0x22, 0xc1, 0x08, 0x32, 0x09, 0xca, 0x4b,
|
||||
0xd2, 0x89, 0x9b, 0xc3, 0x0e, 0x44, 0xae, 0x44, 0x8c, 0x40, 0x80, 0x01, 0x96, 0x8e, 0xf5, 0x0f,
|
||||
0xf4, 0xd4, 0xff, 0xa3, 0xbd, 0xb4, 0x97, 0x9e, 0xfb, 0xef, 0xb4, 0xf7, 0xde, 0x7a, 0xeb, 0xec,
|
||||
0x2e, 0x40, 0x82, 0x14, 0xe5, 0xf4, 0x44, 0xec, 0xef, 0x7b, 0xed, 0xee, 0xb7, 0xdf, 0x8b, 0xb0,
|
||||
0x17, 0xf8, 0x73, 0x46, 0x83, 0x60, 0x36, 0x3a, 0x96, 0x5f, 0x47, 0xb3, 0xc0, 0x67, 0x3e, 0x2a,
|
||||
0x2c, 0xf0, 0x5a, 0x21, 0x98, 0x8d, 0x24, 0xaa, 0xff, 0x37, 0x03, 0xa8, 0x4f, 0xbd, 0xf1, 0x85,
|
||||
0x7d, 0x3b, 0xa5, 0x1e, 0xc3, 0xf4, 0xa7, 0x39, 0x0d, 0x19, 0x42, 0x90, 0x1e, 0xd3, 0x90, 0x69,
|
||||
0xca, 0xa1, 0x52, 0x2f, 0x61, 0xf1, 0x8d, 0x54, 0x48, 0xd9, 0x53, 0xa6, 0x6d, 0x1d, 0x2a, 0xf5,
|
||||
0x14, 0xe6, 0x9f, 0xe8, 0x11, 0xe4, 0xed, 0x29, 0x23, 0xd3, 0xd0, 0x66, 0x5a, 0x49, 0xc0, 0x39,
|
||||
0x7b, 0xca, 0xba, 0xa1, 0xcd, 0xd0, 0x57, 0x50, 0x9a, 0x49, 0x95, 0x64, 0x62, 0x87, 0x13, 0x2d,
|
||||
0x25, 0x14, 0x15, 0x23, 0xec, 0xcc, 0x0e, 0x27, 0xa8, 0x0e, 0xea, 0x95, 0xe3, 0xd9, 0x2e, 0x19,
|
||||
0xb9, 0xec, 0x23, 0x19, 0x53, 0x97, 0xd9, 0x5a, 0xfa, 0x50, 0xa9, 0x67, 0x70, 0x45, 0xe0, 0x4d,
|
||||
0x97, 0x7d, 0x6c, 0x71, 0x14, 0x7d, 0x0d, 0xd5, 0x58, 0x59, 0x20, 0x37, 0xa8, 0x65, 0x0e, 0x95,
|
||||
0x7a, 0x01, 0x57, 0x66, 0xab, 0xdb, 0xfe, 0x1a, 0xaa, 0xcc, 0x99, 0x52, 0x7f, 0xce, 0x48, 0x48,
|
||||
0x47, 0xbe, 0x37, 0x0e, 0xb5, 0xac, 0xd4, 0x18, 0xc1, 0x7d, 0x89, 0x22, 0x1d, 0xca, 0x57, 0x94,
|
||||
0x12, 0xd7, 0x99, 0x3a, 0x8c, 0xf0, 0xed, 0xe7, 0xc4, 0xf6, 0x8b, 0x57, 0x94, 0x76, 0x38, 0xd6,
|
||||
0xb7, 0x19, 0x7a, 0x06, 0x95, 0x25, 0x8f, 0x38, 0x63, 0x59, 0x30, 0x95, 0x62, 0x26, 0x71, 0xd0,
|
||||
0x97, 0xa0, 0xfa, 0x73, 0x76, 0xed, 0x3b, 0xde, 0x35, 0x19, 0x4d, 0x6c, 0x8f, 0x38, 0x63, 0x2d,
|
||||
0x7f, 0xa8, 0xd4, 0xd3, 0xa7, 0x5b, 0xaf, 0x14, 0x5c, 0x89, 0x69, 0xcd, 0x89, 0xed, 0x99, 0x63,
|
||||
0xf4, 0x1c, 0xaa, 0xae, 0x1d, 0x32, 0x32, 0xf1, 0x67, 0x64, 0x36, 0xbf, 0xbc, 0xa1, 0xb7, 0x5a,
|
||||
0x45, 0xdc, 0x4c, 0x99, 0xc3, 0x67, 0xfe, 0xec, 0x42, 0x80, 0xe8, 0x00, 0x40, 0xdc, 0x8a, 0x30,
|
||||
0xae, 0x15, 0xc4, 0x19, 0x0a, 0x1c, 0x11, 0x86, 0xd1, 0x09, 0x14, 0x85, 0x37, 0xc9, 0xc4, 0xf1,
|
||||
0x58, 0xa8, 0xc1, 0x61, 0xaa, 0x5e, 0x3c, 0x51, 0x8f, 0x5c, 0x8f, 0x3b, 0x16, 0x73, 0xca, 0x99,
|
||||
0xe3, 0x31, 0x9c, 0x64, 0x42, 0x63, 0xd8, 0xe1, 0x6e, 0x24, 0xa3, 0x79, 0xc8, 0xfc, 0x29, 0x09,
|
||||
0xe8, 0xc8, 0x0f, 0xc6, 0xa1, 0x56, 0x14, 0xb2, 0xbf, 0x3d, 0x5a, 0xbc, 0x8e, 0xa3, 0xbb, 0xcf,
|
||||
0xe1, 0xa8, 0x45, 0x43, 0xd6, 0x14, 0x72, 0x58, 0x8a, 0x19, 0x1e, 0x0b, 0x6e, 0xf1, 0xf6, 0x78,
|
||||
0x1d, 0x47, 0x2f, 0x01, 0xd9, 0xae, 0xeb, 0xff, 0x4c, 0x42, 0xea, 0x5e, 0x91, 0xc8, 0x3d, 0x5a,
|
||||
0xf5, 0x50, 0xa9, 0xe7, 0xb1, 0x2a, 0x28, 0x7d, 0xea, 0x5e, 0x45, 0xea, 0xd1, 0x6b, 0x28, 0x8b,
|
||||
0x3d, 0x5d, 0x51, 0x9b, 0xcd, 0x03, 0x1a, 0x6a, 0xea, 0x61, 0xaa, 0x5e, 0x39, 0xd9, 0x8e, 0x4e,
|
||||
0xd2, 0x96, 0xf0, 0xa9, 0xc3, 0x70, 0x89, 0xf3, 0x45, 0xeb, 0xb0, 0xd6, 0x82, 0xbd, 0xcd, 0x5b,
|
||||
0xe2, 0x8f, 0x94, 0x5f, 0x2a, 0x7f, 0xb7, 0x69, 0xcc, 0x3f, 0xd1, 0x2e, 0x64, 0x3e, 0xda, 0xee,
|
||||
0x9c, 0x8a, 0x87, 0x5b, 0xc2, 0x72, 0xf1, 0xbb, 0xad, 0x37, 0x8a, 0xfe, 0x06, 0x76, 0x06, 0x81,
|
||||
0x3d, 0xba, 0x59, 0x7b, 0xfb, 0xeb, 0x4f, 0x57, 0xb9, 0xf3, 0x74, 0xf5, 0xbf, 0x2a, 0x50, 0x8e,
|
||||
0xa4, 0xfa, 0xcc, 0x66, 0xf3, 0x10, 0x7d, 0x03, 0x99, 0x90, 0xd9, 0x8c, 0x0a, 0xee, 0xca, 0xc9,
|
||||
0xc3, 0xc4, 0x7d, 0x26, 0x18, 0x29, 0x96, 0x5c, 0xa8, 0x06, 0xf9, 0x59, 0x40, 0x9d, 0xa9, 0x7d,
|
||||
0x1d, 0xef, 0x6b, 0xb1, 0x46, 0x3a, 0x64, 0x84, 0xb0, 0x88, 0x99, 0xe2, 0x49, 0x29, 0xe9, 0x56,
|
||||
0x2c, 0x49, 0xa8, 0x0e, 0x99, 0x09, 0x73, 0x47, 0xa1, 0x96, 0x16, 0xee, 0x43, 0x11, 0xcf, 0xd9,
|
||||
0xa0, 0xd3, 0x6c, 0x30, 0x46, 0xa7, 0x33, 0x86, 0x25, 0x83, 0xfe, 0x7b, 0xa8, 0x0a, 0xc9, 0x36,
|
||||
0xa5, 0x9f, 0x0b, 0xee, 0x87, 0xc0, 0x43, 0x57, 0x84, 0x82, 0x0c, 0xf0, 0xac, 0x3d, 0xe5, 0x51,
|
||||
0xa0, 0x8f, 0x41, 0x5d, 0xca, 0x87, 0x33, 0xdf, 0x0b, 0xb9, 0x75, 0x95, 0x6f, 0x83, 0x3f, 0x79,
|
||||
0x1e, 0x21, 0x22, 0x36, 0x14, 0x21, 0x55, 0x89, 0xf0, 0x36, 0xa5, 0x22, 0x3a, 0x9e, 0xcb, 0x80,
|
||||
0x24, 0xae, 0x3f, 0xba, 0xe1, 0x21, 0x6e, 0xdf, 0x46, 0xea, 0xcb, 0x1c, 0xee, 0xf8, 0xa3, 0x9b,
|
||||
0x16, 0x07, 0xf5, 0x1f, 0x65, 0x16, 0x1a, 0xf8, 0xf2, 0x94, 0xff, 0xb7, 0x27, 0x96, 0x97, 0xb5,
|
||||
0x75, 0xef, 0x65, 0xe9, 0x04, 0x76, 0x56, 0x94, 0x47, 0xa7, 0x48, 0xfa, 0x40, 0x59, 0xf3, 0xc1,
|
||||
0x4b, 0xc8, 0x5d, 0xd9, 0x8e, 0x3b, 0x0f, 0x62, 0xc5, 0x28, 0xe1, 0xd0, 0xb6, 0xa4, 0xe0, 0x98,
|
||||
0x45, 0xff, 0x53, 0x1e, 0x72, 0x11, 0x88, 0x4e, 0x20, 0x3d, 0xf2, 0xc7, 0xf1, 0x3b, 0xf8, 0xf2,
|
||||
0xae, 0x58, 0xfc, 0xdb, 0xf4, 0xc7, 0x14, 0x0b, 0x5e, 0xf4, 0x07, 0xa8, 0xf0, 0xd4, 0xe1, 0x51,
|
||||
0x97, 0xcc, 0x67, 0x63, 0x7b, 0xe1, 0x7a, 0x2d, 0x21, 0xdd, 0x94, 0x0c, 0x43, 0x41, 0xc7, 0xe5,
|
||||
0x51, 0x72, 0x89, 0xf6, 0xa1, 0xc0, 0xbd, 0x2d, 0x3d, 0x91, 0x16, 0x6f, 0x3f, 0xcf, 0x01, 0xe1,
|
||||
0x03, 0x1d, 0xca, 0xbe, 0xe7, 0xf8, 0x1e, 0x09, 0x27, 0x36, 0x39, 0xf9, 0xf6, 0xb5, 0xc8, 0x9d,
|
||||
0x25, 0x5c, 0x14, 0x60, 0x7f, 0x62, 0x9f, 0x7c, 0xfb, 0x1a, 0x3d, 0x81, 0xa2, 0xc8, 0x37, 0xf4,
|
||||
0xd3, 0xcc, 0x09, 0x6e, 0x45, 0xd2, 0x2c, 0x63, 0x91, 0x82, 0x0c, 0x81, 0xf0, 0x28, 0xba, 0x72,
|
||||
0xed, 0xeb, 0x50, 0x24, 0xca, 0x32, 0x96, 0x0b, 0xf4, 0x0a, 0x76, 0xa3, 0x3b, 0x20, 0xa1, 0x3f,
|
||||
0x0f, 0x46, 0x94, 0x38, 0xde, 0x98, 0x7e, 0x12, 0x09, 0xb0, 0x8c, 0x51, 0x44, 0xeb, 0x0b, 0x92,
|
||||
0xc9, 0x29, 0x68, 0x0f, 0xb2, 0x13, 0xea, 0x5c, 0x4f, 0x64, 0x52, 0x2b, 0xe3, 0x68, 0xa5, 0xff,
|
||||
0x33, 0x03, 0xc5, 0xc4, 0xc5, 0xa0, 0x12, 0xe4, 0xb1, 0xd1, 0x37, 0xf0, 0x7b, 0xa3, 0xa5, 0x7e,
|
||||
0x81, 0xea, 0xf0, 0xcc, 0xb4, 0x9a, 0x3d, 0x8c, 0x8d, 0xe6, 0x80, 0xf4, 0x30, 0x19, 0x5a, 0x6f,
|
||||
0xad, 0xde, 0xf7, 0x16, 0xb9, 0x68, 0x7c, 0xe8, 0x1a, 0xd6, 0x80, 0xb4, 0x8c, 0x41, 0xc3, 0xec,
|
||||
0xf4, 0x55, 0x05, 0x3d, 0x06, 0x6d, 0xc9, 0x19, 0x93, 0x1b, 0xdd, 0xde, 0xd0, 0x1a, 0xa8, 0x5b,
|
||||
0xe8, 0x09, 0xec, 0xb7, 0x4d, 0xab, 0xd1, 0x21, 0x4b, 0x9e, 0x66, 0x67, 0xf0, 0x9e, 0x18, 0x3f,
|
||||
0x5c, 0x98, 0xf8, 0x83, 0x9a, 0xda, 0xc4, 0xc0, 0x63, 0x2a, 0xd6, 0x90, 0x46, 0x8f, 0xe0, 0x81,
|
||||
0x64, 0x90, 0x22, 0x64, 0xd0, 0xeb, 0x91, 0x7e, 0xaf, 0x67, 0xa9, 0x19, 0xb4, 0x0d, 0x65, 0xd3,
|
||||
0x7a, 0xdf, 0xe8, 0x98, 0x2d, 0x82, 0x8d, 0x46, 0xa7, 0xab, 0x66, 0xd1, 0x0e, 0x54, 0xd7, 0xf9,
|
||||
0x72, 0x5c, 0x45, 0xcc, 0xd7, 0xb3, 0xcc, 0x9e, 0x45, 0xde, 0x1b, 0xb8, 0x6f, 0xf6, 0x2c, 0x35,
|
||||
0x8f, 0xf6, 0x00, 0xad, 0x92, 0xce, 0xba, 0x8d, 0xa6, 0x5a, 0x40, 0x0f, 0x60, 0x7b, 0x15, 0x7f,
|
||||
0x6b, 0x7c, 0x50, 0x01, 0x69, 0xb0, 0x2b, 0x37, 0x46, 0x4e, 0x8d, 0x4e, 0xef, 0x7b, 0xd2, 0x35,
|
||||
0x2d, 0xb3, 0x3b, 0xec, 0xaa, 0x45, 0xb4, 0x0b, 0x6a, 0xdb, 0x30, 0x88, 0x69, 0xf5, 0x87, 0xed,
|
||||
0xb6, 0xd9, 0x34, 0x0d, 0x6b, 0xa0, 0x96, 0xa4, 0xe5, 0x4d, 0x07, 0x2f, 0x73, 0x81, 0xe6, 0x59,
|
||||
0xc3, 0xb2, 0x8c, 0x0e, 0x69, 0x99, 0xfd, 0xc6, 0x69, 0xc7, 0x68, 0xa9, 0x15, 0x74, 0x00, 0x8f,
|
||||
0x06, 0x46, 0xf7, 0xa2, 0x87, 0x1b, 0xf8, 0x03, 0x89, 0xe9, 0xed, 0x86, 0xd9, 0x19, 0x62, 0x43,
|
||||
0xad, 0xa2, 0xaf, 0xe0, 0x00, 0x1b, 0xef, 0x86, 0x26, 0x36, 0x5a, 0xc4, 0xea, 0xb5, 0x0c, 0xd2,
|
||||
0x36, 0x1a, 0x83, 0x21, 0x36, 0x48, 0xd7, 0xec, 0xf7, 0x4d, 0xeb, 0x3b, 0x55, 0x45, 0xcf, 0xe0,
|
||||
0x70, 0xc1, 0xb2, 0x50, 0xb0, 0xc6, 0xb5, 0xcd, 0xcf, 0x17, 0xbb, 0xd4, 0x32, 0x7e, 0x18, 0x90,
|
||||
0x0b, 0xc3, 0xc0, 0x2a, 0x42, 0x35, 0xd8, 0x5b, 0x9a, 0x97, 0x06, 0x22, 0xdb, 0x3b, 0x9c, 0x76,
|
||||
0x61, 0xe0, 0x6e, 0xc3, 0xe2, 0x0e, 0x5e, 0xa1, 0xed, 0xf2, 0x6d, 0x2f, 0x69, 0xeb, 0xdb, 0x7e,
|
||||
0x80, 0x10, 0x54, 0x12, 0x5e, 0x69, 0x37, 0xb0, 0xba, 0x87, 0xaa, 0x50, 0xec, 0x5e, 0x5c, 0x90,
|
||||
0x81, 0xd9, 0x35, 0x7a, 0xc3, 0x81, 0xfa, 0x10, 0xed, 0x42, 0x35, 0xde, 0x52, 0x2c, 0xf9, 0xaf,
|
||||
0x1c, 0x7a, 0x08, 0x68, 0x68, 0x61, 0xa3, 0xd1, 0xe2, 0x37, 0xb4, 0x20, 0xfc, 0x3b, 0x77, 0x9e,
|
||||
0xce, 0x6f, 0xa9, 0x29, 0xfd, 0x1f, 0x29, 0x28, 0xaf, 0x04, 0x2a, 0x7a, 0x0c, 0x85, 0xd0, 0xb9,
|
||||
0xf6, 0x44, 0xdd, 0x8a, 0xb2, 0xcc, 0x12, 0x10, 0x65, 0x7e, 0x62, 0x3b, 0x9e, 0x4c, 0x6f, 0xb2,
|
||||
0x10, 0x14, 0x04, 0x22, 0x92, 0xdb, 0x3e, 0xe4, 0xe2, 0x96, 0x22, 0xb5, 0x68, 0x29, 0xb2, 0x23,
|
||||
0xd9, 0x4a, 0x3c, 0x86, 0x02, 0xcf, 0xa1, 0x21, 0xb3, 0xa7, 0x33, 0x11, 0xf3, 0x65, 0xbc, 0x04,
|
||||
0xd0, 0x53, 0x28, 0x4f, 0x69, 0x18, 0xda, 0xd7, 0x94, 0xc8, 0xb8, 0x05, 0xc1, 0x51, 0x8a, 0xc0,
|
||||
0xb6, 0x08, 0xdf, 0xa7, 0x10, 0xe7, 0x91, 0x88, 0x29, 0x23, 0x99, 0x22, 0x50, 0x32, 0xad, 0xa7,
|
||||
0x70, 0x66, 0x47, 0xe9, 0x21, 0x99, 0xc2, 0x99, 0x8d, 0x5e, 0xc0, 0xb6, 0xcc, 0x41, 0x8e, 0xe7,
|
||||
0x4c, 0xe7, 0x53, 0x99, 0x8b, 0x72, 0x22, 0x17, 0x55, 0x45, 0x2e, 0x92, 0xb8, 0x48, 0x49, 0x8f,
|
||||
0x20, 0x7f, 0x69, 0x87, 0x94, 0x57, 0x8f, 0x28, 0x57, 0xe4, 0xf8, 0xba, 0x4d, 0x29, 0x27, 0xf1,
|
||||
0x9a, 0x12, 0xf0, 0x2c, 0x28, 0x53, 0x44, 0xee, 0x8a, 0x52, 0xcc, 0xef, 0x72, 0x61, 0xc1, 0xfe,
|
||||
0xb4, 0xb4, 0x50, 0x4c, 0x58, 0x90, 0xb8, 0xb0, 0xf0, 0x02, 0xb6, 0xe9, 0x27, 0x16, 0xd8, 0xc4,
|
||||
0x9f, 0xd9, 0x3f, 0xcd, 0x29, 0x19, 0xdb, 0xcc, 0x16, 0x3d, 0x6a, 0x09, 0x57, 0x05, 0xa1, 0x27,
|
||||
0xf0, 0x96, 0xcd, 0x6c, 0xfd, 0x31, 0xd4, 0x30, 0x0d, 0x29, 0xeb, 0x3a, 0x61, 0xe8, 0xf8, 0x5e,
|
||||
0xd3, 0xf7, 0x58, 0xe0, 0xbb, 0x51, 0x11, 0xd2, 0x0f, 0x60, 0x7f, 0x23, 0x55, 0x56, 0x11, 0x2e,
|
||||
0xfc, 0x6e, 0x4e, 0x83, 0xdb, 0xcd, 0xc2, 0xef, 0x60, 0x7f, 0x23, 0x35, 0x2a, 0x41, 0x2f, 0x21,
|
||||
0x33, 0xb3, 0x9d, 0x20, 0xd4, 0xb6, 0x44, 0x19, 0xdf, 0x5b, 0xe9, 0x1a, 0x9c, 0xe0, 0xcc, 0x09,
|
||||
0x99, 0x1f, 0xdc, 0x62, 0xc9, 0x74, 0x9e, 0xce, 0x2b, 0xea, 0x96, 0xfe, 0x67, 0x05, 0x8a, 0x09,
|
||||
0x22, 0x7f, 0x07, 0x9e, 0x3f, 0xa6, 0xe4, 0x2a, 0xf0, 0xa7, 0xf1, 0x0b, 0x5b, 0x00, 0x48, 0x83,
|
||||
0x9c, 0x58, 0x30, 0x3f, 0x7a, 0x5e, 0xf1, 0x12, 0x7d, 0x03, 0xb9, 0x89, 0x54, 0x21, 0xbc, 0x54,
|
||||
0x3c, 0xd9, 0x59, 0xb3, 0xce, 0xef, 0x06, 0xc7, 0x3c, 0xe7, 0xe9, 0x7c, 0x4a, 0x4d, 0x9f, 0xa7,
|
||||
0xf3, 0x69, 0x35, 0x73, 0x9e, 0xce, 0x67, 0xd4, 0xec, 0x79, 0x3a, 0x9f, 0x55, 0x73, 0xfa, 0x7f,
|
||||
0x14, 0xc8, 0xc7, 0xdc, 0x7c, 0x2f, 0x3c, 0xe7, 0x13, 0xfe, 0x32, 0xa2, 0x8e, 0x60, 0x09, 0x20,
|
||||
0x1d, 0x4a, 0x62, 0xb1, 0xda, 0x68, 0xac, 0x60, 0xe8, 0x19, 0x94, 0x17, 0xeb, 0x45, 0x35, 0x4b,
|
||||
0xe1, 0x55, 0x90, 0x6b, 0x0a, 0xe7, 0xa3, 0x11, 0x0d, 0x43, 0x69, 0x2a, 0x23, 0x35, 0x25, 0x31,
|
||||
0x54, 0x87, 0x6a, 0xbc, 0x8e, 0x0d, 0x66, 0x05, 0xdb, 0x3a, 0x8c, 0x5e, 0x80, 0x9a, 0x84, 0xa6,
|
||||
0xcb, 0x79, 0xe0, 0x0e, 0x2e, 0xaf, 0x41, 0x9f, 0xc2, 0x43, 0xe1, 0xd6, 0x8b, 0xc0, 0xbf, 0xb4,
|
||||
0x2f, 0x1d, 0xd7, 0x61, 0xb7, 0x71, 0xcf, 0xc2, 0xaf, 0x20, 0xf0, 0xa7, 0xc4, 0x8b, 0x9b, 0x80,
|
||||
0x12, 0x5e, 0x02, 0xdc, 0x1d, 0xcc, 0x97, 0xb4, 0xc8, 0x1d, 0xd1, 0x92, 0x77, 0x23, 0x0b, 0xe3,
|
||||
0x29, 0x61, 0x7c, 0xb1, 0xd6, 0x6f, 0x40, 0xbb, 0x6b, 0x2e, 0x7a, 0x42, 0x87, 0x50, 0x9c, 0x2d,
|
||||
0x61, 0x61, 0x51, 0xc1, 0x49, 0x28, 0xe9, 0xe8, 0xad, 0x5f, 0x76, 0xb4, 0xfe, 0x17, 0x05, 0xb6,
|
||||
0x4f, 0xe7, 0x8e, 0x3b, 0x5e, 0x69, 0xc5, 0x92, 0xa3, 0x9e, 0xb2, 0x3a, 0xea, 0x6d, 0x9a, 0xe3,
|
||||
0xb6, 0x36, 0xce, 0x71, 0x9b, 0x66, 0xa5, 0xd4, 0xbd, 0xb3, 0xd2, 0x13, 0x28, 0x2e, 0xc7, 0x24,
|
||||
0xd9, 0xe9, 0x96, 0x30, 0x4c, 0xe2, 0x19, 0x29, 0xd4, 0xdf, 0x00, 0x4a, 0x6e, 0x34, 0xba, 0x90,
|
||||
0x45, 0x47, 0xa8, 0xdc, 0xdb, 0x11, 0xbe, 0xf8, 0xbb, 0x02, 0xa5, 0x64, 0x5b, 0x8e, 0xca, 0x50,
|
||||
0x30, 0x2d, 0xd2, 0xee, 0x98, 0xdf, 0x9d, 0x0d, 0xd4, 0x2f, 0xf8, 0xb2, 0x3f, 0x6c, 0x36, 0x0d,
|
||||
0xa3, 0x65, 0xb4, 0x54, 0x85, 0x17, 0x0c, 0x9e, 0xea, 0x8d, 0xd6, 0xa2, 0x3e, 0x6c, 0xf1, 0xd2,
|
||||
0x1e, 0x61, 0x56, 0x8f, 0xe0, 0xde, 0x70, 0x60, 0xa8, 0x29, 0xa4, 0x42, 0x29, 0x02, 0x0d, 0x8c,
|
||||
0x7b, 0x58, 0x4d, 0xf3, 0xfa, 0x17, 0x21, 0x77, 0xdb, 0x92, 0xb8, 0x6b, 0xc9, 0x88, 0xb6, 0x23,
|
||||
0xe6, 0x5a, 0x56, 0x6c, 0x72, 0xda, 0xe8, 0x34, 0xac, 0xa6, 0xa1, 0x66, 0x4f, 0xfe, 0x96, 0x81,
|
||||
0xac, 0x38, 0x41, 0x80, 0xce, 0xa0, 0x98, 0x98, 0xd0, 0xd0, 0xc1, 0x67, 0x27, 0xb7, 0x9a, 0xb6,
|
||||
0x79, 0x10, 0x99, 0x87, 0xaf, 0x14, 0x74, 0x0e, 0xa5, 0xe4, 0xfc, 0x83, 0x92, 0xcd, 0xea, 0x86,
|
||||
0xc1, 0xe8, 0xb3, 0xba, 0xde, 0x82, 0x6a, 0x84, 0xcc, 0x99, 0xf2, 0xe6, 0x34, 0x1a, 0x17, 0x50,
|
||||
0x2d, 0xc1, 0xbf, 0x36, 0x83, 0xd4, 0xf6, 0x37, 0xd2, 0x22, 0x17, 0x76, 0xe4, 0x11, 0xa3, 0x86,
|
||||
0xfd, 0xce, 0x11, 0x57, 0xa7, 0x84, 0xda, 0x97, 0xf7, 0x91, 0x23, 0x6d, 0x63, 0xd8, 0xd9, 0x90,
|
||||
0xc0, 0xd1, 0xaf, 0x92, 0x3b, 0xb8, 0x37, 0xfd, 0xd7, 0x9e, 0xff, 0x12, 0xdb, 0xd2, 0xca, 0x86,
|
||||
0x4c, 0xbf, 0x62, 0xe5, 0xfe, 0x3a, 0xb1, 0x62, 0xe5, 0x73, 0x05, 0xe3, 0x47, 0x50, 0xd7, 0x33,
|
||||
0x01, 0xd2, 0xd7, 0x65, 0xef, 0x66, 0xa5, 0xda, 0xd3, 0xcf, 0xf2, 0x44, 0xca, 0x4d, 0x80, 0x65,
|
||||
0x3c, 0xa1, 0xc7, 0x09, 0x91, 0x3b, 0xf9, 0xa0, 0x76, 0x70, 0x0f, 0x55, 0xaa, 0x3a, 0xfd, 0xf5,
|
||||
0x1f, 0x8f, 0xaf, 0x1d, 0x36, 0x99, 0x5f, 0x1e, 0x8d, 0xfc, 0xe9, 0xb1, 0xcb, 0x5b, 0x7c, 0xcf,
|
||||
0xf1, 0xae, 0x3d, 0xca, 0x7e, 0xf6, 0x83, 0x9b, 0x63, 0xd7, 0x1b, 0x1f, 0x8b, 0xb0, 0x3c, 0x5e,
|
||||
0x68, 0xb9, 0xcc, 0x8a, 0x3f, 0xa4, 0x7e, 0xf3, 0xbf, 0x00, 0x00, 0x00, 0xff, 0xff, 0x10, 0xf6,
|
||||
0xab, 0x74, 0xc0, 0x12, 0x00, 0x00,
|
||||
}
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
|
@ -256,6 +256,7 @@ message Failure {
|
||||
PERMANENT_NODE_FAILURE = 20;
|
||||
PERMANENT_CHANNEL_FAILURE = 21;
|
||||
EXPIRY_TOO_FAR = 22;
|
||||
MPP_TIMEOUT = 23;
|
||||
|
||||
/**
|
||||
The error source is known, but the failure itself couldn't be decoded.
|
||||
|
@ -415,6 +415,9 @@ func marshallError(sendError error) (*Failure, error) {
|
||||
case *lnwire.FailPermanentChannelFailure:
|
||||
response.Code = Failure_PERMANENT_CHANNEL_FAILURE
|
||||
|
||||
case *lnwire.FailMPPTimeout:
|
||||
response.Code = Failure_MPP_TIMEOUT
|
||||
|
||||
case nil:
|
||||
response.Code = Failure_UNKNOWN_FAILURE
|
||||
|
||||
|
@ -80,6 +80,7 @@ const (
|
||||
CodeFinalIncorrectHtlcAmount FailCode = 19
|
||||
CodeExpiryTooFar FailCode = 21
|
||||
CodeInvalidOnionPayload = FlagPerm | 22
|
||||
CodeMPPTimeout FailCode = 23
|
||||
)
|
||||
|
||||
// String returns the string representation of the failure code.
|
||||
@ -154,6 +155,9 @@ func (c FailCode) String() string {
|
||||
case CodeInvalidOnionPayload:
|
||||
return "InvalidOnionPayload"
|
||||
|
||||
case CodeMPPTimeout:
|
||||
return "MPPTimeout"
|
||||
|
||||
default:
|
||||
return "<unknown>"
|
||||
}
|
||||
@ -1182,6 +1186,26 @@ func (f *InvalidOnionPayload) Encode(w io.Writer, pver uint32) error {
|
||||
return WriteElements(w, f.Offset)
|
||||
}
|
||||
|
||||
// FailMPPTimeout is returned if the complete amount for a multi part payment
|
||||
// was not received within a reasonable time.
|
||||
//
|
||||
// NOTE: May only be returned by the final node in the path.
|
||||
type FailMPPTimeout struct{}
|
||||
|
||||
// Code returns the failure unique code.
|
||||
//
|
||||
// NOTE: Part of the FailureMessage interface.
|
||||
func (f *FailMPPTimeout) Code() FailCode {
|
||||
return CodeMPPTimeout
|
||||
}
|
||||
|
||||
// Returns a human readable string describing the target FailureMessage.
|
||||
//
|
||||
// NOTE: Implements the error interface.
|
||||
func (f *FailMPPTimeout) Error() string {
|
||||
return f.Code().String()
|
||||
}
|
||||
|
||||
// DecodeFailure decodes, validates, and parses the lnwire onion failure, for
|
||||
// the provided protocol version.
|
||||
func DecodeFailure(r io.Reader, pver uint32) (FailureMessage, error) {
|
||||
@ -1366,6 +1390,9 @@ func makeEmptyOnionError(code FailCode) (FailureMessage, error) {
|
||||
case CodeInvalidOnionPayload:
|
||||
return &InvalidOnionPayload{}, nil
|
||||
|
||||
case CodeMPPTimeout:
|
||||
return &FailMPPTimeout{}, nil
|
||||
|
||||
default:
|
||||
return nil, errors.Errorf("unknown error code: %v", code)
|
||||
}
|
||||
|
@ -38,6 +38,7 @@ var onionFailures = []FailureMessage{
|
||||
&FailUnknownNextPeer{},
|
||||
&FailIncorrectPaymentAmount{},
|
||||
&FailFinalExpiryTooSoon{},
|
||||
&FailMPPTimeout{},
|
||||
|
||||
NewFailIncorrectDetails(99, 100),
|
||||
NewInvalidOnionVersion(testOnionHash),
|
||||
|
@ -16,8 +16,12 @@ import (
|
||||
|
||||
const testMaxRecords = 2
|
||||
|
||||
// TestMissionControlStore tests the recording of payment failure events
|
||||
// in mission control. It tests encoding and decoding of differing lnwire
|
||||
// failures (FailIncorrectDetails and FailMppTimeout), pruning of results
|
||||
// and idempotent writes.
|
||||
func TestMissionControlStore(t *testing.T) {
|
||||
// Set time zone explictly to keep test deterministic.
|
||||
// Set time zone explicitly to keep test deterministic.
|
||||
time.Local = time.UTC
|
||||
|
||||
file, err := ioutil.TempFile("", "*.db")
|
||||
@ -115,11 +119,12 @@ func TestMissionControlStore(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Add a newer result.
|
||||
// Add a newer result which failed due to mpp timeout.
|
||||
result3 := result1
|
||||
result3.timeReply = result1.timeReply.Add(2 * time.Hour)
|
||||
result3.timeFwd = result1.timeReply.Add(2 * time.Hour)
|
||||
result3.id = 3
|
||||
result3.failure = &lnwire.FailMPPTimeout{}
|
||||
|
||||
err = store.AddResult(&result3)
|
||||
if err != nil {
|
||||
|
@ -216,6 +216,22 @@ func (i *interpretedResult) processPaymentOutcomeFinal(
|
||||
// deliberately. What to penalize?
|
||||
i.finalFailureReason = &reasonIncorrectDetails
|
||||
|
||||
case *lnwire.FailMPPTimeout:
|
||||
// TODO(carla): decide how to penalize mpp timeout. In the
|
||||
// meantime, attribute success to the hops along the route and
|
||||
// do not penalize the final node.
|
||||
|
||||
i.finalFailureReason = &reasonError
|
||||
|
||||
// If this is a direct payment, take no action.
|
||||
if n == 1 {
|
||||
return
|
||||
}
|
||||
|
||||
// Assign all pairs a success result except the final hop, as
|
||||
// the payment reached the destination correctly.
|
||||
i.successPairRange(route, 0, n-2)
|
||||
|
||||
default:
|
||||
// All other errors are considered terminal if coming from the
|
||||
// final hop. They indicate that something is wrong at the
|
||||
|
@ -272,6 +272,39 @@ var resultTestCases = []resultTestCase{
|
||||
nodeFailure: &hops[1],
|
||||
},
|
||||
},
|
||||
|
||||
// Tests a single hop mpp timeout. Test that final node is not
|
||||
// penalized. This is a temporary measure while we decide how to
|
||||
// penalize mpp timeouts.
|
||||
{
|
||||
name: "one hop mpp timeout",
|
||||
route: &routeOneHop,
|
||||
failureSrcIdx: 1,
|
||||
failure: &lnwire.FailMPPTimeout{},
|
||||
|
||||
expectedResult: &interpretedResult{
|
||||
finalFailureReason: &reasonError,
|
||||
nodeFailure: nil,
|
||||
},
|
||||
},
|
||||
|
||||
// Tests a two hop mpp timeout. Test that final node is not penalized
|
||||
// and the intermediate hop is attributed the success. This is a
|
||||
// temporary measure while we decide how to penalize mpp timeouts.
|
||||
{
|
||||
name: "two hop mpp timeout",
|
||||
route: &routeTwoHop,
|
||||
failureSrcIdx: 2,
|
||||
failure: &lnwire.FailMPPTimeout{},
|
||||
|
||||
expectedResult: &interpretedResult{
|
||||
pairResults: map[DirectedNodePair]pairResult{
|
||||
getTestPair(0, 1): successPairResult(100),
|
||||
},
|
||||
finalFailureReason: &reasonError,
|
||||
nodeFailure: nil,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// TestResultInterpretation executes a list of test cases that test the result
|
||||
|
Loading…
Reference in New Issue
Block a user