• 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

0.0
/input/test_utils.go
1
package input
2

3
import (
4
        "bytes"
5
        "encoding/hex"
6
        "fmt"
7

8
        "github.com/btcsuite/btcd/btcec/v2"
9
        "github.com/btcsuite/btcd/btcec/v2/ecdsa"
10
        "github.com/btcsuite/btcd/btcec/v2/schnorr"
11
        "github.com/btcsuite/btcd/btcutil"
12
        "github.com/btcsuite/btcd/chaincfg"
13
        "github.com/btcsuite/btcd/chaincfg/chainhash"
14
        "github.com/btcsuite/btcd/txscript"
15
        "github.com/btcsuite/btcd/wire"
16
        "github.com/lightningnetwork/lnd/keychain"
17
)
18

19
var (
20

21
        // For simplicity a single priv key controls all of our test outputs.
22
        testWalletPrivKey = []byte{
23
                0x2b, 0xd8, 0x06, 0xc9, 0x7f, 0x0e, 0x00, 0xaf,
24
                0x1a, 0x1f, 0xc3, 0x32, 0x8f, 0xa7, 0x63, 0xa9,
25
                0x26, 0x97, 0x23, 0xc8, 0xdb, 0x8f, 0xac, 0x4f,
26
                0x93, 0xaf, 0x71, 0xdb, 0x18, 0x6d, 0x6e, 0x90,
27
        }
28

29
        // We're alice :)
30
        bobsPrivKey = []byte{
31
                0x81, 0xb6, 0x37, 0xd8, 0xfc, 0xd2, 0xc6, 0xda,
32
                0x63, 0x59, 0xe6, 0x96, 0x31, 0x13, 0xa1, 0x17,
33
                0xd, 0xe7, 0x95, 0xe4, 0xb7, 0x25, 0xb8, 0x4d,
34
                0x1e, 0xb, 0x4c, 0xfd, 0x9e, 0xc5, 0x8c, 0xe9,
35
        }
36

37
        // Use a hard-coded HD seed.
38
        testHdSeed = chainhash.Hash{
39
                0xb7, 0x94, 0x38, 0x5f, 0x2d, 0x1e, 0xf7, 0xab,
40
                0x4d, 0x92, 0x73, 0xd1, 0x90, 0x63, 0x81, 0xb4,
41
                0x4f, 0x2f, 0x6f, 0x25, 0x88, 0xa3, 0xef, 0xb9,
42
                0x6a, 0x49, 0x18, 0x83, 0x31, 0x98, 0x47, 0x53,
43
        }
44
)
45

46
// MockSigner is a simple implementation of the Signer interface. Each one has
47
// a set of private keys in a slice and can sign messages using the appropriate
48
// one.
49
type MockSigner struct {
50
        Privkeys  []*btcec.PrivateKey
51
        NetParams *chaincfg.Params
52

53
        *MusigSessionManager
54
}
55

56
// NewMockSigner returns a new instance of the MockSigner given a set of
57
// backing private keys.
58
func NewMockSigner(privKeys []*btcec.PrivateKey,
UNCOV
59
        netParams *chaincfg.Params) *MockSigner {
×
UNCOV
60

×
UNCOV
61
        signer := &MockSigner{
×
UNCOV
62
                Privkeys:  privKeys,
×
UNCOV
63
                NetParams: netParams,
×
UNCOV
64
        }
×
UNCOV
65

×
UNCOV
66
        keyFetcher := func(*keychain.KeyDescriptor) (*btcec.PrivateKey, error) {
×
UNCOV
67
                return signer.Privkeys[0], nil
×
UNCOV
68
        }
×
UNCOV
69
        signer.MusigSessionManager = NewMusigSessionManager(keyFetcher)
×
UNCOV
70

×
UNCOV
71
        return signer
×
72
}
73

