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

lightningnetwork / lnd / 12879602034

21 Jan 2025 03:26AM UTC coverage: 49.413% (-9.3%) from 58.72%
12879602034

Pull #9430

github

Roasbeef
lnwallet: add new NoopAdd payDesc entry type

In this commit, we add a new NoopAdd payDesc entry type. This type is
meant to be used primarily by taproot overlay channels. When we go to
settle this HTLC, rather than credit the settler for the funds, we just
give the funds back to the sender. This results in an add that when
settled, doesn't affect the balance in the channel.

This new HTLC type is intended to be used alongside a push amt, to
ensure the remote party has a non-dust balance from the start. With that
in place, then this new add type can be used for special overlay HTLCs.
Pull Request #9430: lnwallet: add new NoopAdd payDesc entry type

23 of 36 new or added lines in 2 files covered. (63.89%)

26915 existing lines in 431 files now uncovered.

100600 of 203591 relevant lines covered (49.41%)

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

© 2026 Coveralls, Inc