• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

lightningnetwork / lnd / 11216766535

07 Oct 2024 01:37PM UTC coverage: 57.817% (-1.0%) from 58.817%
11216766535

Pull #9148

github

ProofOfKeags
lnwire: remove kickoff feerate from propose/commit
Pull Request #9148: DynComms [2/n]: lnwire: add authenticated wire messages for Dyn*

571 of 879 new or added lines in 16 files covered. (64.96%)

23253 existing lines in 251 files now uncovered.

99022 of 171268 relevant lines covered (57.82%)

38420.67 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

62.14
/contractcourt/htlc_outgoing_contest_resolver.go
1
package contractcourt
2

3
import (
4
        "fmt"
5
        "io"
6

7
        "github.com/btcsuite/btcd/btcutil"
8
        "github.com/lightningnetwork/lnd/channeldb"
9
        "github.com/lightningnetwork/lnd/fn"
10
        "github.com/lightningnetwork/lnd/lnwallet"
11
)
12

13
// htlcOutgoingContestResolver is a ContractResolver that's able to resolve an
14
// outgoing HTLC that is still contested. An HTLC is still contested, if at the
15
// time that we broadcast the commitment transaction, it isn't able to be fully
16
// resolved. In this case, we'll either wait for the HTLC to timeout, or for
17
// us to learn of the preimage.
18
type htlcOutgoingContestResolver struct {
19
        // htlcTimeoutResolver is the inner solver that this resolver may turn
20
        // into. This only happens if the HTLC expires on-chain.
21
        *htlcTimeoutResolver
22
}
23

24
// newOutgoingContestResolver instantiates a new outgoing contested htlc
25
// resolver.
26
func newOutgoingContestResolver(res lnwallet.OutgoingHtlcResolution,
27
        broadcastHeight uint32, htlc channeldb.HTLC,
28
        resCfg ResolverConfig) *htlcOutgoingContestResolver {
1✔
29

1✔
30
        timeout := newTimeoutResolver(
1✔
31
                res, broadcastHeight, htlc, resCfg,
1✔
32
        )
1✔
33

1✔
34
        return &htlcOutgoingContestResolver{
1✔
35
                htlcTimeoutResolver: timeout,
1✔
36
        }
1✔
37
}
1✔
38

39
// Resolve commences the resolution of this contract. As this contract hasn't
40
// yet timed out, we'll wait for one of two things to happen
41
//
42
//  1. The HTLC expires. In this case, we'll sweep the funds and send a clean
43
//     up cancel message to outside sub-systems.
44
//
45
//  2. The remote party sweeps this HTLC on-chain, in which case we'll add the
46
//     pre-image to our global cache, then send a clean up settle message
47
//     backwards.
48
//
49
// When either of these two things happens, we'll create a new resolver which
50
// is able to handle the final resolution of the contract. We're only the pivot
51
// point.
52
func (h *htlcOutgoingContestResolver) Resolve(
53
        _ bool) (ContractResolver, error) {
4✔
54

4✔
55
        // If we're already full resolved, then we don't have anything further
4✔
56
        // to do.
4✔
57
        if h.resolved {
4✔
58
                return nil, nil
×
59
        }
×
60

61
        // Otherwise, we'll watch for two external signals to decide if we'll
62
        // morph into another resolver, or fully resolve the contract.
63
        //
64
        // The output we'll be watching for is the *direct* spend from the HTLC
65
        // output. If this isn't our commitment transaction, it'll be right on
4✔
UNCOV
66
        // the resolution. Otherwise, we fetch this pointer from the input of
×
UNCOV
67
        // the time out transaction.
×
UNCOV
68
        outPointToWatch, scriptToWatch, err := h.chainDetailsToWatch()
×
69
        if err != nil {
70
                return nil, err
71
        }
72

73
        // First, we'll register for a spend notification for this output. If
74
        // the remote party sweeps with the pre-image, we'll be notified.
75
        spendNtfn, err := h.Notifier.RegisterSpendNtfn(
76
                outPointToWatch, scriptToWatch, h.broadcastHeight,
77
        )
78
        if err != nil {
79
                return nil, err
4✔
80
        }
4✔
UNCOV
81

×
UNCOV
82
        // We'll quickly check to see if the output has already been spent.
×
83
        select {
84
        // If the output has already been spent, then we can stop early and
85
        // sweep the pre-image from the output.
86
        case commitSpend, ok := <-spendNtfn.Spend:
4✔
87
                if !ok {
4✔
88
                        return nil, errResolverShuttingDown
4✔
89
                }
4✔
UNCOV
90

×
UNCOV
91
                // TODO(roasbeef): Checkpoint?
×
92
                return h.claimCleanUp(commitSpend)
93

94
        // If it hasn't, then we'll watch for both the expiration, and the
4✔
95
        // sweeping out this output.
96
        default:
UNCOV
97
        }
×
UNCOV
98

×
UNCOV
99
        // If we reach this point, then we can't fully act yet, so we'll await
×
UNCOV
100
        // either of our signals triggering: the HTLC expires, or we learn of
×
101
        // the preimage.
102
        blockEpochs, err := h.Notifier.RegisterBlockEpochNtfn(nil)
UNCOV
103
        if err != nil {
×
104
                return nil, err
105
        }
106
        defer blockEpochs.Cancel()
107

4✔
108
        for {
109
                select {
110

111
                // A new block has arrived, we'll check to see if this leads to
112
                // HTLC expiration.
113
                case newBlock, ok := <-blockEpochs.Epochs:
4✔
114
                        if !ok {
4✔
115
                                return nil, errResolverShuttingDown
×
116
                        }
×
117

4✔
118
                        // If the current height is >= expiry, then a timeout
4✔
119
                        // path spend will be valid to be included in the next
10✔
120
                        // block, and we can immediately return the resolver.
6✔
121
                        //
122
                        // NOTE: when broadcasting this transaction, btcd will
123
                        // check the timelock in `CheckTransactionStandard`,
124
                        // which requires `expiry < currentHeight+1`. If the
4✔
125
                        // check doesn't pass, error `transaction is not
4✔
UNCOV
126
                        // finalized` will be returned and the broadcast will
×
UNCOV
127
                        // fail.
×
128
                        newHeight := uint32(newBlock.Height)
129
                        if newHeight >= h.htlcResolution.Expiry {
130
                                log.Infof("%T(%v): HTLC has expired "+
131
                                        "(height=%v, expiry=%v), transforming "+
132
                                        "into timeout resolver", h,
133
                                        h.htlcResolution.ClaimOutpoint,
134
                                        newHeight, h.htlcResolution.Expiry)
135
                                return h.htlcTimeoutResolver, nil
136
                        }
137

138
                // The output has been spent! This means the preimage has been
139
                // revealed on-chain.
4✔
140
                case commitSpend, ok := <-spendNtfn.Spend:
6✔
141
                        if !ok {
2✔
142
                                return nil, errResolverShuttingDown
2✔
143
                        }
2✔
144

2✔
145
                        // The only way this output can be spent by the remote
2✔
146
                        // party is by revealing the preimage. So we'll perform
2✔
147
                        // our duties to clean up the contract once it has been
2✔
148
                        // claimed.
149
                        return h.claimCleanUp(commitSpend)
150

151
                case <-h.quit:
1✔
152
                        return nil, fmt.Errorf("resolver canceled")
1✔
UNCOV
153
                }
×
UNCOV
154
        }
×
155
}
156