74
// SignOutputRaw generates a signature for the passed transaction according to
75
// the data within the passed SignDescriptor.
76
func (m *MockSigner) SignOutputRaw(tx *wire.MsgTx,
UNCOV
77
        signDesc *SignDescriptor) (Signature, error) {
×
UNCOV
78

×
UNCOV
79
        pubkey := signDesc.KeyDesc.PubKey
×
UNCOV
80
        switch {
×
UNCOV
81
        case signDesc.SingleTweak != nil:
×
UNCOV
82
                pubkey = TweakPubKeyWithTweak(pubkey, signDesc.SingleTweak)
×
UNCOV
83
        case signDesc.DoubleTweak != nil:
×
UNCOV
84
                pubkey = DeriveRevocationPubkey(pubkey, signDesc.DoubleTweak.PubKey())
×
85
        }
86

UNCOV
87
        hash160 := btcutil.Hash160(pubkey.SerializeCompressed())
×
UNCOV
88
        privKey := m.findKey(hash160, signDesc.SingleTweak, signDesc.DoubleTweak)
×
UNCOV
89
        if privKey == nil {
×
90
                return nil, fmt.Errorf("mock signer does not have key")
×
91
        }
×
92

93
        // In case of a taproot output any signature is always a Schnorr
94
        // signature, based on the new tapscript sighash algorithm.
UNCOV
95
        if txscript.IsPayToTaproot(signDesc.Output.PkScript) {
×
UNCOV
96
                sigHashes := txscript.NewTxSigHashes(
×
UNCOV
97
                        tx, signDesc.PrevOutputFetcher,
×
UNCOV
98
                )
×
UNCOV
99

×
UNCOV
100
                // Are we spending a script path or the key path? The API is
×
UNCOV
101
                // slightly different, so we need to account for that to get
×
UNCOV
102
                // the raw signature.
×
UNCOV
103
                var (
×
UNCOV
104
                        rawSig []byte
×
UNCOV
105
                        err    error
×
UNCOV
106
                )
×
UNCOV
107
                switch signDesc.SignMethod {
×
108
                case TaprootKeySpendBIP0086SignMethod,
UNCOV
109
                        TaprootKeySpendSignMethod:
×
UNCOV
110

×
UNCOV
111
                        // This function tweaks the private key using the tap
×
UNCOV
112
                        // root key supplied as the tweak.
×
UNCOV
113
                        rawSig, err = txscript.RawTxInTaprootSignature(
×
UNCOV
114
                                tx, sigHashes, signDesc.InputIndex,
×
UNCOV
115
                                signDesc.Output.Value, signDesc.Output.PkScript,
×
UNCOV
116
                                signDesc.TapTweak, signDesc.HashType,
×
UNCOV
117
                                privKey,
×
UNCOV
118
                        )
×
UNCOV
119
                        if err != nil {
×
120
                                return nil, err
×
121
                        }
×
122

UNCOV
123
                case TaprootScriptSpendSignMethod:
×
UNCOV
124
                        leaf := txscript.TapLeaf{
×
UNCOV
125
                                LeafVersion: txscript.BaseLeafVersion,
×
UNCOV
126
                                Script:      signDesc.WitnessScript,
×
UNCOV
127
                        }
×
UNCOV
128
                        rawSig, err = txscript.RawTxInTapscriptSignature(
×
UNCOV
129
                                tx, sigHashes, signDesc.InputIndex,
×
UNCOV
130
                                signDesc.Output.Value, signDesc.Output.PkScript,
×
UNCOV
131
                                leaf, signDesc.HashType, privKey,
×
UNCOV
132
                        )
×
UNCOV
133
                        if err != nil {
×
134
                                return nil, err
×
135
                        }
×
136
                }
137

138
                // The signature returned above might have a sighash flag
139
                // attached if a non-default type was used. We'll slice this
140
                // off if it exists to ensure we can properly parse the raw
141
                // signature.
UNCOV
142
                sig, err := schnorr.ParseSignature(
×
UNCOV
143
                        rawSig[:schnorr.SignatureSize],
×
UNCOV
144
                )
×
UNCOV
145
                if err != nil {
×
146
                        return nil, err
×
147
                }
×
148

UNCOV
149
                return sig, nil
×
150
        }
151

UNCOV
152
        sig, err := txscript.RawTxInWitnessSignature(
×
UNCOV
153
                tx, signDesc.SigHashes, signDesc.InputIndex,
×
UNCOV
154
                signDesc.Output.Value, signDesc.WitnessScript,
×
UNCOV
155
                signDesc.HashType, privKey,
×
UNCOV
156
        )
×
UNCOV
157
        if err != nil {
×
158
                return nil, err
×
159
        }
×
160

UNCOV
161
        return ecdsa.ParseDERSignature(sig[:len(sig)-1])
×
162
}
163

164
// ComputeInputScript generates a complete InputIndex for the passed transaction
165
// with the signature as defined within the passed SignDescriptor. This method
166
// should be capable of generating the proper input script for both regular
167
// p2wkh output and p2wkh outputs nested within a regular p2sh output.
168
func (m *MockSigner) ComputeInputScript(tx *wire.MsgTx, signDesc *SignDescriptor) (*Script, error) {
×
169
        scriptType, addresses, _, err := txscript.ExtractPkScriptAddrs(
×
170
                signDesc.Output.PkScript, m.NetParams)
×
171
        if err != nil {
×
172
                return nil, err
×
173
        }
×
174

175
        switch scriptType {
×
176
        case txscript.PubKeyHashTy:
×
177
                privKey := m.findKey(addresses[0].ScriptAddress(), signDesc.SingleTweak,
×
178
                        signDesc.DoubleTweak)
×
179
                if privKey == nil {
×
180
                        return nil, fmt.Errorf("mock signer does not have key for "+
×
181
                                "address %v", addresses[0])
×
182
                }
×
183

184
                sigScript, err := txscript.SignatureScript(
×
185
                        tx, signDesc.InputIndex, signDesc.Output.PkScript,
×
186
                        txscript.SigHashAll, privKey, true,
×
187
                )
×
188
                if err != nil {
×
189
                        return nil, err
×
190
                }
×
191

192
                return &Script{SigScript: sigScript}, nil
×
193

194
        case txscript.WitnessV0PubKeyHashTy:
×
195
                privKey := m.findKey(addresses[0].ScriptAddress(), signDesc.SingleTweak,
×
196
                        signDesc.DoubleTweak)
×
197
                if privKey == nil {
×
198
                        return nil, fmt.Errorf("mock signer does not have key for "+
×
199
                                "address %v", addresses[0])
×
200
                }
×
201

202
                witnessScript, err := txscript.WitnessSignature(tx, signDesc.SigHashes,
×
203
                        signDesc.InputIndex, signDesc.Output.Value,
×
204
                        signDesc.Output.PkScript, txscript.SigHashAll, privKey, true)
×
205
                if err != nil {
×
206
                        return nil, err
×
207
                }
×
208

209
                return &Script{Witness: witnessScript}, nil
×
210

211
        default:
×
212
                return nil, fmt.Errorf("unexpected script type: %v", scriptType)
×
213
        }
214
}
215

216
// findKey searches through all stored private keys and returns one
217
// corresponding to the hashed pubkey if it can be found. The public key may
218
// either correspond directly to the private key or to the private key with a
219
// tweak applied.
220
func (m *MockSigner) findKey(needleHash160 []byte, singleTweak []byte,
UNCOV
221
        doubleTweak *btcec.PrivateKey) *btcec.PrivateKey {
×
UNCOV
222

×
UNCOV
223
        for _, privkey := range m.Privkeys {
×
UNCOV
224
                // First check whether public key is directly derived from
×
UNCOV
225
                // private key.
×
UNCOV
226
                hash160 := btcutil.Hash160(privkey.PubKey().SerializeCompressed())
×
UNCOV
227
                if bytes.Equal(hash160, needleHash160) {
×
UNCOV
228
                        return privkey
×
UNCOV
229
                }
×
230

231
                // Otherwise check if public key is derived from tweaked
232
                // private key.
UNCOV
233
                switch {
×
UNCOV
234
                case singleTweak != nil:
×
UNCOV
235
                        privkey = TweakPrivKey(privkey, singleTweak)
×
UNCOV
236
                case doubleTweak != nil:
×
UNCOV
237
                        privkey = DeriveRevocationPrivKey(privkey, doubleTweak)
×
UNCOV
238
                default:
×
UNCOV
239
                        continue
×
240
                }
UNCOV
241
                hash160 = btcutil.Hash160(privkey.PubKey().SerializeCompressed())
×
UNCOV
242
                if bytes.Equal(hash160, needleHash160) {
×
UNCOV
243
                        return privkey
×
UNCOV
244
                }
×
245
        }
246
        return nil
×
247
}
248

249
// pubkeyFromHex parses a Bitcoin public key from a hex encoded string.
UNCOV
250
func pubkeyFromHex(keyHex string) (*btcec.PublicKey, error) {
×
UNCOV
251
        bytes, err := hex.DecodeString(keyHex)
×
UNCOV
252
        if err != nil {
×
253
                return nil, err
×
254
        }
×
UNCOV
255
        return btcec.ParsePubKey(bytes)
×
256
}
257

258
// privkeyFromHex parses a Bitcoin private key from a hex encoded string.
UNCOV
259
func privkeyFromHex(keyHex string) (*btcec.PrivateKey, error) {
×
UNCOV
260
        bytes, err := hex.DecodeString(keyHex)
×
UNCOV
261
        if err != nil {
×
262
                return nil, err
×
263
        }
×
UNCOV
264
        key, _ := btcec.PrivKeyFromBytes(bytes)
×
UNCOV
265
        return key, nil
×
266

267
}
268

269
// pubkeyToHex serializes a Bitcoin public key to a hex encoded string.
UNCOV
270
func pubkeyToHex(key *btcec.PublicKey) string {
×
UNCOV
271
        return hex.EncodeToString(key.SerializeCompressed())
×
UNCOV
272
}
×
273

274
// privkeyFromHex serializes a Bitcoin private key to a hex encoded string.
UNCOV
275
func privkeyToHex(key *btcec.PrivateKey) string {
×
UNCOV
276
        return hex.EncodeToString(key.Serialize())
×
UNCOV
277
}
×
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