From 969e30024153926b57176323cb945d068981978e Mon Sep 17 00:00:00 2001 From: carla Date: Mon, 24 May 2021 08:40:13 +0200 Subject: [PATCH] loop: add cancel swap to server interface --- server_mock_test.go | 12 +++++ swap_server_client.go | 103 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 115 insertions(+) diff --git a/server_mock_test.go b/server_mock_test.go index 9aef6ff..9e53a7f 100644 --- a/server_mock_test.go +++ b/server_mock_test.go @@ -43,6 +43,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 +62,7 @@ func newServerMock(lnd *test.LndMockServices) *serverMock { height: 600, preimagePush: make(chan lntypes.Preimage), + cancelSwap: make(chan *outCancelDetails), lnd: lnd, } @@ -178,6 +182,14 @@ 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) GetLoopInTerms(ctx context.Context) ( *LoopInTerms, error) { diff --git a/swap_server_client.go b/swap_server_client.go index c79f795..b40cef8 100644 --- a/swap_server_client.go +++ b/swap_server_client.go @@ -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.