/
loopin_builder.go
131 lines (108 loc) · 3.64 KB
/
loopin_builder.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
package liquidity
import (
"context"
"github.com/btcsuite/btcd/btcutil"
"github.com/lightninglabs/loop"
"github.com/lightninglabs/loop/labels"
"github.com/lightninglabs/loop/swap"
"github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/routing/route"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
// Compile-time assertion that loopInBuilder satisfies the swapBuilder
// interface.
var _ swapBuilder = (*loopInBuilder)(nil)
func newLoopInBuilder(cfg *Config) *loopInBuilder {
return &loopInBuilder{
cfg: cfg,
}
}
type loopInBuilder struct {
// cfg contains all the external functionality we require to create
// swaps.
cfg *Config
}
// swapType returns the swap type that the builder is responsible for creating.
func (b *loopInBuilder) swapType() swap.Type {
return swap.TypeIn
}
// maySwap checks whether we can currently execute a swap, examining the
// current on-chain fee conditions against relevant to our swap type against
// our fee restrictions.
//
// For loop in, we cannot check any upfront costs because we do not know how
// many inputs will be used for our on-chain htlc before it is made, so we can't
// make nay estimations.
func (b *loopInBuilder) maySwap(_ context.Context, _ Parameters) error {
return nil
}
// inUse examines our current swap traffic to determine whether we should
// suggest the builder's type of swap for the peer and channels suggested.
func (b *loopInBuilder) inUse(traffic *swapTraffic, peer route.Vertex,
channels []lnwire.ShortChannelID) error {
for _, chanID := range channels {
if traffic.ongoingLoopOut[chanID] {
log.Debugf("Channel: %v not eligible for suggestions, "+
"ongoing loop out utilizing channel", chanID)
return newReasonError(ReasonLoopOut)
}
}
if traffic.ongoingLoopIn[peer] {
log.Debugf("Peer: %x not eligible for suggestions ongoing "+
"loop in utilizing peer", peer)
return newReasonError(ReasonLoopIn)
}
lastFail, recentFail := traffic.failedLoopIn[peer]
if recentFail {
log.Debugf("Peer: %v not eligible for suggestions, "+
"was part of a failed swap at: %v", peer,
lastFail)
return newReasonError(ReasonFailureBackoff)
}
return nil
}
// buildSwap creates a swap for the target peer/channels provided. The autoloop
// boolean indicates whether this swap will actually be executed.
//
// For loop in, we do not add the autoloop label for dry runs.
func (b *loopInBuilder) buildSwap(ctx context.Context, pubkey route.Vertex,
_ []lnwire.ShortChannelID, amount btcutil.Amount,
params Parameters) (swapSuggestion, error) {
quote, err := b.cfg.LoopInQuote(ctx, &loop.LoopInQuoteRequest{
Amount: amount,
LastHop: &pubkey,
HtlcConfTarget: params.HtlcConfTarget,
Initiator: getInitiator(params),
})
if err != nil {
// If the server fails our quote, we're not reachable right
// now, so we want to catch this error and fail with a
// structured error so that we know why we can't swap.
status, ok := status.FromError(err)
if ok && status.Code() == codes.FailedPrecondition {
return nil, newReasonError(ReasonLoopInUnreachable)
}
return nil, err
}
if err := params.FeeLimit.loopInLimits(amount, quote); err != nil {
return nil, err
}
request := loop.LoopInRequest{
Amount: amount,
MaxSwapFee: quote.SwapFee,
MaxMinerFee: quote.MinerFee,
HtlcConfTarget: params.HtlcConfTarget,
LastHop: &pubkey,
Initiator: getInitiator(params),
}
if params.Autoloop {
request.Label = labels.AutoloopLabel(swap.TypeIn)
if params.EasyAutoloop {
request.Label = labels.EasyAutoloopLabel(swap.TypeIn)
}
}
return &loopInSwapSuggestion{
LoopInRequest: request,
}, nil
}