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

lightningnetwork / lnd / 12986279612

27 Jan 2025 09:51AM UTC coverage: 57.652% (-1.1%) from 58.788%
12986279612

Pull #9447

github

yyforyongyu
sweep: rename methods for clarity

We now rename "third party" to "unknown" as the inputs can be spent via
an older sweeping tx, a third party (anchor), or a remote party (pin).
In fee bumper we don't have the info to distinguish the above cases, and
leave them to be further handled by the sweeper as it has more context.
Pull Request #9447: sweep: start tracking input spending status in the fee bumper

83 of 87 new or added lines in 2 files covered. (95.4%)

19578 existing lines in 256 files now uncovered.

103448 of 179434 relevant lines covered (57.65%)

24884.58 hits per line

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

76.3
/contractcourt/anchor_resolver.go
1
package contractcourt
2

3
import (
4
        "errors"
5
        "fmt"
6
        "io"
7
        "sync"
8

9
        "github.com/btcsuite/btcd/btcutil"
10
        "github.com/btcsuite/btcd/chaincfg/chainhash"
11
        "github.com/btcsuite/btcd/wire"
12
        "github.com/lightningnetwork/lnd/channeldb"
13
        "github.com/lightningnetwork/lnd/fn/v2"
14
        "github.com/lightningnetwork/lnd/input"
15
        "github.com/lightningnetwork/lnd/sweep"
16
)
17

18
// anchorResolver is a resolver that will attempt to sweep our anchor output.
19
type anchorResolver struct {
20
        // anchorSignDescriptor contains the information that is required to
21
        // sweep the anchor.
22
        anchorSignDescriptor input.SignDescriptor
23

24
        // anchor is the outpoint on the commitment transaction.
25
        anchor wire.OutPoint
26

27
        // broadcastHeight is the height that the original contract was
28
        // broadcast to the main-chain at. We'll use this value to bound any
29
        // historical queries to the chain for spends/confirmations.
30
        broadcastHeight uint32
31

32
        // chanPoint is the channel point of the original contract.
33
        chanPoint wire.OutPoint
34

35
        // chanType denotes the type of channel the contract belongs to.
36
        chanType channeldb.ChannelType
37

38
        // currentReport stores the current state of the resolver for reporting
39
        // over the rpc interface.
40
        currentReport ContractReport
41

42
        // reportLock prevents concurrent access to the resolver report.
43
        reportLock sync.Mutex
44

45
        contractResolverKit
46
}
47

48
// newAnchorResolver instantiates a new anchor resolver.
49
func newAnchorResolver(anchorSignDescriptor input.SignDescriptor,
50
        anchor wire.OutPoint, broadcastHeight uint32,
51
        chanPoint wire.OutPoint, resCfg ResolverConfig) *anchorResolver {
2✔
52

2✔
53
        amt := btcutil.Amount(anchorSignDescriptor.Output.Value)
2✔
54

2✔
55
        report := ContractReport{
2✔
56
                Outpoint:         anchor,
2✔
57
                Type:             ReportOutputAnchor,
2✔
58
                Amount:           amt,
2✔
59
                LimboBalance:     amt,
2✔
60
                RecoveredBalance: 0,
2✔
61
        }
2✔
62

2✔
63
        r := &anchorResolver{
2✔
64
                contractResolverKit:  *newContractResolverKit(resCfg),
2✔
65
                anchorSignDescriptor: anchorSignDescriptor,
2✔
66
                anchor:               anchor,
2✔
67
                broadcastHeight:      broadcastHeight,
2✔
68
                chanPoint:            chanPoint,
2✔
69
                currentReport:        report,
2✔
70
        }
2✔
71

2✔
72
        r.initLogger(fmt.Sprintf("%T(%v)", r, r.anchor))
2✔
73

2✔
74
        return r
2✔
75
}
2✔
76

77
// ResolverKey returns an identifier which should be globally unique for this
78
// particular resolver within the chain the original contract resides within.
79
func (c *anchorResolver) ResolverKey() []byte {
2✔
80
        // The anchor resolver is stateless and doesn't need a database key.
2✔
81
        return nil
2✔
82
}
2✔
83

84
// Resolve waits for the output to be swept.
85
//
86
// NOTE: Part of the ContractResolver interface.
87
func (c *anchorResolver) Resolve() (ContractResolver, error) {
2✔
88
        // If we're already resolved, then we can exit early.
2✔
89
        if c.IsResolved() {
2✔
90
                c.log.Errorf("already resolved")
×
91
                return nil, nil
×
92
        }
×
93

94
        var (
2✔
95
                outcome channeldb.ResolverOutcome
2✔
96
                spendTx *chainhash.Hash
2✔
97
        )
2✔
98

2✔
99
        select {
2✔
100
        case sweepRes := <-c.sweepResultChan:
2✔
101
                switch sweepRes.Err {
2✔
102
                // Anchor was swept successfully.
103
                case nil:
2✔
104
                        sweepTxID := sweepRes.Tx.TxHash()
2✔
105

2✔
106
                        spendTx = &sweepTxID
2✔
107
                        outcome = channeldb.ResolverOutcomeClaimed
2✔
108

109
                // Anchor was swept by someone else. This is possible after the
110
                // 16 block csv lock.
UNCOV
111
                case sweep.ErrRemoteSpend:
×
UNCOV
112
                        c.log.Warnf("our anchor spent by someone else")
×
113
                        outcome = channeldb.ResolverOutcomeUnclaimed
×
114

115
                // An unexpected error occurred.
UNCOV
116
                default:
×
UNCOV
117
                        c.log.Errorf("unable to sweep anchor: %v", sweepRes.Err)
×
UNCOV
118

×
UNCOV
119
                        return nil, sweepRes.Err
×
120
                }
121

122
        case <-c.quit:
×
123
                return nil, errResolverShuttingDown
×
124
        }
125

126
        c.log.Infof("resolved in tx %v", spendTx)
2✔
127

2✔
128
        // Update report to reflect that funds are no longer in limbo.
2✔
129
        c.reportLock.Lock()
2✔
130
        if outcome == channeldb.ResolverOutcomeClaimed {
4✔
131
                c.currentReport.RecoveredBalance = c.currentReport.LimboBalance
2✔
132
        }
2✔
133
        c.currentReport.LimboBalance = 0
2✔
134
        report := c.currentReport.resolverReport(
2✔
135
                spendTx, channeldb.ResolverTypeAnchor, outcome,
2✔
136
        )
2✔
137
        c.reportLock.Unlock()
2✔
138

2✔
139
        c.markResolved()
2✔
140
        return nil, c.PutResolverReport(nil, report)
2✔
141
}
142

