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

lightningnetwork / lnd / 13211764208

08 Feb 2025 03:08AM UTC coverage: 49.288% (-9.5%) from 58.815%
13211764208

Pull #9489

github

calvinrzachman
itest: verify switchrpc server enforces send then track

We prevent the rpc server from allowing onion dispatches for
attempt IDs which have already been tracked by rpc clients.

This helps protect the client from leaking a duplicate onion
attempt. NOTE: This is not the only method for solving this
issue! The issue could be addressed via careful client side
programming which accounts for the uncertainty and async
nature of dispatching onions to a remote process via RPC.
This would require some lnd ChannelRouter changes for how
we intend to use these RPCs though.
Pull Request #9489: multi: add BuildOnion, SendOnion, and TrackOnion RPCs

474 of 990 new or added lines in 11 files covered. (47.88%)

27321 existing lines in 435 files now uncovered.

101192 of 205306 relevant lines covered (49.29%)

1.54 hits per line

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

68.57
/lnwallet/chanvalidate/validate.go
1
package chanvalidate
2

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

7
        "github.com/btcsuite/btcd/btcutil"
8
        "github.com/btcsuite/btcd/txscript"
9
        "github.com/btcsuite/btcd/wire"
10
        "github.com/lightningnetwork/lnd/lnwire"
11
)
12

13
var (
14
        // ErrInvalidOutPoint is returned when the ChanLocator is unable to
15
        // find the target outpoint.
16
        ErrInvalidOutPoint = fmt.Errorf("output meant to create channel cannot " +
17
                "be found")
18

19
        // ErrWrongPkScript is returned when the alleged funding transaction is
20
        // found to have an incorrect pkSript.
21
        ErrWrongPkScript = fmt.Errorf("wrong pk script")
22

23
        // ErrInvalidSize is returned when the alleged funding transaction
24
        // output has the wrong size (channel capacity).
25
        ErrInvalidSize = fmt.Errorf("channel has wrong size")
26
)
27

28
// ErrScriptValidateError is returned when Script VM validation fails for an
29
// alleged channel output.
30
type ErrScriptValidateError struct {
31
        err error
32
}
33

34
// Error returns a human readable string describing the error.
35
func (e *ErrScriptValidateError) Error() string {
×
36
        return fmt.Sprintf("script validation failed: %v", e.err)
×
37
}
×
38

39
// Unwrap returns the underlying wrapped VM execution failure error.
40
func (e *ErrScriptValidateError) Unwrap() error {
×
41
        return e.err
×
42
}
×
43

44
// ChanLocator abstracts away obtaining the output that created the channel, as
45
// well as validating its existence given the funding transaction.  We need
46
// this as there are several ways (outpoint, short chan ID) to identify the
47
// output of a channel given the funding transaction.
48
type ChanLocator interface {
49
        // Locate attempts to locate the funding output within the funding
50
        // transaction. It also returns the final out point of the channel
51
        // which uniquely identifies the output which creates the channel. If
52
        // the target output cannot be found, or cannot exist on the funding
53
        // transaction, then an error is to be returned.
54
        Locate(*wire.MsgTx) (*wire.TxOut, *wire.OutPoint, error)
55
}
56

57
// OutPointChanLocator is an implementation of the ChanLocator that can be used
58
// when one already knows the expected chan point.
59
type OutPointChanLocator struct {
60
        // ChanPoint is the expected chan point.
61
        ChanPoint wire.OutPoint
62
}
63

64
// Locate attempts to locate the funding output within the passed funding
65
// transaction.
66
//
67
// NOTE: Part of the ChanLocator interface.
68
func (o *OutPointChanLocator) Locate(fundingTx *wire.MsgTx) (
69
        *wire.TxOut, *wire.OutPoint, error) {
3✔
70

3✔
71
        // If the expected index is greater than the amount of output in the
3✔
72
        // transaction, then we'll reject this channel as it's invalid.
3✔
73
        if int(o.ChanPoint.Index) >= len(fundingTx.TxOut) {
3✔
UNCOV
74
                return nil, nil, ErrInvalidOutPoint
×
UNCOV
75
        }
×
76

77
        // As an extra sanity check, we'll also ensure the txid hash matches.
78
        fundingHash := fundingTx.TxHash()
3✔
79
        if !bytes.Equal(fundingHash[:], o.ChanPoint.Hash[:]) {
3✔
80
                return nil, nil, ErrInvalidOutPoint
×
81
        }
×
82

83
        return fundingTx.TxOut[o.ChanPoint.Index], &o.ChanPoint, nil
3✔
84
}
85

86
// ShortChanIDChanLocator is an implementation of the ChanLocator that can be
87
// used when one only knows the short channel ID of a channel. This should be
88
// used in contexts when one is verifying a 3rd party channel.
89
type ShortChanIDChanLocator struct {
90
        // ID is the short channel ID of the target channel.
91
        ID lnwire.ShortChannelID
92
}
93