157
// report returns a report on the resolution state of the contract.
158
func (h *htlcOutgoingContestResolver) report() *ContractReport {
159
        // No locking needed as these values are read-only.
160

1✔
161
        finalAmt := h.htlc.Amt.ToSatoshis()
162
        if h.htlcResolution.SignedTimeoutTx != nil {
1✔
163
                finalAmt = btcutil.Amount(
1✔
164
                        h.htlcResolution.SignedTimeoutTx.TxOut[0].Value,
165
                )
166
        }
167

168
        return &ContractReport{
UNCOV
169
                Outpoint:       h.htlcResolution.ClaimOutpoint,
×
UNCOV
170
                Type:           ReportOutputOutgoingHtlc,
×
UNCOV
171
                Amount:         finalAmt,
×
UNCOV
172
                MaturityHeight: h.htlcResolution.Expiry,
×
UNCOV
173
                LimboBalance:   finalAmt,
×
UNCOV
174
                Stage:          1,
×
UNCOV
175
        }
×
UNCOV
176
}
×
UNCOV
177

×
178
// Stop signals the resolver to cancel any current resolution processes, and
UNCOV
179
// suspend.
×
UNCOV
180
//
×
UNCOV
181
// NOTE: Part of the ContractResolver interface.
×
UNCOV
182
func (h *htlcOutgoingContestResolver) Stop() {
×
UNCOV
183
        close(h.quit)
×
UNCOV
184
}
×
UNCOV
185

×
UNCOV
186
// IsResolved returns true if the stored state in the resolve is fully
×
187
// resolved. In this case the target output can be forgotten.
188
//
189
// NOTE: Part of the ContractResolver interface.
190
func (h *htlcOutgoingContestResolver) IsResolved() bool {
191
        return h.resolved
192
}
193

1✔
194
// Encode writes an encoded version of the ContractResolver into the passed
1✔
195
// Writer.
1✔
196
//
197
// NOTE: Part of the ContractResolver interface.
198
func (h *htlcOutgoingContestResolver) Encode(w io.Writer) error {
199
        return h.htlcTimeoutResolver.Encode(w)
200
}
201

2✔
202
// SupplementDeadline does nothing for an incoming htlc resolver.
2✔
203
//
2✔
204
// NOTE: Part of the htlcContractResolver interface.
205
func (h *htlcOutgoingContestResolver) SupplementDeadline(_ fn.Option[int32]) {
206
}
207

208
// newOutgoingContestResolverFromReader attempts to decode an encoded ContractResolver
209
// from the passed Reader instance, returning an active ContractResolver
3✔
210
// instance.
3✔
211
func newOutgoingContestResolverFromReader(r io.Reader, resCfg ResolverConfig) (
3✔
212
        *htlcOutgoingContestResolver, error) {
213

214
        h := &htlcOutgoingContestResolver{}
215
        timeoutResolver, err := newTimeoutResolverFromReader(r, resCfg)
216
        if err != nil {
2✔
217
                return nil, err
2✔
218
        }
219
        h.htlcTimeoutResolver = timeoutResolver
220
        return h, nil
221
}
222

223
// A compile time assertion to ensure htlcOutgoingContestResolver meets the
4✔
224
// ContractResolver interface.
4✔
225
var _ htlcContractResolver = (*htlcOutgoingContestResolver)(nil)
4✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc