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

lightningnetwork / lnd / 12058234999

27 Nov 2024 09:06PM UTC coverage: 57.847% (-1.1%) from 58.921%
12058234999

Pull #9148

github

ProofOfKeags
lnwire: convert DynPropose and DynCommit to use typed tlv records
Pull Request #9148: DynComms [2/n]: lnwire: add authenticated wire messages for Dyn*

142 of 177 new or added lines in 4 files covered. (80.23%)

19365 existing lines in 251 files now uncovered.

100876 of 174383 relevant lines covered (57.85%)

25338.28 hits per line

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

63.64
/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
66
        // the resolution. Otherwise, we fetch this pointer from the input of
67
        // the time out transaction.
68
        outPointToWatch, scriptToWatch, err := h.chainDetailsToWatch()
4✔
69
        if err != nil {
4✔
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(
4✔
76
                outPointToWatch, scriptToWatch, h.broadcastHeight,
4✔
77
        )
4✔
78
        if err != nil {
4✔
79
                return nil, err
×
80
        }
×
81

82
        // We'll quickly check to see if the output has already been spent.
83
        select {
4✔
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:
×
87
                if !ok {
×
88
                        return nil, errResolverShuttingDown
×
89
                }
×
90

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
95
        // sweeping out this output.
96
        default:
4✔
97
        }
98

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

4✔
108
        for {
10✔
109
                select {
6✔
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

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

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

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

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

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

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

UNCOV
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
        }
×
176
}
177

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

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 {
2✔
191
        return h.resolved
2✔
192
}
2✔
193

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

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

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

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

223
// A compile time assertion to ensure htlcOutgoingContestResolver meets the
224
// ContractResolver interface.
225
var _ htlcContractResolver = (*htlcOutgoingContestResolver)(nil)
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