Merge pull request #378 from carlaKC/383-cancelation

loopout: Add swap cancelation for swaps that cannot route off-chain
pull/391/head
Carla Kirk-Cohen 3 years ago committed by GitHub
commit 124eeea9d3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -142,6 +142,7 @@ func NewClient(dbDir string, cfg *ClientConfig) (*Client, func(), error) {
sweeper: sweeper,
createExpiryTimer: config.CreateExpiryTimer,
loopOutMaxParts: cfg.LoopOutMaxParts,
cancelSwap: swapServerClient.CancelLoopOutSwap,
})
client := &Client{

@ -100,6 +100,7 @@ func TestFailOffchain(t *testing.T) {
signalPrepaymentResult(
errors.New(lndclient.PaymentResultUnknownPaymentHash),
)
<-ctx.serverMock.cancelSwap
ctx.assertStatus(loopdb.StateFailOffchainPayments)
ctx.assertStoreFinished(loopdb.StateFailOffchainPayments)

@ -25,6 +25,8 @@ type executorConfig struct {
createExpiryTimer func(expiry time.Duration) <-chan time.Time
loopOutMaxParts uint32
cancelSwap func(ctx context.Context, details *outCancelDetails) error
}
// executor is responsible for executing swaps.
@ -138,13 +140,17 @@ func (s *executor) run(mainCtx context.Context,
go func() {
defer s.wg.Done()
newSwap.execute(mainCtx, &executeConfig{
err := newSwap.execute(mainCtx, &executeConfig{
statusChan: statusChan,
sweeper: s.sweeper,
blockEpochChan: queue.ChanOut(),
timerFactory: s.executorConfig.createExpiryTimer,
loopOutMaxParts: s.executorConfig.loopOutMaxParts,
cancelSwap: s.executorConfig.cancelSwap,
}, height)
if err != nil && err != context.Canceled {
log.Errorf("Execute error: %v", err)
}
select {
case swapDoneChan <- swapID:

@ -39,13 +39,17 @@ const (
// invoice so that the server can perform a multi-path probe.
ProtocolVersionMultiLoopIn ProtocolVersion = 6
// ProtocolVersionLoopOutCancel indicates that the client supports
// canceling loop out swaps.
ProtocolVersionLoopOutCancel = 7
// ProtocolVersionUnrecorded is set for swaps were created before we
// started saving protocol version with swaps.
ProtocolVersionUnrecorded ProtocolVersion = math.MaxUint32
// CurrentRPCProtocolVersion defines the version of the RPC protocol
// that is currently supported by the loop client.
CurrentRPCProtocolVersion = looprpc.ProtocolVersion_MULTI_LOOP_IN
CurrentRPCProtocolVersion = looprpc.ProtocolVersion_LOOP_OUT_CANCEL
// CurrentInternalProtocolVersion defines the RPC current protocol in
// the internal representation.
@ -81,6 +85,9 @@ func (p ProtocolVersion) String() string {
case ProtocolVersionHtlcV2:
return "HTLC V2"
case ProtocolVersionLoopOutCancel:
return "Loop Out Cancel"
default:
return "Unknown"
}

@ -21,6 +21,7 @@ func TestProtocolVersionSanity(t *testing.T) {
ProtocolVersionUserExpiryLoopOut,
ProtocolVersionHtlcV2,
ProtocolVersionMultiLoopIn,
ProtocolVersionLoopOutCancel,
}
rpcVersions := [...]looprpc.ProtocolVersion{
@ -31,6 +32,7 @@ func TestProtocolVersionSanity(t *testing.T) {
looprpc.ProtocolVersion_USER_EXPIRY_LOOP_OUT,
looprpc.ProtocolVersion_HTLC_V2,
looprpc.ProtocolVersion_MULTI_LOOP_IN,
looprpc.ProtocolVersion_LOOP_OUT_CANCEL,
}
require.Equal(t, len(versions), len(rpcVersions))

@ -39,6 +39,7 @@ func newLoopInTestContext(t *testing.T) *loopInTestContext {
sweeper: &sweeper,
blockEpochChan: blockEpochChan,
timerFactory: timerFactory,
cancelSwap: server.CancelLoopOutSwap,
}
return &loopInTestContext{

@ -6,6 +6,7 @@ import (
"crypto/sha256"
"errors"
"fmt"
"math"
"sync"
"time"
@ -21,8 +22,17 @@ import (
"github.com/lightningnetwork/lnd/channeldb"
"github.com/lightningnetwork/lnd/lnrpc"
"github.com/lightningnetwork/lnd/lntypes"
"github.com/lightningnetwork/lnd/zpay32"
)
// loopInternalHops indicate the number of hops that a loop out swap makes in
// the server's off-chain infrastructure. We are ok reporting failure distances
// from the server up until this point, because every swap takes these two
// hops, so surfacing this information does not identify the client in any way.
// After this point, the client does not report failure distances, so that
// sender-privacy is preserved.
const loopInternalHops = 2
var (
// MinLoopOutPreimageRevealDelta configures the minimum number of
// remaining blocks before htlc expiry required to reveal preimage.
@ -62,8 +72,8 @@ type loopOutSwap struct {
// htlcTxHash is the confirmed htlc tx id.
htlcTxHash *chainhash.Hash
swapPaymentChan chan lndclient.PaymentResult
prePaymentChan chan lndclient.PaymentResult
swapPaymentChan chan paymentResult
prePaymentChan chan paymentResult
wg sync.WaitGroup
}
@ -75,6 +85,7 @@ type executeConfig struct {
blockEpochChan <-chan interface{}
timerFactory func(d time.Duration) <-chan time.Time
loopOutMaxParts uint32
cancelSwap func(context.Context, *outCancelDetails) error
}
// loopOutInitResult contains information about a just-initiated loop out swap.
@ -340,27 +351,35 @@ func (s *loopOutSwap) executeAndFinalize(globalCtx context.Context) error {
select {
case result := <-s.swapPaymentChan:
s.swapPaymentChan = nil
if result.Err != nil {
err := s.handlePaymentResult(result)
if err != nil {
return err
}
if result.failure() != nil {
// Server didn't pull the swap payment.
s.log.Infof("Swap payment failed: %v",
result.Err)
result.failure())
continue
}
s.cost.Server += result.PaidAmt
s.cost.Offchain += result.PaidFee
case result := <-s.prePaymentChan:
s.prePaymentChan = nil
if result.Err != nil {
err := s.handlePaymentResult(result)
if err != nil {
return err
}
if result.failure() != nil {
// Server didn't pull the prepayment.
s.log.Infof("Prepayment failed: %v",
result.Err)
result.failure())
continue
}
s.cost.Server += result.PaidAmt
s.cost.Offchain += result.PaidFee
case <-globalCtx.Done():
return globalCtx.Err()
@ -379,6 +398,27 @@ func (s *loopOutSwap) executeAndFinalize(globalCtx context.Context) error {
return s.persistState(globalCtx)
}
func (s *loopOutSwap) handlePaymentResult(result paymentResult) error {
switch {
// If our result has a non-nil error, our status will be nil. In this
// case the payment failed so we do not need to take any action.
case result.err != nil:
return nil
case result.status.State == lnrpc.Payment_SUCCEEDED:
s.cost.Server += result.status.Value.ToSatoshis()
s.cost.Offchain += result.status.Fee.ToSatoshis()
return nil
case result.status.State == lnrpc.Payment_FAILED:
return nil
default:
return fmt.Errorf("unexpected state: %v", result.status.State)
}
}
// executeSwap executes the swap, but returns as soon as the swap outcome is
// final. At that point, there may still be pending off-chain payment(s).
func (s *loopOutSwap) executeSwap(globalCtx context.Context) error {
@ -510,31 +550,64 @@ func (s *loopOutSwap) payInvoices(ctx context.Context) {
)
}
// paymentResult contains the response for a failed or settled payment, and
// any errors that occurred if the payment unexpectedly failed.
type paymentResult struct {
status lndclient.PaymentStatus
err error
}
// failure returns the error we encountered trying to dispatch a payment result,
// if any.
func (p paymentResult) failure() error {
if p.err != nil {
return p.err
}
if p.status.State == lnrpc.Payment_SUCCEEDED {
return nil
}
return fmt.Errorf("payment failed: %v", p.status.FailureReason)
}
// payInvoice pays a single invoice.
func (s *loopOutSwap) payInvoice(ctx context.Context, invoice string,
maxFee btcutil.Amount,
outgoingChanIds loopdb.ChannelSet) chan lndclient.PaymentResult {
outgoingChanIds loopdb.ChannelSet) chan paymentResult {
resultChan := make(chan lndclient.PaymentResult)
resultChan := make(chan paymentResult)
sendResult := func(result paymentResult) {
select {
case resultChan <- result:
case <-ctx.Done():
}
}
go func() {
var result lndclient.PaymentResult
var result paymentResult
status, err := s.payInvoiceAsync(
ctx, invoice, maxFee, outgoingChanIds,
)
if err != nil {
result.Err = err
} else {
result.Preimage = status.Preimage
result.PaidFee = status.Fee.ToSatoshis()
result.PaidAmt = status.Value.ToSatoshis()
result.err = err
sendResult(result)
return
}
select {
case resultChan <- result:
case <-ctx.Done():
// If our payment failed or succeeded, our status should be
// non-nil.
switch status.State {
case lnrpc.Payment_FAILED, lnrpc.Payment_SUCCEEDED:
result.status = *status
default:
result.err = fmt.Errorf("unexpected payment state: %v",
status.State)
}
sendResult(result)
}()
return resultChan
@ -583,7 +656,7 @@ func (s *loopOutSwap) payInvoiceAsync(ctx context.Context,
return &payState, nil
case lnrpc.Payment_FAILED:
return nil, errors.New("payment failed")
return &payState, nil
case lnrpc.Payment_IN_FLIGHT:
// Continue waiting for final state.
@ -686,30 +759,45 @@ func (s *loopOutSwap) waitForConfirmedHtlc(globalCtx context.Context) (
// have lost the prepayment.
case result := <-s.swapPaymentChan:
s.swapPaymentChan = nil
if result.Err != nil {
s.state = loopdb.StateFailOffchainPayments
err := s.handlePaymentResult(result)
if err != nil {
return nil, err
}
if result.failure() != nil {
s.log.Infof("Failed swap payment: %v",
result.Err)
result.failure())
s.failOffChain(
ctx, paymentTypeInvoice,
result.status,
)
return nil, nil
}
s.cost.Server += result.PaidAmt
s.cost.Offchain += result.PaidFee
// If the prepay fails, abandon the swap. Because we
// didn't reveal the preimage, the swap payment will be
// canceled or time out.
case result := <-s.prePaymentChan:
s.prePaymentChan = nil
if result.Err != nil {
s.state = loopdb.StateFailOffchainPayments
err := s.handlePaymentResult(result)
if err != nil {
return nil, err
}
if result.failure() != nil {
s.log.Infof("Failed prepayment: %v",
result.Err)
result.failure())
s.failOffChain(
ctx, paymentTypeInvoice,
result.status,
)
return nil, nil
}
s.cost.Server += result.PaidAmt
s.cost.Offchain += result.PaidFee
// Unexpected error on the confirm channel happened,
// abandon the swap.
@ -901,6 +989,98 @@ func (s *loopOutSwap) pushPreimage(ctx context.Context) {
}
}
// failOffChain updates a swap's state when it has failed due to a routing
// failure and notifies the server of the failure.
func (s *loopOutSwap) failOffChain(ctx context.Context, paymentType paymentType,
status lndclient.PaymentStatus) {
// Set our state to failed off chain timeout.
s.state = loopdb.StateFailOffchainPayments
swapPayReq, err := zpay32.Decode(
s.LoopOutContract.SwapInvoice, s.swapConfig.lnd.ChainParams,
)
if err != nil {
s.log.Errorf("could not decode swap invoice: %v", err)
return
}
if swapPayReq.PaymentAddr == nil {
s.log.Errorf("expected payment address for invoice")
return
}
details := &outCancelDetails{
hash: s.hash,
paymentAddr: *swapPayReq.PaymentAddr,
metadata: routeCancelMetadata{
paymentType: paymentType,
failureReason: status.FailureReason,
},
}
for _, htlc := range status.Htlcs {
if htlc.Status != lnrpc.HTLCAttempt_FAILED {
continue
}
if htlc.Route == nil {
continue
}
if len(htlc.Route.Hops) == 0 {
continue
}
if htlc.Failure == nil {
continue
}
failureIdx := htlc.Failure.FailureSourceIndex
hops := uint32(len(htlc.Route.Hops))
// We really don't expect a failure index that is greater than
// our number of hops. This is because failure index is zero
// based, where a value of zero means that the payment failed
// at the client's node, and a value = len(hops) means that it
// failed at the last node in the route. We don't want to
// underflow so we check and log a warning if this happens.
if failureIdx > hops {
s.log.Warnf("Htlc attempt failure index > hops",
failureIdx, hops)
continue
}
// Add the number of hops from the server that we failed at
// to the set of attempts that we will report to the server.
distance := hops - failureIdx
// In the case that our swap failed in the network at large,
// rather than the loop server's internal infrastructure, we
// don't want to disclose and information about distance from
// the server, so we set maxUint32 to represent failure in
// "the network at large" rather than due to the server's
// liquidity.
if distance > loopInternalHops {
distance = math.MaxUint32
}
details.metadata.attempts = append(
details.metadata.attempts, distance,
)
}
s.log.Infof("Canceling swap: %v payment failed: %v, %v attempts",
paymentType, details.metadata.failureReason,
len(details.metadata.attempts))
// Report to server, it's not critical if this doesn't go through.
if err := s.cancelSwap(ctx, details); err != nil {
s.log.Warnf("Could not report failure: %v", err)
}
}
// sweep tries to sweep the given htlc to a destination address. It takes into
// account the max miner fee and marks the preimage as revealed when it
// published the tx. If the preimage has not yet been revealed, and the time

@ -3,6 +3,7 @@ package loop
import (
"context"
"errors"
"math"
"reflect"
"testing"
"time"
@ -16,6 +17,7 @@ import (
"github.com/lightninglabs/loop/test"
"github.com/lightningnetwork/lnd/lnrpc"
"github.com/lightningnetwork/lnd/lnwallet/chainfee"
"github.com/lightningnetwork/lnd/zpay32"
"github.com/stretchr/testify/require"
)
@ -73,6 +75,7 @@ func TestLoopOutPaymentParameters(t *testing.T) {
blockEpochChan: blockEpochChan,
timerFactory: timerFactory,
loopOutMaxParts: maxParts,
cancelSwap: server.CancelLoopOutSwap,
}, height)
if err != nil {
log.Error(err)
@ -173,6 +176,7 @@ func TestLateHtlcPublish(t *testing.T) {
sweeper: sweeper,
blockEpochChan: blockEpochChan,
timerFactory: timerFactory,
cancelSwap: server.CancelLoopOutSwap,
}, height)
if err != nil {
log.Error(err)
@ -267,6 +271,7 @@ func TestCustomSweepConfTarget(t *testing.T) {
blockEpochChan: blockEpochChan,
timerFactory: timerFactory,
sweeper: sweeper,
cancelSwap: server.CancelLoopOutSwap,
}, ctx.Lnd.Height)
if err != nil {
log.Error(err)
@ -461,6 +466,7 @@ func TestPreimagePush(t *testing.T) {
blockEpochChan: blockEpochChan,
timerFactory: timerFactory,
sweeper: sweeper,
cancelSwap: server.CancelLoopOutSwap,
}, ctx.Lnd.Height)
if err != nil {
log.Error(err)
@ -697,3 +703,140 @@ func TestExpiryBeforeReveal(t *testing.T) {
require.Nil(t, <-errChan)
}
// TestFailedOffChainCancelation tests sending of a cancelation message to
// the server when a swap fails due to off-chain routing.
func TestFailedOffChainCancelation(t *testing.T) {
defer test.Guard(t)()
lnd := test.NewMockLnd()
ctx := test.NewContext(t, lnd)
server := newServerMock(lnd)
testReq := *testRequest
testReq.Expiry = lnd.Height + 20
cfg := newSwapConfig(
&lnd.LndServices, newStoreMock(t), server,
)
initResult, err := newLoopOutSwap(
context.Background(), cfg, lnd.Height, &testReq,
)
require.NoError(t, err)
swap := initResult.swap
// Set up the required dependencies to execute the swap.
sweeper := &sweep.Sweeper{Lnd: &lnd.LndServices}
blockEpochChan := make(chan interface{})
statusChan := make(chan SwapInfo)
expiryChan := make(chan time.Time)
timerFactory := func(_ time.Duration) <-chan time.Time {
return expiryChan
}
errChan := make(chan error)
go func() {
cfg := &executeConfig{
statusChan: statusChan,
sweeper: sweeper,
blockEpochChan: blockEpochChan,
timerFactory: timerFactory,
cancelSwap: server.CancelLoopOutSwap,
}
err := swap.execute(context.Background(), cfg, ctx.Lnd.Height)
errChan <- err
}()
// The swap should be found in its initial state.
cfg.store.(*storeMock).assertLoopOutStored()
state := <-statusChan
require.Equal(t, loopdb.StateInitiated, state.State)
// Assert that we register for htlc confirmation notifications.
ctx.AssertRegisterConf(false, defaultConfirmations)
// We expect prepayment and invoice to be dispatched, order is unknown.
pmt1 := <-ctx.Lnd.RouterSendPaymentChannel
pmt2 := <-ctx.Lnd.RouterSendPaymentChannel
failUpdate := lndclient.PaymentStatus{
State: lnrpc.Payment_FAILED,
FailureReason: lnrpc.PaymentFailureReason_FAILURE_REASON_ERROR,
Htlcs: []*lndclient.HtlcAttempt{
{
// Include a non-failed htlc to test that we
// only report failed htlcs.
Status: lnrpc.HTLCAttempt_IN_FLIGHT,
},
// Add one htlc that failed within the server's
// infrastructure.
{
Status: lnrpc.HTLCAttempt_FAILED,
Route: &lnrpc.Route{
Hops: []*lnrpc.Hop{
{}, {}, {},
},
},
Failure: &lndclient.HtlcFailure{
FailureSourceIndex: 1,
},
},
// Add one htlc that failed in the network at wide.
{
Status: lnrpc.HTLCAttempt_FAILED,
Route: &lnrpc.Route{
Hops: []*lnrpc.Hop{
{}, {}, {}, {}, {},
},
},
Failure: &lndclient.HtlcFailure{
FailureSourceIndex: 1,
},
},
},
}
successUpdate := lndclient.PaymentStatus{
State: lnrpc.Payment_SUCCEEDED,
}
// We want to fail our swap payment and succeed the prepush, so we send
// a failure update to the payment that has the larger amount.
if pmt1.Amount > pmt2.Amount {
pmt1.TrackPaymentMessage.Updates <- failUpdate
pmt2.TrackPaymentMessage.Updates <- successUpdate
} else {
pmt1.TrackPaymentMessage.Updates <- successUpdate
pmt2.TrackPaymentMessage.Updates <- failUpdate
}
invoice, err := zpay32.Decode(
swap.LoopOutContract.SwapInvoice, lnd.ChainParams,
)
require.NoError(t, err)
require.NotNil(t, invoice.PaymentAddr)
swapCancelation := &outCancelDetails{
hash: swap.hash,
paymentAddr: *invoice.PaymentAddr,
metadata: routeCancelMetadata{
paymentType: paymentTypeInvoice,
failureReason: failUpdate.FailureReason,
attempts: []uint32{
2,
math.MaxUint32,
},
},
}
server.assertSwapCanceled(t, swapCancelation)
// Finally, the swap should be recorded with failed off chain timeout.
cfg.store.(*storeMock).assertLoopOutState(
loopdb.StateFailOffchainPayments,
)
state = <-statusChan
require.Equal(t, state.State, loopdb.StateFailOffchainPayments)
require.NoError(t, <-errChan)
}

@ -54,6 +54,8 @@ const (
// The client creates a probe invoice so that the server can perform a
// multi-path probe.
ProtocolVersion_MULTI_LOOP_IN ProtocolVersion = 6
// The client supports loop out swap cancelation.
ProtocolVersion_LOOP_OUT_CANCEL ProtocolVersion = 7
)
var ProtocolVersion_name = map[int32]string{
@ -64,6 +66,7 @@ var ProtocolVersion_name = map[int32]string{
4: "USER_EXPIRY_LOOP_OUT",
5: "HTLC_V2",
6: "MULTI_LOOP_IN",
7: "LOOP_OUT_CANCEL",
}
var ProtocolVersion_value = map[string]int32{
@ -74,6 +77,7 @@ var ProtocolVersion_value = map[string]int32{
"USER_EXPIRY_LOOP_OUT": 4,
"HTLC_V2": 5,
"MULTI_LOOP_IN": 6,
"LOOP_OUT_CANCEL": 7,
}
func (x ProtocolVersion) String() string {
@ -121,6 +125,11 @@ const (
ServerSwapState_UNEXPECTED_FAILURE ServerSwapState = 11
// The swap htlc has confirmed on chain.
ServerSwapState_HTLC_CONFIRMED ServerSwapState = 12
// The client canceled the swap because they could not route the prepay.
ServerSwapState_CLIENT_PREPAY_CANCEL ServerSwapState = 13
// The client canceled the swap because they could not route the swap
// payment.
ServerSwapState_CLIENT_INVOICE_CANCEL ServerSwapState = 14
)
var ServerSwapState_name = map[int32]string{
@ -137,6 +146,8 @@ var ServerSwapState_name = map[int32]string{
10: "TIMEOUT_PUBLISHED",
11: "UNEXPECTED_FAILURE",
12: "HTLC_CONFIRMED",
13: "CLIENT_PREPAY_CANCEL",
14: "CLIENT_INVOICE_CANCEL",
}
var ServerSwapState_value = map[string]int32{
@ -153,6 +164,8 @@ var ServerSwapState_value = map[string]int32{
"TIMEOUT_PUBLISHED": 10,
"UNEXPECTED_FAILURE": 11,
"HTLC_CONFIRMED": 12,
"CLIENT_PREPAY_CANCEL": 13,
"CLIENT_INVOICE_CANCEL": 14,
}
func (x ServerSwapState) String() string {
@ -163,6 +176,92 @@ func (ServerSwapState) EnumDescriptor() ([]byte, []int) {
return fileDescriptor_ad098daeda4239f7, []int{1}
}
type RoutePaymentType int32
const (
// No reason, used to distinguish from the default value.
RoutePaymentType_UNKNOWN RoutePaymentType = 0
// Prepay route indicates that the swap was canceled because the client
// could not find a route to the server for the prepay.
RoutePaymentType_PREPAY_ROUTE RoutePaymentType = 1
// Invoice route indicates that the swap was canceled because the client
// could not find a route to the server for the swap invoice.
RoutePaymentType_INVOICE_ROUTE RoutePaymentType = 2
)
var RoutePaymentType_name = map[int32]string{
0: "UNKNOWN",
1: "PREPAY_ROUTE",
2: "INVOICE_ROUTE",
}
var RoutePaymentType_value = map[string]int32{
"UNKNOWN": 0,
"PREPAY_ROUTE": 1,
"INVOICE_ROUTE": 2,
}
func (x RoutePaymentType) String() string {
return proto.EnumName(RoutePaymentType_name, int32(x))
}
func (RoutePaymentType) EnumDescriptor() ([]byte, []int) {
return fileDescriptor_ad098daeda4239f7, []int{2}
}
// PaymentFailureReason describes the reason that a payment failed. These
// values are copied directly from lnd.
type PaymentFailureReason int32
const (
//
//Payment isn't failed (yet).
PaymentFailureReason_FAILURE_REASON_NONE PaymentFailureReason = 0
//
//There are more routes to try, but the payment timeout was exceeded.
PaymentFailureReason_FAILURE_REASON_TIMEOUT PaymentFailureReason = 1
//
//All possible routes were tried and failed permanently. Or were no
//routes to the destination at all.
PaymentFailureReason_FAILURE_REASON_NO_ROUTE PaymentFailureReason = 2
//
//A non-recoverable error has occured.
PaymentFailureReason_FAILURE_REASON_ERROR PaymentFailureReason = 3
//
//Payment details incorrect (unknown hash, invalid amt or
//invalid final cltv delta)
PaymentFailureReason_FAILURE_REASON_INCORRECT_PAYMENT_DETAILS PaymentFailureReason = 4
//
//Insufficient local balance.
PaymentFailureReason_FAILURE_REASON_INSUFFICIENT_BALANCE PaymentFailureReason = 5
)
var PaymentFailureReason_name = map[int32]string{
0: "FAILURE_REASON_NONE",
1: "FAILURE_REASON_TIMEOUT",
2: "FAILURE_REASON_NO_ROUTE",
3: "FAILURE_REASON_ERROR",
4: "FAILURE_REASON_INCORRECT_PAYMENT_DETAILS",
5: "FAILURE_REASON_INSUFFICIENT_BALANCE",
}
var PaymentFailureReason_value = map[string]int32{
"FAILURE_REASON_NONE": 0,
"FAILURE_REASON_TIMEOUT": 1,
"FAILURE_REASON_NO_ROUTE": 2,
"FAILURE_REASON_ERROR": 3,
"FAILURE_REASON_INCORRECT_PAYMENT_DETAILS": 4,
"FAILURE_REASON_INSUFFICIENT_BALANCE": 5,
}
func (x PaymentFailureReason) String() string {
return proto.EnumName(PaymentFailureReason_name, int32(x))
}
func (PaymentFailureReason) EnumDescriptor() ([]byte, []int) {
return fileDescriptor_ad098daeda4239f7, []int{3}
}
type ServerLoopOutRequest struct {
ReceiverKey []byte `protobuf:"bytes,1,opt,name=receiver_key,json=receiverKey,proto3" json:"receiver_key,omitempty"`
SwapHash []byte `protobuf:"bytes,2,opt,name=swap_hash,json=swapHash,proto3" json:"swap_hash,omitempty"`
@ -1207,9 +1306,236 @@ func (m *SubscribeLoopInUpdatesResponse) GetState() ServerSwapState {
return ServerSwapState_INITIATED
}
type RouteCancel struct {
// The type of the payment that failed.
RouteType RoutePaymentType `protobuf:"varint,1,opt,name=route_type,json=routeType,proto3,enum=looprpc.RoutePaymentType" json:"route_type,omitempty"`
// The htlcs that the client tried to pay the server with, if any.
Attempts []*HtlcAttempt `protobuf:"bytes,2,rep,name=attempts,proto3" json:"attempts,omitempty"`
// The reason that the payment failed.
Failure PaymentFailureReason `protobuf:"varint,3,opt,name=failure,proto3,enum=looprpc.PaymentFailureReason" json:"failure,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *RouteCancel) Reset() { *m = RouteCancel{} }
func (m *RouteCancel) String() string { return proto.CompactTextString(m) }
func (*RouteCancel) ProtoMessage() {}
func (*RouteCancel) Descriptor() ([]byte, []int) {
return fileDescriptor_ad098daeda4239f7, []int{17}
}
func (m *RouteCancel) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_RouteCancel.Unmarshal(m, b)
}
func (m *RouteCancel) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_RouteCancel.Marshal(b, m, deterministic)
}
func (m *RouteCancel) XXX_Merge(src proto.Message) {
xxx_messageInfo_RouteCancel.Merge(m, src)
}
func (m *RouteCancel) XXX_Size() int {
return xxx_messageInfo_RouteCancel.Size(m)
}
func (m *RouteCancel) XXX_DiscardUnknown() {
xxx_messageInfo_RouteCancel.DiscardUnknown(m)
}
var xxx_messageInfo_RouteCancel proto.InternalMessageInfo
func (m *RouteCancel) GetRouteType() RoutePaymentType {
if m != nil {
return m.RouteType
}
return RoutePaymentType_UNKNOWN
}
func (m *RouteCancel) GetAttempts() []*HtlcAttempt {
if m != nil {
return m.Attempts
}
return nil
}
func (m *RouteCancel) GetFailure() PaymentFailureReason {
if m != nil {
return m.Failure
}
return PaymentFailureReason_FAILURE_REASON_NONE
}
type HtlcAttempt struct {
// The number of hops from the htlc's failure hop that it needed to take
// to reach the server's node.
RemainingHops uint32 `protobuf:"varint,1,opt,name=remaining_hops,json=remainingHops,proto3" json:"remaining_hops,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *HtlcAttempt) Reset() { *m = HtlcAttempt{} }
func (m *HtlcAttempt) String() string { return proto.CompactTextString(m) }
func (*HtlcAttempt) ProtoMessage() {}
func (*HtlcAttempt) Descriptor() ([]byte, []int) {
return fileDescriptor_ad098daeda4239f7, []int{18}
}
func (m *HtlcAttempt) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_HtlcAttempt.Unmarshal(m, b)
}
func (m *HtlcAttempt) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_HtlcAttempt.Marshal(b, m, deterministic)
}
func (m *HtlcAttempt) XXX_Merge(src proto.Message) {
xxx_messageInfo_HtlcAttempt.Merge(m, src)
}
func (m *HtlcAttempt) XXX_Size() int {
return xxx_messageInfo_HtlcAttempt.Size(m)
}
func (m *HtlcAttempt) XXX_DiscardUnknown() {
xxx_messageInfo_HtlcAttempt.DiscardUnknown(m)
}
var xxx_messageInfo_HtlcAttempt proto.InternalMessageInfo
func (m *HtlcAttempt) GetRemainingHops() uint32 {
if m != nil {
return m.RemainingHops
}
return 0
}
type CancelLoopOutSwapRequest struct {
// The protocol version that the client adheres to.
ProtocolVersion ProtocolVersion `protobuf:"varint,1,opt,name=protocol_version,json=protocolVersion,proto3,enum=looprpc.ProtocolVersion" json:"protocol_version,omitempty"`
// The swap hash.
SwapHash []byte `protobuf:"bytes,2,opt,name=swap_hash,json=swapHash,proto3" json:"swap_hash,omitempty"`
// The payment address for the swap invoice, used to ensure that only the
// swap owner can cancel the payment.
PaymentAddress []byte `protobuf:"bytes,3,opt,name=payment_address,json=paymentAddress,proto3" json:"payment_address,omitempty"`
// Additional information about the swap cancelation.
//
// Types that are valid to be assigned to CancelInfo:
// *CancelLoopOutSwapRequest_RouteCancel
CancelInfo isCancelLoopOutSwapRequest_CancelInfo `protobuf_oneof:"cancel_info"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *CancelLoopOutSwapRequest) Reset() { *m = CancelLoopOutSwapRequest{} }
func (m *CancelLoopOutSwapRequest) String() string { return proto.CompactTextString(m) }
func (*CancelLoopOutSwapRequest) ProtoMessage() {}
func (*CancelLoopOutSwapRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_ad098daeda4239f7, []int{19}
}
func (m *CancelLoopOutSwapRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_CancelLoopOutSwapRequest.Unmarshal(m, b)
}
func (m *CancelLoopOutSwapRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_CancelLoopOutSwapRequest.Marshal(b, m, deterministic)
}
func (m *CancelLoopOutSwapRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_CancelLoopOutSwapRequest.Merge(m, src)
}
func (m *CancelLoopOutSwapRequest) XXX_Size() int {
return xxx_messageInfo_CancelLoopOutSwapRequest.Size(m)
}
func (m *CancelLoopOutSwapRequest) XXX_DiscardUnknown() {
xxx_messageInfo_CancelLoopOutSwapRequest.DiscardUnknown(m)
}
var xxx_messageInfo_CancelLoopOutSwapRequest proto.InternalMessageInfo
func (m *CancelLoopOutSwapRequest) GetProtocolVersion() ProtocolVersion {
if m != nil {
return m.ProtocolVersion
}
return ProtocolVersion_LEGACY
}
func (m *CancelLoopOutSwapRequest) GetSwapHash() []byte {
if m != nil {
return m.SwapHash
}
return nil
}
func (m *CancelLoopOutSwapRequest) GetPaymentAddress() []byte {
if m != nil {
return m.PaymentAddress
}
return nil
}
type isCancelLoopOutSwapRequest_CancelInfo interface {
isCancelLoopOutSwapRequest_CancelInfo()
}
type CancelLoopOutSwapRequest_RouteCancel struct {
RouteCancel *RouteCancel `protobuf:"bytes,5,opt,name=route_cancel,json=routeCancel,proto3,oneof"`
}
func (*CancelLoopOutSwapRequest_RouteCancel) isCancelLoopOutSwapRequest_CancelInfo() {}
func (m *CancelLoopOutSwapRequest) GetCancelInfo() isCancelLoopOutSwapRequest_CancelInfo {
if m != nil {
return m.CancelInfo
}
return nil
}
func (m *CancelLoopOutSwapRequest) GetRouteCancel() *RouteCancel {
if x, ok := m.GetCancelInfo().(*CancelLoopOutSwapRequest_RouteCancel); ok {
return x.RouteCancel
}
return nil
}
// XXX_OneofWrappers is for the internal use of the proto package.
func (*CancelLoopOutSwapRequest) XXX_OneofWrappers() []interface{} {
return []interface{}{
(*CancelLoopOutSwapRequest_RouteCancel)(nil),
}
}
type CancelLoopOutSwapResponse struct {
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *CancelLoopOutSwapResponse) Reset() { *m = CancelLoopOutSwapResponse{} }
func (m *CancelLoopOutSwapResponse) String() string { return proto.CompactTextString(m) }
func (*CancelLoopOutSwapResponse) ProtoMessage() {}
func (*CancelLoopOutSwapResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_ad098daeda4239f7, []int{20}
}
func (m *CancelLoopOutSwapResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_CancelLoopOutSwapResponse.Unmarshal(m, b)
}
func (m *CancelLoopOutSwapResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_CancelLoopOutSwapResponse.Marshal(b, m, deterministic)
}
func (m *CancelLoopOutSwapResponse) XXX_Merge(src proto.Message) {
xxx_messageInfo_CancelLoopOutSwapResponse.Merge(m, src)
}
func (m *CancelLoopOutSwapResponse) XXX_Size() int {
return xxx_messageInfo_CancelLoopOutSwapResponse.Size(m)
}
func (m *CancelLoopOutSwapResponse) XXX_DiscardUnknown() {
xxx_messageInfo_CancelLoopOutSwapResponse.DiscardUnknown(m)
}
var xxx_messageInfo_CancelLoopOutSwapResponse proto.InternalMessageInfo
func init() {
proto.RegisterEnum("looprpc.ProtocolVersion", ProtocolVersion_name, ProtocolVersion_value)
proto.RegisterEnum("looprpc.ServerSwapState", ServerSwapState_name, ServerSwapState_value)
proto.RegisterEnum("looprpc.RoutePaymentType", RoutePaymentType_name, RoutePaymentType_value)
proto.RegisterEnum("looprpc.PaymentFailureReason", PaymentFailureReason_name, PaymentFailureReason_value)
proto.RegisterType((*ServerLoopOutRequest)(nil), "looprpc.ServerLoopOutRequest")
proto.RegisterType((*ServerLoopOutResponse)(nil), "looprpc.ServerLoopOutResponse")
proto.RegisterType((*ServerLoopOutQuoteRequest)(nil), "looprpc.ServerLoopOutQuoteRequest")
@ -1227,96 +1553,123 @@ func init() {
proto.RegisterType((*SubscribeUpdatesRequest)(nil), "looprpc.SubscribeUpdatesRequest")
proto.RegisterType((*SubscribeLoopOutUpdatesResponse)(nil), "looprpc.SubscribeLoopOutUpdatesResponse")
proto.RegisterType((*SubscribeLoopInUpdatesResponse)(nil), "looprpc.SubscribeLoopInUpdatesResponse")
proto.RegisterType((*RouteCancel)(nil), "looprpc.RouteCancel")
proto.RegisterType((*HtlcAttempt)(nil), "looprpc.HtlcAttempt")
proto.RegisterType((*CancelLoopOutSwapRequest)(nil), "looprpc.CancelLoopOutSwapRequest")
proto.RegisterType((*CancelLoopOutSwapResponse)(nil), "looprpc.CancelLoopOutSwapResponse")
}
func init() { proto.RegisterFile("server.proto", fileDescriptor_ad098daeda4239f7) }
var fileDescriptor_ad098daeda4239f7 = []byte{
// 1336 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x56, 0x4f, 0x73, 0xda, 0x46,
0x14, 0xaf, 0xc4, 0x1f, 0x9b, 0x07, 0xd8, 0xf2, 0x26, 0x71, 0x30, 0x89, 0x53, 0xac, 0x34, 0x29,
0xf5, 0xc1, 0xc9, 0xa4, 0xb7, 0xde, 0x14, 0x90, 0x63, 0x4d, 0xb0, 0xa0, 0x02, 0x9c, 0xe4, 0xb4,
0x5d, 0xe3, 0xad, 0xad, 0x29, 0x48, 0x8a, 0x24, 0x1c, 0x7b, 0x7a, 0xec, 0xe7, 0xe8, 0x4c, 0xa7,
0xc7, 0x5e, 0xda, 0x4b, 0x3f, 0x41, 0x67, 0xfa, 0x0d, 0xfa, 0x7d, 0x3a, 0xbb, 0x5a, 0x81, 0x04,
0xb2, 0x13, 0x77, 0xdc, 0x1b, 0x7a, 0xef, 0xb7, 0xef, 0xcf, 0xef, 0xbd, 0xfd, 0x2d, 0x50, 0x09,
0xa8, 0x7f, 0x4e, 0xfd, 0x3d, 0xcf, 0x77, 0x43, 0x17, 0xad, 0x8c, 0x5d, 0xd7, 0xf3, 0xbd, 0x51,
0xfd, 0xe1, 0xa9, 0xeb, 0x9e, 0x8e, 0xe9, 0x33, 0xe2, 0xd9, 0xcf, 0x88, 0xe3, 0xb8, 0x21, 0x09,
0x6d, 0xd7, 0x09, 0x22, 0x98, 0xfa, 0x8b, 0x0c, 0x77, 0xfb, 0xfc, 0x5c, 0xc7, 0x75, 0xbd, 0xee,
0x34, 0xb4, 0xe8, 0xfb, 0x29, 0x0d, 0x42, 0xb4, 0x03, 0x15, 0x9f, 0x8e, 0xa8, 0x7d, 0x4e, 0x7d,
0xfc, 0x03, 0xbd, 0xac, 0x49, 0x0d, 0xa9, 0x59, 0xb1, 0xca, 0xb1, 0xed, 0x35, 0xbd, 0x44, 0x0f,
0xa0, 0x14, 0x7c, 0x20, 0x1e, 0x3e, 0x23, 0xc1, 0x59, 0x4d, 0xe6, 0xfe, 0x55, 0x66, 0x38, 0x20,
0xc1, 0x19, 0x52, 0x20, 0x47, 0x26, 0x61, 0x2d, 0xd7, 0x90, 0x9a, 0x79, 0x8b, 0xfd, 0x44, 0xdf,
0xc0, 0x16, 0x87, 0x7b, 0xd3, 0xe3, 0xb1, 0x3d, 0xe2, 0x55, 0xe0, 0x13, 0x4a, 0x4e, 0xc6, 0xb6,
0x43, 0x6b, 0xf9, 0x86, 0xd4, 0xcc, 0x59, 0xf7, 0x19, 0xa0, 0x37, 0xf7, 0xb7, 0x85, 0x1b, 0xb5,
0x40, 0xe1, 0xf5, 0x8e, 0xdc, 0x31, 0x3e, 0xa7, 0x7e, 0x60, 0xbb, 0x4e, 0xad, 0xd0, 0x90, 0x9a,
0x6b, 0x2f, 0x6a, 0x7b, 0xa2, 0xd1, 0xbd, 0x9e, 0x00, 0x1c, 0x45, 0x7e, 0x6b, 0xdd, 0x4b, 0x1b,
0xd0, 0x26, 0x14, 0xe9, 0x85, 0x67, 0xfb, 0x97, 0xb5, 0x62, 0x43, 0x6a, 0x16, 0x2c, 0xf1, 0x85,
0xb6, 0x01, 0xa6, 0x01, 0xf5, 0x31, 0x39, 0xa5, 0x4e, 0x58, 0x5b, 0x69, 0x48, 0xcd, 0x92, 0x55,
0x62, 0x16, 0x8d, 0x19, 0xd4, 0xbf, 0x24, 0xb8, 0xb7, 0x40, 0x51, 0xe0, 0xb9, 0x4e, 0x40, 0x19,
0x47, 0xbc, 0x23, 0xdb, 0x39, 0x77, 0xed, 0x11, 0xe5, 0x1c, 0x95, 0xac, 0x32, 0xb3, 0x19, 0x91,
0x09, 0x3d, 0x81, 0x35, 0xcf, 0xa7, 0x1e, 0xb9, 0x9c, 0x81, 0x64, 0x0e, 0xaa, 0x46, 0xd6, 0x18,
0xb6, 0x0d, 0x10, 0x50, 0xe7, 0x44, 0x70, 0x9d, 0xe3, 0x5c, 0x96, 0x22, 0x0b, 0x63, 0xba, 0x3e,
0xab, 0x9c, 0xf1, 0x54, 0x78, 0x29, 0xd7, 0xa4, 0x59, 0xf5, 0x4f, 0x60, 0x2d, 0x1a, 0x3c, 0x9e,
0xd0, 0x20, 0x20, 0xa7, 0x94, 0x13, 0x53, 0xb2, 0xaa, 0x91, 0xf5, 0x30, 0x32, 0xaa, 0x7f, 0x4b,
0xb0, 0x95, 0xea, 0xe2, 0xdb, 0xa9, 0x1b, 0xd2, 0x78, 0xda, 0x62, 0x5a, 0xd2, 0x27, 0x4e, 0x4b,
0xbe, 0xf9, 0xb4, 0x72, 0xff, 0x7d, 0x5a, 0xf9, 0xe4, 0xb4, 0xd4, 0x9f, 0x65, 0x40, 0xcb, 0x8d,
0xa0, 0x5d, 0xd8, 0x88, 0xea, 0x25, 0x97, 0x13, 0xea, 0x84, 0xf8, 0x84, 0x06, 0xa1, 0x18, 0xc8,
0x3a, 0xaf, 0x33, 0xb2, 0xb7, 0x59, 0xb7, 0x5b, 0xc0, 0xf7, 0x14, 0x7f, 0x4f, 0xe3, 0x56, 0x56,
0xd8, 0xf7, 0x3e, 0xa5, 0xe8, 0x29, 0x54, 0x63, 0x17, 0xf6, 0x49, 0x48, 0x79, 0xdd, 0x39, 0x4e,
0x78, 0x59, 0x60, 0x2c, 0x12, 0xf2, 0x81, 0x89, 0xb9, 0x32, 0xde, 0xf2, 0x9c, 0xb7, 0x52, 0x64,
0xd1, 0x26, 0x21, 0xda, 0x85, 0xf5, 0x89, 0xed, 0x60, 0x1e, 0x8a, 0x4c, 0xdc, 0xa9, 0x13, 0xf2,
0xa9, 0xe4, 0x79, 0xa0, 0xea, 0xc4, 0x76, 0xfa, 0x1f, 0x88, 0xa7, 0x71, 0x07, 0xc7, 0x92, 0x8b,
0x14, 0xb6, 0x98, 0xc0, 0x92, 0x8b, 0x04, 0x76, 0x07, 0x60, 0x34, 0x0e, 0xcf, 0xf1, 0x09, 0x1d,
0x87, 0x84, 0xaf, 0x6a, 0xb4, 0x0c, 0x25, 0x66, 0x6d, 0x33, 0xa3, 0xfa, 0xdd, 0xc2, 0x9c, 0x07,
0xd4, 0x9f, 0x04, 0xf1, 0x9c, 0xb3, 0x26, 0x23, 0xdd, 0x70, 0x32, 0xea, 0x1f, 0xd2, 0xc2, 0x04,
0x78, 0x0a, 0xf4, 0x74, 0xb9, 0xe7, 0x68, 0x9f, 0x16, 0xfa, 0x7d, 0xba, 0xdc, 0xaf, 0x2c, 0x70,
0xa9, 0x5e, 0xbf, 0x80, 0x35, 0x16, 0x2f, 0xd1, 0x6f, 0x8e, 0x2f, 0x42, 0x65, 0x62, 0x3b, 0xad,
0xb8, 0x5d, 0x8e, 0x22, 0x17, 0x49, 0x54, 0x5e, 0xa0, 0xc8, 0xc5, 0x0c, 0xa5, 0xfe, 0x26, 0xc3,
0x9d, 0x79, 0xc9, 0x86, 0x13, 0xf3, 0x91, 0xbe, 0x77, 0xd2, 0xe2, 0xbd, 0xbb, 0xa1, 0xc2, 0x2d,
0xea, 0x41, 0x7e, 0x59, 0x0f, 0xb6, 0x60, 0x75, 0x4c, 0x82, 0x10, 0x9f, 0xb9, 0x1e, 0xdf, 0x88,
0x8a, 0xb5, 0xc2, 0xbe, 0x0f, 0x5c, 0x2f, 0x73, 0x36, 0xc5, 0x9b, 0xde, 0x9a, 0xc7, 0x50, 0xf5,
0x7c, 0xf7, 0x98, 0xce, 0x6a, 0x88, 0xe4, 0xac, 0xc2, 0x8d, 0x09, 0xb5, 0x49, 0x08, 0xde, 0xea,
0xa2, 0xe0, 0x5d, 0x24, 0x9f, 0x04, 0xc6, 0xd5, 0x5c, 0xee, 0x3e, 0xf6, 0x24, 0xcc, 0x2f, 0xad,
0x9c, 0x92, 0xd8, 0x65, 0x91, 0xca, 0x65, 0x89, 0xd4, 0x7b, 0xa8, 0x25, 0x33, 0x7f, 0x44, 0xa2,
0xb2, 0x08, 0x93, 0x6f, 0xba, 0xcc, 0xff, 0xa4, 0x74, 0x71, 0x96, 0x53, 0xb4, 0x9c, 0x54, 0x0a,
0xe9, 0x23, 0x4a, 0x21, 0x67, 0x2b, 0x45, 0x86, 0x14, 0xe4, 0x6f, 0x20, 0x05, 0x85, 0xab, 0xa4,
0x60, 0x3b, 0x25, 0x05, 0xd1, 0x8b, 0x96, 0x90, 0x01, 0x9c, 0xa6, 0xf2, 0xf6, 0x55, 0x60, 0x04,
0x1b, 0x4b, 0x09, 0x6e, 0x5b, 0x03, 0xd4, 0x9f, 0x24, 0x68, 0xa4, 0xa4, 0xa6, 0x37, 0x0d, 0xce,
0x7a, 0x3e, 0xb5, 0x27, 0xe4, 0x94, 0xde, 0x66, 0x3b, 0xa8, 0x0e, 0xab, 0x9e, 0x88, 0x1b, 0xdf,
0xf4, 0xf8, 0x5b, 0x7d, 0x0c, 0x3b, 0xd7, 0x14, 0x11, 0xad, 0x8a, 0xfa, 0x23, 0xdc, 0xef, 0x4f,
0x8f, 0x83, 0x91, 0x6f, 0x1f, 0xd3, 0xa1, 0x77, 0x42, 0x42, 0x7a, 0xab, 0x7c, 0x5f, 0xab, 0x45,
0x6a, 0x08, 0x9f, 0xcf, 0x92, 0x8b, 0x22, 0x67, 0x35, 0xcc, 0x6f, 0x6f, 0x68, 0x4f, 0x68, 0x10,
0x92, 0x89, 0x87, 0x9d, 0x40, 0xac, 0x73, 0x79, 0x66, 0x33, 0x03, 0xb4, 0x07, 0x85, 0x20, 0x8c,
0x57, 0x39, 0x59, 0x5c, 0xd4, 0x3d, 0x9b, 0x4b, 0x9f, 0xf9, 0xad, 0x08, 0xa6, 0x06, 0xf0, 0x28,
0x95, 0xd5, 0x70, 0xfe, 0xff, 0xa4, 0xbb, 0xbf, 0x4a, 0xb0, 0xbe, 0x40, 0x16, 0x02, 0x28, 0x76,
0xf4, 0x57, 0x5a, 0xeb, 0x9d, 0xf2, 0x19, 0x42, 0xb0, 0x76, 0x38, 0xec, 0x0c, 0x0c, 0xdc, 0xe9,
0x76, 0x7b, 0xb8, 0x3b, 0x1c, 0x28, 0x12, 0xda, 0x82, 0x7b, 0xa6, 0x36, 0x30, 0x8e, 0x74, 0xdc,
0xd7, 0x5f, 0xbd, 0x31, 0x06, 0x91, 0xcf, 0x30, 0x15, 0x19, 0xd5, 0x61, 0xb3, 0x67, 0xe9, 0xc6,
0xa1, 0xf6, 0x4a, 0xc7, 0xbd, 0x61, 0xff, 0x60, 0x7e, 0x2c, 0x87, 0x6a, 0x70, 0x77, 0xd8, 0xd7,
0x2d, 0xac, 0xbf, 0xed, 0x19, 0xd6, 0xbb, 0xb9, 0x27, 0x8f, 0xca, 0xb0, 0x72, 0x30, 0xe8, 0xb4,
0xf0, 0xd1, 0x0b, 0xa5, 0x80, 0x36, 0xa0, 0x9a, 0xc8, 0x68, 0x98, 0x4a, 0x71, 0xf7, 0x4f, 0x19,
0xd6, 0x17, 0xea, 0x47, 0x55, 0x28, 0x19, 0xa6, 0x31, 0x30, 0xb4, 0x81, 0xde, 0x8e, 0xea, 0xe4,
0x21, 0x7a, 0xc3, 0x97, 0x1d, 0xa3, 0x7f, 0xa0, 0xb7, 0x15, 0x89, 0x85, 0xed, 0x0f, 0x5b, 0x2d,
0xbd, 0xdf, 0x57, 0x64, 0x06, 0xd8, 0xd7, 0x8c, 0x8e, 0xde, 0xc6, 0x43, 0xf3, 0xb5, 0xd9, 0x7d,
0x63, 0x2a, 0xb9, 0x84, 0xcd, 0xec, 0x62, 0x76, 0x5c, 0xc9, 0xa3, 0x47, 0x50, 0x17, 0x36, 0xc3,
0x3c, 0xd2, 0x3a, 0x46, 0x9b, 0x3b, 0xb0, 0x76, 0xd8, 0x1d, 0x9a, 0x03, 0xa5, 0x80, 0x1e, 0x42,
0x4d, 0xf8, 0xbb, 0xfb, 0xfb, 0xb8, 0x75, 0xa0, 0x19, 0x26, 0x1e, 0x18, 0x87, 0x3a, 0xeb, 0xa4,
0x98, 0x88, 0x18, 0xdb, 0x56, 0x58, 0xdf, 0xc2, 0xd6, 0x7f, 0xa3, 0xf5, 0x70, 0x5b, 0xd7, 0xda,
0x1d, 0xc3, 0xd4, 0x95, 0x55, 0xf4, 0x00, 0xee, 0x0b, 0xcf, 0xbc, 0xf6, 0x96, 0x36, 0x30, 0xba,
0xa6, 0x52, 0x42, 0xf7, 0x60, 0x43, 0xc4, 0x48, 0x34, 0x05, 0x68, 0x13, 0xd0, 0xd0, 0xd4, 0xdf,
0xf6, 0xf4, 0xd6, 0x40, 0x6f, 0x63, 0x76, 0x7c, 0x68, 0xe9, 0x4a, 0x79, 0x46, 0x40, 0xab, 0x6b,
0xee, 0x1b, 0xd6, 0xa1, 0xde, 0x56, 0x2a, 0x2f, 0x7e, 0x2f, 0x02, 0x70, 0xc6, 0x38, 0x77, 0xa8,
0x0b, 0x95, 0xd4, 0x5f, 0x0c, 0x75, 0x61, 0x39, 0x32, 0xfe, 0xe2, 0xd4, 0x1f, 0x5c, 0x83, 0x41,
0x5d, 0x58, 0x33, 0xe9, 0x07, 0x61, 0x62, 0x89, 0xd0, 0x76, 0x36, 0x3c, 0x8e, 0xf6, 0xe8, 0x2a,
0xb7, 0x58, 0xf0, 0x31, 0xdc, 0xc9, 0x10, 0x05, 0xf4, 0x55, 0xf6, 0xb1, 0x0c, 0xf5, 0xaa, 0xef,
0x7e, 0x0a, 0x54, 0x64, 0x9b, 0xf3, 0x11, 0xfd, 0xe9, 0xbd, 0x82, 0x8f, 0xe4, 0xbb, 0x79, 0x15,
0x1f, 0x51, 0x80, 0x0e, 0x94, 0x93, 0xf2, 0xbd, 0x93, 0x81, 0x4d, 0xbf, 0x1d, 0xf5, 0xfa, 0xd5,
0x10, 0xd4, 0x81, 0xaa, 0x60, 0xd7, 0xe0, 0x62, 0x8f, 0x1e, 0x66, 0x82, 0xe3, 0x50, 0xdb, 0x57,
0x78, 0x45, 0xb3, 0x83, 0xb8, 0xb6, 0xa8, 0xd4, 0xec, 0xda, 0x52, 0xad, 0xaa, 0xd7, 0x41, 0x44,
0xd4, 0xd3, 0x84, 0x4c, 0xa7, 0x95, 0x12, 0x35, 0xe6, 0xc7, 0xb3, 0x85, 0xbc, 0xde, 0x5c, 0x46,
0x64, 0xab, 0xed, 0x73, 0x09, 0x51, 0xd8, 0xcc, 0x16, 0xc7, 0x4f, 0xc8, 0xf3, 0x65, 0x76, 0x9e,
0x25, 0x7d, 0x7d, 0x2e, 0x1d, 0x17, 0xf9, 0x3b, 0xf1, 0xf5, 0xbf, 0x01, 0x00, 0x00, 0xff, 0xff,
0xbd, 0x37, 0x63, 0xb1, 0xfe, 0x0f, 0x00, 0x00,
// 1712 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x57, 0xcd, 0x6e, 0xdb, 0xda,
0x11, 0x0e, 0x25, 0xd9, 0xb2, 0x46, 0x92, 0x4d, 0x9f, 0xfc, 0xc9, 0x4a, 0x9c, 0x2a, 0x4c, 0x6f,
0xa2, 0x1a, 0x45, 0x6e, 0x90, 0x16, 0xe8, 0xcf, 0x8e, 0xa1, 0xe8, 0x88, 0xb8, 0x32, 0xa9, 0x52,
0x94, 0x73, 0xd3, 0xcd, 0x29, 0x2d, 0x9f, 0xd8, 0x44, 0x25, 0x92, 0x97, 0xa4, 0x1c, 0x1b, 0x5d,
0x76, 0xd9, 0x67, 0x28, 0xd0, 0x75, 0x57, 0x45, 0x37, 0x7d, 0x80, 0x02, 0x7d, 0x83, 0x2e, 0xfa,
0x0a, 0xdd, 0xf4, 0x15, 0x8a, 0xf3, 0x43, 0x89, 0x94, 0xe8, 0x24, 0xbe, 0x30, 0xee, 0x4e, 0x9c,
0xf9, 0xce, 0xfc, 0x7c, 0x33, 0x67, 0xce, 0x08, 0x1a, 0x31, 0x89, 0x2e, 0x48, 0xf4, 0x32, 0x8c,
0x82, 0x24, 0x40, 0xd5, 0x69, 0x10, 0x84, 0x51, 0x38, 0x69, 0x3f, 0x3e, 0x0b, 0x82, 0xb3, 0x29,
0xf9, 0xda, 0x0d, 0xbd, 0xaf, 0x5d, 0xdf, 0x0f, 0x12, 0x37, 0xf1, 0x02, 0x3f, 0xe6, 0x30, 0xe5,
0x2f, 0x25, 0xb8, 0x37, 0x62, 0xe7, 0x06, 0x41, 0x10, 0x5a, 0xf3, 0xc4, 0x26, 0xdf, 0xcd, 0x49,
0x9c, 0xa0, 0xa7, 0xd0, 0x88, 0xc8, 0x84, 0x78, 0x17, 0x24, 0xc2, 0xbf, 0x27, 0x57, 0x2d, 0xa9,
0x23, 0x75, 0x1b, 0x76, 0x3d, 0x95, 0x7d, 0x43, 0xae, 0xd0, 0x23, 0xa8, 0xc5, 0x1f, 0xdd, 0x10,
0x9f, 0xbb, 0xf1, 0x79, 0xab, 0xc4, 0xf4, 0x5b, 0x54, 0xd0, 0x77, 0xe3, 0x73, 0x24, 0x43, 0xd9,
0x9d, 0x25, 0xad, 0x72, 0x47, 0xea, 0x56, 0x6c, 0xfa, 0x13, 0xfd, 0x1a, 0xf6, 0x18, 0x3c, 0x9c,
0x9f, 0x4c, 0xbd, 0x09, 0x8b, 0x02, 0x9f, 0x12, 0xf7, 0x74, 0xea, 0xf9, 0xa4, 0x55, 0xe9, 0x48,
0xdd, 0xb2, 0xfd, 0x90, 0x02, 0x86, 0x4b, 0x7d, 0x4f, 0xa8, 0x91, 0x06, 0x32, 0x8b, 0x77, 0x12,
0x4c, 0xf1, 0x05, 0x89, 0x62, 0x2f, 0xf0, 0x5b, 0x1b, 0x1d, 0xa9, 0xbb, 0xfd, 0xba, 0xf5, 0x52,
0x24, 0xfa, 0x72, 0x28, 0x00, 0xc7, 0x5c, 0x6f, 0xef, 0x84, 0x79, 0x01, 0x7a, 0x00, 0x9b, 0xe4,
0x32, 0xf4, 0xa2, 0xab, 0xd6, 0x66, 0x47, 0xea, 0x6e, 0xd8, 0xe2, 0x0b, 0xed, 0x03, 0xcc, 0x63,
0x12, 0x61, 0xf7, 0x8c, 0xf8, 0x49, 0xab, 0xda, 0x91, 0xba, 0x35, 0xbb, 0x46, 0x25, 0x2a, 0x15,
0x28, 0xff, 0x94, 0xe0, 0xfe, 0x0a, 0x45, 0x71, 0x18, 0xf8, 0x31, 0xa1, 0x1c, 0xb1, 0x8c, 0x3c,
0xff, 0x22, 0xf0, 0x26, 0x84, 0x71, 0x54, 0xb3, 0xeb, 0x54, 0x66, 0x70, 0x11, 0xfa, 0x0a, 0xb6,
0xc3, 0x88, 0x84, 0xee, 0xd5, 0x02, 0x54, 0x62, 0xa0, 0x26, 0x97, 0xa6, 0xb0, 0x7d, 0x80, 0x98,
0xf8, 0xa7, 0x82, 0xeb, 0x32, 0xe3, 0xb2, 0xc6, 0x25, 0x94, 0xe9, 0xf6, 0x22, 0x72, 0xca, 0xd3,
0xc6, 0x9b, 0x52, 0x4b, 0x5a, 0x44, 0xff, 0x15, 0x6c, 0xf3, 0xc2, 0xe3, 0x19, 0x89, 0x63, 0xf7,
0x8c, 0x30, 0x62, 0x6a, 0x76, 0x93, 0x4b, 0x8f, 0xb8, 0x50, 0xf9, 0x97, 0x04, 0x7b, 0xb9, 0x2c,
0x7e, 0x33, 0x0f, 0x12, 0x92, 0x56, 0x5b, 0x54, 0x4b, 0xfa, 0xc2, 0x6a, 0x95, 0x6e, 0x5e, 0xad,
0xf2, 0xf7, 0xaf, 0x56, 0x25, 0x5b, 0x2d, 0xe5, 0xcf, 0x25, 0x40, 0xeb, 0x89, 0xa0, 0x03, 0xd8,
0xe5, 0xf1, 0xba, 0x57, 0x33, 0xe2, 0x27, 0xf8, 0x94, 0xc4, 0x89, 0x28, 0xc8, 0x0e, 0x8b, 0x93,
0xcb, 0x7b, 0x34, 0xdb, 0x3d, 0x60, 0x7d, 0x8a, 0x3f, 0x90, 0x34, 0x95, 0x2a, 0xfd, 0x3e, 0x24,
0x04, 0x3d, 0x87, 0x66, 0xaa, 0xc2, 0x91, 0x9b, 0x10, 0x16, 0x77, 0x99, 0x11, 0x5e, 0x17, 0x18,
0xdb, 0x4d, 0x58, 0xc1, 0x44, 0x5d, 0x29, 0x6f, 0x15, 0xc6, 0x5b, 0x8d, 0x4b, 0xd4, 0x59, 0x82,
0x0e, 0x60, 0x67, 0xe6, 0xf9, 0x98, 0x99, 0x72, 0x67, 0xc1, 0xdc, 0x4f, 0x58, 0x55, 0x2a, 0xcc,
0x50, 0x73, 0xe6, 0xf9, 0xa3, 0x8f, 0x6e, 0xa8, 0x32, 0x05, 0xc3, 0xba, 0x97, 0x39, 0xec, 0x66,
0x06, 0xeb, 0x5e, 0x66, 0xb0, 0x4f, 0x01, 0x26, 0xd3, 0xe4, 0x02, 0x9f, 0x92, 0x69, 0xe2, 0xb2,
0x56, 0xe5, 0xcd, 0x50, 0xa3, 0xd2, 0x1e, 0x15, 0x2a, 0xbf, 0x5b, 0xa9, 0xb3, 0x43, 0xa2, 0x59,
0x9c, 0xd6, 0xb9, 0xa8, 0x32, 0xd2, 0x0d, 0x2b, 0xa3, 0xfc, 0x4d, 0x5a, 0xa9, 0x00, 0x73, 0x81,
0x9e, 0xaf, 0xe7, 0xcc, 0xfb, 0x69, 0x25, 0xdf, 0xe7, 0xeb, 0xf9, 0x96, 0x04, 0x2e, 0x97, 0xeb,
0x8f, 0x61, 0x9b, 0xda, 0xcb, 0xe4, 0x5b, 0x66, 0x8d, 0xd0, 0x98, 0x79, 0xbe, 0x96, 0xa6, 0xcb,
0x50, 0xee, 0x65, 0x16, 0x55, 0x11, 0x28, 0xf7, 0x72, 0x81, 0x52, 0xfe, 0x5a, 0x82, 0xbb, 0xcb,
0x90, 0x0d, 0x3f, 0xe5, 0x23, 0x7f, 0xef, 0xa4, 0xd5, 0x7b, 0x77, 0xc3, 0x09, 0xb7, 0x3a, 0x0f,
0x2a, 0xeb, 0xf3, 0x60, 0x0f, 0xb6, 0xa6, 0x6e, 0x9c, 0xe0, 0xf3, 0x20, 0x64, 0x1d, 0xd1, 0xb0,
0xab, 0xf4, 0xbb, 0x1f, 0x84, 0x85, 0xb5, 0xd9, 0xbc, 0xe9, 0xad, 0x79, 0x06, 0xcd, 0x30, 0x0a,
0x4e, 0xc8, 0x22, 0x06, 0x3e, 0xce, 0x1a, 0x4c, 0x98, 0x99, 0x36, 0x99, 0x81, 0xb7, 0xb5, 0x3a,
0xf0, 0x2e, 0xb3, 0x4f, 0x02, 0xe5, 0x6a, 0x39, 0xee, 0x3e, 0xf7, 0x24, 0x2c, 0x2f, 0x6d, 0x29,
0x37, 0x62, 0xd7, 0x87, 0x54, 0xb9, 0x68, 0x48, 0x7d, 0x07, 0xad, 0xac, 0xe7, 0xcf, 0x8c, 0xa8,
0x22, 0xc2, 0x4a, 0x37, 0x6d, 0xe6, 0x7f, 0xe7, 0xe6, 0xe2, 0xc2, 0xa7, 0x48, 0x39, 0x3b, 0x29,
0xa4, 0xcf, 0x4c, 0x8a, 0x52, 0xf1, 0xa4, 0x28, 0x18, 0x05, 0x95, 0x1b, 0x8c, 0x82, 0x8d, 0xeb,
0x46, 0xc1, 0x7e, 0x6e, 0x14, 0xf0, 0x17, 0x2d, 0x33, 0x06, 0x70, 0x9e, 0xca, 0xdb, 0x9f, 0x02,
0x13, 0xd8, 0x5d, 0x73, 0x70, 0xdb, 0x33, 0x40, 0xf9, 0xa3, 0x04, 0x9d, 0xdc, 0xa8, 0x19, 0xce,
0xe3, 0xf3, 0x61, 0x44, 0xbc, 0x99, 0x7b, 0x46, 0x6e, 0x33, 0x1d, 0xd4, 0x86, 0xad, 0x50, 0xd8,
0x4d, 0x6f, 0x7a, 0xfa, 0xad, 0x3c, 0x83, 0xa7, 0x9f, 0x08, 0x82, 0xb7, 0x8a, 0xf2, 0x07, 0x78,
0x38, 0x9a, 0x9f, 0xc4, 0x93, 0xc8, 0x3b, 0x21, 0xe3, 0xf0, 0xd4, 0x4d, 0xc8, 0xad, 0xf2, 0xfd,
0xc9, 0x59, 0xa4, 0x24, 0xf0, 0xa3, 0x85, 0x73, 0x11, 0xe4, 0x22, 0x86, 0xe5, 0xed, 0x4d, 0xbc,
0x19, 0x89, 0x13, 0x77, 0x16, 0x62, 0x3f, 0x16, 0xed, 0x5c, 0x5f, 0xc8, 0xcc, 0x18, 0xbd, 0x84,
0x8d, 0x38, 0x49, 0x5b, 0x39, 0x1b, 0x1c, 0xcf, 0x9e, 0xd6, 0x65, 0x44, 0xf5, 0x36, 0x87, 0x29,
0x31, 0x3c, 0xc9, 0x79, 0x35, 0xfc, 0x1f, 0xc0, 0xe9, 0xdf, 0x25, 0xa8, 0xdb, 0xc1, 0x3c, 0x21,
0x9a, 0xeb, 0x4f, 0xc8, 0x14, 0xfd, 0x12, 0x20, 0xa2, 0x9f, 0x38, 0xb9, 0x0a, 0x89, 0xa0, 0x75,
0x6f, 0x61, 0x84, 0x21, 0xc5, 0xdb, 0xef, 0x5c, 0x85, 0xc4, 0xae, 0x31, 0x30, 0xfd, 0x89, 0x5e,
0xc1, 0x96, 0x9b, 0x24, 0x64, 0x16, 0x26, 0x71, 0xab, 0xd4, 0x29, 0x77, 0xeb, 0xaf, 0xef, 0x2d,
0xce, 0xf5, 0x93, 0xe9, 0x44, 0xe5, 0x4a, 0x7b, 0x81, 0x42, 0xbf, 0x80, 0xea, 0x07, 0xd7, 0x9b,
0xce, 0x23, 0x22, 0xf6, 0x99, 0xfd, 0x65, 0xfd, 0xb8, 0x8f, 0x43, 0xae, 0xb6, 0x89, 0x1b, 0x07,
0xbe, 0x9d, 0xa2, 0x95, 0x9f, 0x43, 0x3d, 0x63, 0x91, 0x8e, 0xc3, 0x88, 0xcc, 0x5c, 0xcf, 0xf7,
0xfc, 0x33, 0xfa, 0x14, 0x70, 0x62, 0x9a, 0x76, 0x73, 0x21, 0xed, 0x07, 0x61, 0xac, 0xfc, 0x4f,
0x82, 0x16, 0xcf, 0x52, 0xd4, 0x94, 0x92, 0xf1, 0x83, 0x35, 0x15, 0x7a, 0x01, 0x3b, 0xe9, 0x36,
0xe5, 0x9e, 0x9e, 0x46, 0x24, 0x8e, 0xc5, 0x66, 0xba, 0x2d, 0xc4, 0x2a, 0x97, 0xa2, 0x5f, 0x41,
0x83, 0x97, 0x60, 0xc2, 0x82, 0x65, 0x33, 0x2b, 0x4b, 0x66, 0xa6, 0x5c, 0xfd, 0x3b, 0x76, 0x3d,
0x5a, 0x7e, 0xbe, 0x69, 0x42, 0x9d, 0x1f, 0xc2, 0x9e, 0xff, 0x21, 0x50, 0x1e, 0xc1, 0x5e, 0x41,
0xc2, 0xbc, 0x99, 0x0e, 0xfe, 0x21, 0xc1, 0xce, 0x4a, 0x46, 0x08, 0x60, 0x73, 0xa0, 0xbf, 0x55,
0xb5, 0xf7, 0xf2, 0x1d, 0x84, 0x60, 0xfb, 0x68, 0x3c, 0x70, 0x0c, 0x3c, 0xb0, 0xac, 0x21, 0xb6,
0xc6, 0x8e, 0x2c, 0xa1, 0x3d, 0xb8, 0x6f, 0xaa, 0x8e, 0x71, 0xac, 0xe3, 0x91, 0xfe, 0xf6, 0x9d,
0xe1, 0x70, 0x9d, 0x61, 0xca, 0x25, 0xd4, 0x86, 0x07, 0x43, 0x5b, 0x37, 0x8e, 0xd4, 0xb7, 0x3a,
0x1e, 0x8e, 0x47, 0xfd, 0xe5, 0xb1, 0x32, 0x6a, 0xc1, 0xbd, 0xf1, 0x48, 0xb7, 0xb1, 0xfe, 0xed,
0xd0, 0xb0, 0xdf, 0x2f, 0x35, 0x15, 0x54, 0x87, 0x6a, 0xdf, 0x19, 0x68, 0xf8, 0xf8, 0xb5, 0xbc,
0x81, 0x76, 0xa1, 0x99, 0xf1, 0x68, 0x98, 0xf2, 0x26, 0xba, 0x0b, 0x3b, 0x29, 0x1a, 0x6b, 0xaa,
0xa9, 0xe9, 0x03, 0xb9, 0x7a, 0xf0, 0xdf, 0x12, 0xec, 0xac, 0xb4, 0x33, 0x6a, 0x42, 0xcd, 0x30,
0x0d, 0xc7, 0x50, 0x1d, 0xbd, 0xc7, 0x83, 0x67, 0x76, 0x87, 0xe3, 0x37, 0x03, 0x63, 0xd4, 0xd7,
0x7b, 0xb2, 0x44, 0x7d, 0x8d, 0xc6, 0x9a, 0xa6, 0x8f, 0x46, 0x72, 0x89, 0x02, 0x0e, 0x55, 0x63,
0xa0, 0xf7, 0xf0, 0xd8, 0xfc, 0xc6, 0xb4, 0xde, 0x99, 0x72, 0x39, 0x23, 0x33, 0x2d, 0x4c, 0x8f,
0xcb, 0x15, 0xf4, 0x04, 0xda, 0x42, 0x66, 0x98, 0xc7, 0xea, 0xc0, 0xe8, 0x31, 0x05, 0x56, 0x8f,
0xac, 0xb1, 0xe9, 0xc8, 0x1b, 0xe8, 0x31, 0xb4, 0x84, 0xde, 0x3a, 0x3c, 0xc4, 0x5a, 0x5f, 0x35,
0x4c, 0xec, 0x18, 0x47, 0x3a, 0x4d, 0x6f, 0x33, 0x63, 0x31, 0x95, 0x55, 0x29, 0x19, 0x42, 0x36,
0x7a, 0xa7, 0x0e, 0x71, 0x4f, 0x57, 0x7b, 0x03, 0xc3, 0xd4, 0xe5, 0x2d, 0xf4, 0x08, 0x1e, 0x0a,
0xcd, 0x32, 0x76, 0x4d, 0x75, 0x0c, 0xcb, 0x94, 0x6b, 0xe8, 0x3e, 0xec, 0x0a, 0x1b, 0x99, 0xa4,
0x00, 0x3d, 0x00, 0x34, 0x36, 0xf5, 0x6f, 0x87, 0xba, 0xe6, 0xe8, 0x3d, 0x4c, 0x8f, 0x8f, 0x6d,
0x5d, 0xae, 0x2f, 0x08, 0xd0, 0x2c, 0xf3, 0xd0, 0xb0, 0x8f, 0xf4, 0x9e, 0xdc, 0xa0, 0x9e, 0xb5,
0x81, 0xa1, 0x9b, 0x0e, 0x1e, 0xda, 0xfa, 0x50, 0x7d, 0x9f, 0x32, 0xda, 0xa4, 0x75, 0x15, 0x1a,
0xc3, 0x3c, 0xb6, 0x0c, 0x4d, 0x4f, 0x55, 0xdb, 0x07, 0x3d, 0x90, 0x57, 0x6f, 0x3d, 0x65, 0x32,
0x65, 0xed, 0x0e, 0x92, 0xa1, 0x21, 0xcc, 0xd9, 0xd6, 0xd8, 0xd1, 0x65, 0x89, 0xd6, 0x31, 0x35,
0xc3, 0x45, 0xa5, 0x83, 0xff, 0x48, 0x70, 0xaf, 0xe8, 0x4e, 0xa3, 0x87, 0x70, 0x57, 0x04, 0x8d,
0x6d, 0x5d, 0x1d, 0x59, 0x26, 0x36, 0x2d, 0x53, 0x97, 0xef, 0xd0, 0x7e, 0x5a, 0x51, 0xa4, 0x14,
0x4a, 0x29, 0x51, 0xb9, 0x43, 0xa9, 0xab, 0x94, 0xdf, 0x8c, 0x52, 0xb7, 0x6d, 0xcb, 0x96, 0xcb,
0xe8, 0xa7, 0xd0, 0x5d, 0xd1, 0x18, 0xa6, 0x66, 0xd9, 0xb6, 0xae, 0x39, 0x78, 0xa8, 0xbe, 0x3f,
0xa2, 0xf9, 0xf7, 0x74, 0x47, 0x35, 0x06, 0x23, 0xb9, 0x82, 0x5e, 0xc0, 0xb3, 0x35, 0xf4, 0x68,
0x7c, 0x78, 0x68, 0x68, 0x8c, 0xa8, 0x37, 0xea, 0x80, 0x52, 0x24, 0x6f, 0xbc, 0xfe, 0x53, 0x15,
0x80, 0x35, 0x22, 0x6b, 0x49, 0x64, 0x41, 0x23, 0xb7, 0xc8, 0x2b, 0x2b, 0x23, 0xb8, 0xe0, 0x8f,
0x44, 0xfb, 0xd1, 0x27, 0x30, 0xc8, 0x82, 0x6d, 0x93, 0x7c, 0xcc, 0x5c, 0x61, 0xb4, 0x5f, 0x0c,
0x4f, 0xad, 0x3d, 0xb9, 0x4e, 0x2d, 0x9e, 0x91, 0x29, 0xdc, 0x2d, 0x78, 0x7a, 0xd1, 0x4f, 0x8a,
0x8f, 0x15, 0xec, 0x08, 0xed, 0x83, 0x2f, 0x81, 0x0a, 0x6f, 0x4b, 0x3e, 0xf8, 0x5f, 0xcb, 0x6b,
0xf8, 0xc8, 0x6e, 0xa7, 0xd7, 0xf1, 0xc1, 0x0d, 0x0c, 0xa0, 0x9e, 0x5d, 0x92, 0x9e, 0x16, 0x60,
0xf3, 0x1b, 0x5a, 0xbb, 0x7d, 0x3d, 0x04, 0x0d, 0xa0, 0x29, 0xd8, 0x35, 0xd8, 0x4a, 0x85, 0x1e,
0x17, 0x82, 0x53, 0x53, 0xfb, 0xd7, 0x68, 0x45, 0xb2, 0x4e, 0x1a, 0x1b, 0x0f, 0xb5, 0x38, 0xb6,
0x5c, 0xaa, 0xca, 0xa7, 0x20, 0xc2, 0xea, 0x59, 0x66, 0x19, 0xca, 0xef, 0x23, 0xa8, 0xb3, 0x3c,
0x5e, 0xbc, 0x2e, 0xb5, 0xbb, 0xeb, 0x88, 0xe2, 0x9d, 0xe6, 0x95, 0x84, 0x08, 0x3c, 0x28, 0x5e,
0x41, 0xbe, 0xc0, 0xcf, 0x8b, 0x62, 0x3f, 0x6b, 0x5b, 0xcc, 0x2b, 0x09, 0xfd, 0x16, 0x76, 0xd7,
0xde, 0xa5, 0x0c, 0x57, 0xd7, 0x3d, 0xd2, 0x19, 0xae, 0xae, 0x7d, 0xd6, 0x4e, 0x36, 0xd9, 0xa3,
0xfc, 0xb3, 0xff, 0x07, 0x00, 0x00, 0xff, 0xff, 0xac, 0x6e, 0xf1, 0xeb, 0xc0, 0x13, 0x00, 0x00,
}
// Reference imports to suppress errors if they are not otherwise used.
@ -1340,6 +1693,7 @@ type SwapServerClient interface {
LoopInQuote(ctx context.Context, in *ServerLoopInQuoteRequest, opts ...grpc.CallOption) (*ServerLoopInQuoteResponse, error)
SubscribeLoopOutUpdates(ctx context.Context, in *SubscribeUpdatesRequest, opts ...grpc.CallOption) (SwapServer_SubscribeLoopOutUpdatesClient, error)
SubscribeLoopInUpdates(ctx context.Context, in *SubscribeUpdatesRequest, opts ...grpc.CallOption) (SwapServer_SubscribeLoopInUpdatesClient, error)
CancelLoopOutSwap(ctx context.Context, in *CancelLoopOutSwapRequest, opts ...grpc.CallOption) (*CancelLoopOutSwapResponse, error)
}
type swapServerClient struct {
@ -1477,6 +1831,15 @@ func (x *swapServerSubscribeLoopInUpdatesClient) Recv() (*SubscribeLoopInUpdates
return m, nil
}
func (c *swapServerClient) CancelLoopOutSwap(ctx context.Context, in *CancelLoopOutSwapRequest, opts ...grpc.CallOption) (*CancelLoopOutSwapResponse, error) {
out := new(CancelLoopOutSwapResponse)
err := c.cc.Invoke(ctx, "/looprpc.SwapServer/CancelLoopOutSwap", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// SwapServerServer is the server API for SwapServer service.
type SwapServerServer interface {
LoopOutTerms(context.Context, *ServerLoopOutTermsRequest) (*ServerLoopOutTerms, error)
@ -1488,6 +1851,7 @@ type SwapServerServer interface {
LoopInQuote(context.Context, *ServerLoopInQuoteRequest) (*ServerLoopInQuoteResponse, error)
SubscribeLoopOutUpdates(*SubscribeUpdatesRequest, SwapServer_SubscribeLoopOutUpdatesServer) error
SubscribeLoopInUpdates(*SubscribeUpdatesRequest, SwapServer_SubscribeLoopInUpdatesServer) error
CancelLoopOutSwap(context.Context, *CancelLoopOutSwapRequest) (*CancelLoopOutSwapResponse, error)
}
// UnimplementedSwapServerServer can be embedded to have forward compatible implementations.
@ -1521,6 +1885,9 @@ func (*UnimplementedSwapServerServer) SubscribeLoopOutUpdates(req *SubscribeUpda
func (*UnimplementedSwapServerServer) SubscribeLoopInUpdates(req *SubscribeUpdatesRequest, srv SwapServer_SubscribeLoopInUpdatesServer) error {
return status.Errorf(codes.Unimplemented, "method SubscribeLoopInUpdates not implemented")
}
func (*UnimplementedSwapServerServer) CancelLoopOutSwap(ctx context.Context, req *CancelLoopOutSwapRequest) (*CancelLoopOutSwapResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method CancelLoopOutSwap not implemented")
}
func RegisterSwapServerServer(s *grpc.Server, srv SwapServerServer) {
s.RegisterService(&_SwapServer_serviceDesc, srv)
@ -1694,6 +2061,24 @@ func (x *swapServerSubscribeLoopInUpdatesServer) Send(m *SubscribeLoopInUpdatesR
return x.ServerStream.SendMsg(m)
}
func _SwapServer_CancelLoopOutSwap_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(CancelLoopOutSwapRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(SwapServerServer).CancelLoopOutSwap(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/looprpc.SwapServer/CancelLoopOutSwap",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(SwapServerServer).CancelLoopOutSwap(ctx, req.(*CancelLoopOutSwapRequest))
}
return interceptor(ctx, in, info, handler)
}
var _SwapServer_serviceDesc = grpc.ServiceDesc{
ServiceName: "looprpc.SwapServer",
HandlerType: (*SwapServerServer)(nil),
@ -1726,6 +2111,10 @@ var _SwapServer_serviceDesc = grpc.ServiceDesc{
MethodName: "LoopInQuote",
Handler: _SwapServer_LoopInQuote_Handler,
},
{
MethodName: "CancelLoopOutSwap",
Handler: _SwapServer_CancelLoopOutSwap_Handler,
},
},
Streams: []grpc.StreamDesc{
{

@ -26,6 +26,9 @@ service SwapServer {
rpc SubscribeLoopInUpdates (SubscribeUpdatesRequest)
returns (stream SubscribeLoopInUpdatesResponse);
rpc CancelLoopOutSwap(CancelLoopOutSwapRequest)
returns (CancelLoopOutSwapResponse);
}
/**
@ -64,6 +67,9 @@ enum ProtocolVersion {
// The client creates a probe invoice so that the server can perform a
// multi-path probe.
MULTI_LOOP_IN = 6;
// The client supports loop out swap cancelation.
LOOP_OUT_CANCEL = 7;
}
message ServerLoopOutRequest {
@ -292,6 +298,14 @@ enum ServerSwapState {
// The swap htlc has confirmed on chain.
HTLC_CONFIRMED = 12;
// The client canceled the swap because they could not route the prepay.
CLIENT_PREPAY_CANCEL = 13;
// The client canceled the swap because they could not route the swap
// payment.
CLIENT_INVOICE_CANCEL = 14;
}
message SubscribeLoopOutUpdatesResponse{
@ -309,3 +323,88 @@ message SubscribeLoopInUpdatesResponse{
// The swap's current state.
ServerSwapState state = 2;
}
enum RoutePaymentType {
// No reason, used to distinguish from the default value.
UNKNOWN = 0;
// Prepay route indicates that the swap was canceled because the client
// could not find a route to the server for the prepay.
PREPAY_ROUTE = 1;
// Invoice route indicates that the swap was canceled because the client
// could not find a route to the server for the swap invoice.
INVOICE_ROUTE = 2;
}
// PaymentFailureReason describes the reason that a payment failed. These
// values are copied directly from lnd.
enum PaymentFailureReason {
/*
Payment isn't failed (yet).
*/
FAILURE_REASON_NONE = 0;
/*
There are more routes to try, but the payment timeout was exceeded.
*/
FAILURE_REASON_TIMEOUT = 1;
/*
All possible routes were tried and failed permanently. Or were no
routes to the destination at all.
*/
FAILURE_REASON_NO_ROUTE = 2;
/*
A non-recoverable error has occured.
*/
FAILURE_REASON_ERROR = 3;
/*
Payment details incorrect (unknown hash, invalid amt or
invalid final cltv delta)
*/
FAILURE_REASON_INCORRECT_PAYMENT_DETAILS = 4;
/*
Insufficient local balance.
*/
FAILURE_REASON_INSUFFICIENT_BALANCE = 5;
}
message RouteCancel {
// The type of the payment that failed.
RoutePaymentType route_type = 1;
// The htlcs that the client tried to pay the server with, if any.
repeated HtlcAttempt attempts = 2;
// The reason that the payment failed.
PaymentFailureReason failure = 3;
}
message HtlcAttempt {
// The number of hops from the htlc's failure hop that it needed to take
// to reach the server's node.
uint32 remaining_hops = 1;
}
message CancelLoopOutSwapRequest {
// The protocol version that the client adheres to.
ProtocolVersion protocol_version = 1;
// The swap hash.
bytes swap_hash = 2;
// The payment address for the swap invoice, used to ensure that only the
// swap owner can cancel the payment.
bytes payment_address = 3;
// Additional information about the swap cancelation.
oneof cancel_info {
RouteCancel route_cancel = 5;
}
}
message CancelLoopOutSwapResponse{}

@ -15,6 +15,14 @@ This file tracks release notes for the loop client.
## Next release
#### New Features
- The loopd client reports off-chain routing failures for loop out swaps if
it cannot find a route to the server for the swap's prepay or invoice payment.
This allows the server to release accepted invoices, if there are any,
earlier, reducing the amount of time that funds are held off-chain. If the
swap failed on one of the loop server's channels, it will report failure
location of its off-chain failure. If the failure occurred outside of the
loop server's infrastructure, a generic failure will be used so that no
information about the client's position in the network is leaked.
#### Breaking Changes

@ -3,6 +3,7 @@ package loop
import (
"context"
"errors"
"testing"
"time"
@ -15,6 +16,7 @@ import (
"github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/routing/route"
"github.com/lightningnetwork/lnd/zpay32"
"github.com/stretchr/testify/require"
)
var (
@ -43,6 +45,9 @@ type serverMock struct {
// preimagePush is a channel that preimage pushes are sent into.
preimagePush chan lntypes.Preimage
// cancelSwap is a channel that swap cancelations are sent into.
cancelSwap chan *outCancelDetails
lnd *test.LndMockServices
}
@ -59,6 +64,7 @@ func newServerMock(lnd *test.LndMockServices) *serverMock {
height: 600,
preimagePush: make(chan lntypes.Preimage),
cancelSwap: make(chan *outCancelDetails),
lnd: lnd,
}
@ -120,10 +126,17 @@ func (s *serverMock) GetLoopOutQuote(ctx context.Context, amt btcutil.Amount,
}
func getInvoice(hash lntypes.Hash, amt btcutil.Amount, memo string) (string, error) {
// Set different payment addresses for swap invoices.
payAddr := [32]byte{1, 2, 3}
if memo == swapInvoiceDesc {
payAddr = [32]byte{3, 2, 1}
}
req, err := zpay32.NewInvoice(
&chaincfg.TestNet3Params, hash, testTime,
zpay32.Description(memo),
zpay32.Amount(lnwire.MilliSatoshi(1000*amt)),
zpay32.PaymentAddr(payAddr),
)
if err != nil {
return "", err
@ -178,6 +191,18 @@ func (s *serverMock) PushLoopOutPreimage(_ context.Context,
return nil
}
// CancelLoopOutSwap pushes a request to cancel a swap into our mock's channel.
func (s *serverMock) CancelLoopOutSwap(ctx context.Context,
details *outCancelDetails) error {
s.cancelSwap <- details
return nil
}
func (s *serverMock) assertSwapCanceled(t *testing.T, details *outCancelDetails) {
require.Equal(t, details, <-s.cancelSwap)
}
func (s *serverMock) GetLoopInTerms(ctx context.Context) (
*LoopInTerms, error) {

@ -17,6 +17,7 @@ import (
"github.com/lightninglabs/aperture/lsat"
"github.com/lightninglabs/loop/loopdb"
"github.com/lightninglabs/loop/looprpc"
"github.com/lightningnetwork/lnd/lnrpc"
"github.com/lightningnetwork/lnd/lntypes"
"github.com/lightningnetwork/lnd/routing/route"
"github.com/lightningnetwork/lnd/tor"
@ -74,6 +75,10 @@ type swapServerClient interface {
// SubscribeLoopInUpdates subscribes to loop in server state.
SubscribeLoopInUpdates(ctx context.Context,
hash lntypes.Hash) (<-chan *ServerUpdate, <-chan error, error)
// CancelLoopOutSwap cancels a loop out swap.
CancelLoopOutSwap(ctx context.Context,
details *outCancelDetails) error
}
type grpcSwapServerClient struct {
@ -456,6 +461,104 @@ func (s *grpcSwapServerClient) makeServerUpdate(ctx context.Context,
return updateChan, errChan
}
// paymentType is an enum representing different types of off-chain payments
// made by a swap.
type paymentType uint8
const (
// paymentTypePrepay indicates that we could not route the prepay.
paymentTypePrepay paymentType = iota
// paymentTypeInvoice indicates that we could not route the swap
// invoice.
paymentTypeInvoice
)
// routeCancelMetadata contains cancelation information for swaps that are
// canceled because the client could not route off-chain to the server.
type routeCancelMetadata struct {
// paymentType is the type of payment that failed.
paymentType paymentType
// attempts is the set of htlc attempts made by the client, reporting
// the distance from the invoice's destination node that a failure
// occurred.
attempts []uint32
// failureReason is the reason that the payment failed.
failureReason lnrpc.PaymentFailureReason
}
// outCancelDetails contains the informaton required to cancel a loop out swap.
type outCancelDetails struct {
// Hash is the swap's hash.
hash lntypes.Hash
// paymentAddr is the payment address for the swap's invoice.
paymentAddr [32]byte
// metadata contains additional information about the swap.
metadata routeCancelMetadata
}
// CancelLoopOutSwap sends an instruction to the server to cancel a loop out
// swap.
func (s *grpcSwapServerClient) CancelLoopOutSwap(ctx context.Context,
details *outCancelDetails) error {
req := &looprpc.CancelLoopOutSwapRequest{
ProtocolVersion: loopdb.CurrentRPCProtocolVersion,
SwapHash: details.hash[:],
PaymentAddress: details.paymentAddr[:],
}
var err error
req.CancelInfo, err = rpcRouteCancel(details)
if err != nil {
return err
}
_, err = s.server.CancelLoopOutSwap(ctx, req)
return err
}
func rpcRouteCancel(details *outCancelDetails) (
*looprpc.CancelLoopOutSwapRequest_RouteCancel, error) {
attempts := make([]*looprpc.HtlcAttempt, len(details.metadata.attempts))
for i, remaining := range details.metadata.attempts {
attempts[i] = &looprpc.HtlcAttempt{
RemainingHops: remaining,
}
}
resp := &looprpc.CancelLoopOutSwapRequest_RouteCancel{
RouteCancel: &looprpc.RouteCancel{
Attempts: attempts,
// We can cast our lnd failure reason to a loop payment
// failure reason because these values are copied 1:1
// from lnd.
Failure: looprpc.PaymentFailureReason(
details.metadata.failureReason,
),
},
}
switch details.metadata.paymentType {
case paymentTypePrepay:
resp.RouteCancel.RouteType = looprpc.RoutePaymentType_PREPAY_ROUTE
case paymentTypeInvoice:
resp.RouteCancel.RouteType = looprpc.RoutePaymentType_INVOICE_ROUTE
default:
return nil, fmt.Errorf("unknown payment type: %v",
details.metadata.paymentType)
}
return resp, nil
}
// getSwapServerConn returns a connection to the swap server. A non-empty
// proxyAddr indicates that a SOCKS proxy found at the address should be used to
// establish the connection.

@ -52,6 +52,7 @@ func newSwapClient(config *clientConfig) *Client {
store: config.Store,
sweeper: sweeper,
createExpiryTimer: config.CreateExpiryTimer,
cancelSwap: config.Server.CancelLoopOutSwap,
})
return &Client{

Loading…
Cancel
Save