94
// Locate attempts to locate the funding output within the passed funding
95
// transaction.
96
//
97
// NOTE: Part of the ChanLocator interface.
98
func (s *ShortChanIDChanLocator) Locate(fundingTx *wire.MsgTx) (
99
        *wire.TxOut, *wire.OutPoint, error) {
3✔
100

3✔
101
        // If the expected index is greater than the amount of output in the
3✔
102
        // transaction, then we'll reject this channel as it's invalid.
3✔
103
        outputIndex := s.ID.TxPosition
3✔
104
        if int(outputIndex) >= len(fundingTx.TxOut) {
3✔
UNCOV
105
                return nil, nil, ErrInvalidOutPoint
×
UNCOV
106
        }
×
107

108
        chanPoint := wire.OutPoint{
3✔
109
                Hash:  fundingTx.TxHash(),
3✔
110
                Index: uint32(outputIndex),
3✔
111
        }
3✔
112

3✔
113
        return fundingTx.TxOut[outputIndex], &chanPoint, nil
3✔
114
}
115

116
// CommitmentContext is optional validation context that can be passed into the
117
// main Validate for self-owned channel. The information in this context allows
118
// us to fully verify out initial commitment spend based on the on-chain state
119
// of the funding output.
120
type CommitmentContext struct {
121
        // Value is the known size of the channel.
122
        Value btcutil.Amount
123

124
        // FullySignedCommitTx is the fully signed commitment transaction. This
125
        // should include a valid witness.
126
        FullySignedCommitTx *wire.MsgTx
127
}
128

129
// Context is the main validation contxet. For a given channel, all fields but
130
// the optional CommitCtx should be populated based on existing
131
// known-to-be-valid parameters.
132
type Context struct {
133
        // Locator is a concrete implementation of the ChanLocator interface.
134
        Locator ChanLocator
135

136
        // MultiSigPkScript is the fully serialized witness script of the
137
        // multi-sig output. This is the final witness program that should be
138
        // found in the funding output.
139
        MultiSigPkScript []byte
140

141
        // FundingTx is channel funding transaction as found confirmed in the
142
        // chain.
143
        FundingTx *wire.MsgTx
144

145
        // CommitCtx is an optional additional set of validation context
146
        // required to validate a self-owned channel. If present, then a full
147
        // Script VM validation will be performed.
148
        CommitCtx *CommitmentContext
149
}
150

151
// Validate given the specified context, this function validates that the
152
// alleged channel is well formed, and spendable (if the optional CommitCtx is
153
// specified).  If this method returns an error, then the alleged channel is
154
// invalid and should be abandoned immediately.
155
func Validate(ctx *Context) (*wire.OutPoint, error) {
3✔
156
        // First, we'll attempt to locate the target outpoint in the funding
3✔
157
        // transaction. If this returns an error, then we know that the
3✔
158
        // outpoint doesn't actually exist, so we'll exit early.
3✔
159
        fundingOutput, chanPoint, err := ctx.Locator.Locate(
3✔
160
                ctx.FundingTx,
3✔
161
        )
3✔
162
        if err != nil {
3✔
UNCOV
163
                return nil, err
×
UNCOV
164
        }
×
165

166
        // The scripts should match up exactly, otherwise the channel is
167
        // invalid.
168
        fundingScript := fundingOutput.PkScript
3✔
169
        if !bytes.Equal(ctx.MultiSigPkScript, fundingScript) {
3✔
UNCOV
170
                return nil, ErrWrongPkScript
×
UNCOV
171
        }
×
172

173
        // If there's no commitment context, then we're done here as this is a
174
        // 3rd party channel.
175
        if ctx.CommitCtx == nil {
6✔
176
                return chanPoint, nil
3✔
177
        }
3✔
178

179
        // Now that we know this is our channel, we'll verify the amount of the
180
        // created output against our expected size of the channel.
181
        fundingValue := fundingOutput.Value
3✔
182
        if btcutil.Amount(fundingValue) != ctx.CommitCtx.Value {
3✔
UNCOV
183
                return nil, ErrInvalidSize
×
UNCOV
184
        }
×
185

186
        // If we reach this point, then all other checks have succeeded, so
187
        // we'll now attempt a full Script VM execution to ensure that we're
188
        // able to close the channel using this initial state.
189
        prevFetcher := txscript.NewCannedPrevOutputFetcher(
3✔
190
                ctx.MultiSigPkScript, fundingValue,
3✔
191
        )
3✔
192
        commitTx := ctx.CommitCtx.FullySignedCommitTx
3✔
193
        hashCache := txscript.NewTxSigHashes(commitTx, prevFetcher)
3✔
194
        vm, err := txscript.NewEngine(
3✔
195
                ctx.MultiSigPkScript, commitTx, 0, txscript.StandardVerifyFlags,
3✔
196
                nil, hashCache, fundingValue, prevFetcher,
3✔
197
        )
3✔
198
        if err != nil {
3✔
199
                return nil, err
×
200
        }
×
201

202
        // Finally, we'll attempt to verify our full spend, if this fails then
203
        // the channel is definitely invalid.
204
        err = vm.Execute()
3✔
205
        if err != nil {
3✔
UNCOV
206
                return nil, &ErrScriptValidateError{err: err}
×
UNCOV
207
        }
×
208

209
        return chanPoint, nil
3✔
210
}
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