143
// Stop signals the resolver to cancel any current resolution processes, and
144
// suspend.
145
//
146
// NOTE: Part of the ContractResolver interface.
147
func (c *anchorResolver) Stop() {
2✔
148
        c.log.Debugf("stopping...")
2✔
149
        defer c.log.Debugf("stopped")
2✔
150

2✔
151
        close(c.quit)
2✔
152
}
2✔
153

154
// SupplementState allows the user of a ContractResolver to supplement it with
155
// state required for the proper resolution of a contract.
156
//
157
// NOTE: Part of the ContractResolver interface.
158
func (c *anchorResolver) SupplementState(state *channeldb.OpenChannel) {
2✔
159
        c.chanType = state.ChanType
2✔
160
}
2✔
161

162
// report returns a report on the resolution state of the contract.
UNCOV
163
func (c *anchorResolver) report() *ContractReport {
×
UNCOV
164
        c.reportLock.Lock()
×
UNCOV
165
        defer c.reportLock.Unlock()
×
UNCOV
166

×
UNCOV
167
        reportCopy := c.currentReport
×
UNCOV
168
        return &reportCopy
×
UNCOV
169
}
×
170

UNCOV
171
func (c *anchorResolver) Encode(w io.Writer) error {
×
UNCOV
172
        return errors.New("serialization not supported")
×
UNCOV
173
}
×
174

175
// A compile time assertion to ensure anchorResolver meets the
176
// ContractResolver interface.
177
var _ ContractResolver = (*anchorResolver)(nil)
178

179
// Launch offers the anchor output to the sweeper.
180
func (c *anchorResolver) Launch() error {
2✔
181
        if c.isLaunched() {
2✔
UNCOV
182
                c.log.Tracef("already launched")
×
UNCOV
183
                return nil
×
UNCOV
184
        }
×
185

186
        c.log.Debugf("launching resolver...")
2✔
187
        c.markLaunched()
2✔
188

2✔
189
        // If we're already resolved, then we can exit early.
2✔
190
        if c.IsResolved() {
2✔
UNCOV
191
                c.log.Errorf("already resolved")
×
UNCOV
192
                return nil
×
UNCOV
193
        }
×
194

195
        // Attempt to update the sweep parameters to the post-confirmation
196
        // situation. We don't want to force sweep anymore, because the anchor
197
        // lost its special purpose to get the commitment confirmed. It is just
198
        // an output that we want to sweep only if it is economical to do so.
199
        //
200
        // An exclusive group is not necessary anymore, because we know that
201
        // this is the only anchor that can be swept.
202
        //
203
        // We also clear the parent tx information for cpfp, because the
204
        // commitment tx is confirmed.
205
        //
206
        // After a restart or when the remote force closes, the sweeper is not
207
        // yet aware of the anchor. In that case, it will be added as new input
208
        // to the sweeper.
209
        witnessType := input.CommitmentAnchor
2✔
210

2✔
211
        // For taproot channels, we need to use the proper witness type.
2✔
212
        if c.chanType.IsTaproot() {
2✔
UNCOV
213
                witnessType = input.TaprootAnchorSweepSpend
×
UNCOV
214
        }
×
215

216
        anchorInput := input.MakeBaseInput(
2✔
217
                &c.anchor, witnessType, &c.anchorSignDescriptor,
2✔
218
                c.broadcastHeight, nil,
2✔
219
        )
2✔
220

2✔
221
        resultChan, err := c.Sweeper.SweepInput(
2✔
222
                &anchorInput,
2✔
223
                sweep.Params{
2✔
224
                        // For normal anchor sweeping, the budget is 330 sats.
2✔
225
                        Budget: btcutil.Amount(
2✔
226
                                anchorInput.SignDesc().Output.Value,
2✔
227
                        ),
2✔
228

2✔
229
                        // There's no rush to sweep the anchor, so we use a nil
2✔
230
                        // deadline here.
2✔
231
                        DeadlineHeight: fn.None[int32](),
2✔
232
                },
2✔
233
        )
2✔
234

2✔
235
        if err != nil {
2✔
UNCOV
236
                return err
×
UNCOV
237
        }
×
238

239
        c.sweepResultChan = resultChan
2✔
240

2✔
241
        return nil
2✔
242
}
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