-
Notifications
You must be signed in to change notification settings - Fork 2.1k
/
signer.go
276 lines (235 loc) · 8.39 KB
/
signer.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
package btcwallet
import (
"github.com/btcsuite/btcd/btcec"
"github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/btcsuite/btcd/txscript"
"github.com/btcsuite/btcd/wire"
"github.com/btcsuite/btcutil"
"github.com/btcsuite/btcwallet/waddrmgr"
"github.com/btcsuite/btcwallet/walletdb"
"github.com/go-errors/errors"
"github.com/lightningnetwork/lnd/input"
"github.com/lightningnetwork/lnd/keychain"
"github.com/lightningnetwork/lnd/lnwallet"
)
// FetchInputInfo queries for the WalletController's knowledge of the passed
// outpoint. If the base wallet determines this output is under its control,
// then the original txout should be returned. Otherwise, a non-nil error value
// of ErrNotMine should be returned instead.
//
// This is a part of the WalletController interface.
func (b *BtcWallet) FetchInputInfo(prevOut *wire.OutPoint) (*lnwallet.Utxo, error) {
_, txOut, confirmations, err := b.wallet.FetchInputInfo(prevOut)
if err != nil {
return nil, err
}
// Then, we'll populate all of the information required by the struct.
addressType := lnwallet.UnknownAddressType
switch {
case txscript.IsPayToWitnessPubKeyHash(txOut.PkScript):
addressType = lnwallet.WitnessPubKey
case txscript.IsPayToScriptHash(txOut.PkScript):
addressType = lnwallet.NestedWitnessPubKey
}
return &lnwallet.Utxo{
AddressType: addressType,
Value: btcutil.Amount(txOut.Value),
PkScript: txOut.PkScript,
Confirmations: confirmations,
OutPoint: *prevOut,
}, nil
}
// deriveFromKeyLoc attempts to derive a private key using a fully specified
// KeyLocator.
func deriveFromKeyLoc(scopedMgr *waddrmgr.ScopedKeyManager,
addrmgrNs walletdb.ReadWriteBucket,
keyLoc keychain.KeyLocator) (*btcec.PrivateKey, error) {
path := waddrmgr.DerivationPath{
Account: uint32(keyLoc.Family),
Branch: 0,
Index: uint32(keyLoc.Index),
}
addr, err := scopedMgr.DeriveFromKeyPath(addrmgrNs, path)
if err != nil {
return nil, err
}
return addr.(waddrmgr.ManagedPubKeyAddress).PrivKey()
}
// deriveKeyByLocator attempts to derive a key stored in the wallet given a
// valid key locator.
func (b *BtcWallet) deriveKeyByLocator(keyLoc keychain.KeyLocator) (*btcec.PrivateKey, error) {
// We'll assume the special lightning key scope in this case.
scopedMgr, err := b.wallet.Manager.FetchScopedKeyManager(
b.chainKeyScope,
)
if err != nil {
return nil, err
}
var key *btcec.PrivateKey
err = walletdb.Update(b.db, func(tx walletdb.ReadWriteTx) error {
addrmgrNs := tx.ReadWriteBucket(waddrmgrNamespaceKey)
key, err = deriveFromKeyLoc(scopedMgr, addrmgrNs, keyLoc)
if waddrmgr.IsError(err, waddrmgr.ErrAccountNotFound) {
// If we've reached this point, then the account
// doesn't yet exist, so we'll create it now to ensure
// we can sign.
acctErr := scopedMgr.NewRawAccount(
addrmgrNs, uint32(keyLoc.Family),
)
if acctErr != nil {
return acctErr
}
// Now that we know the account exists, we'll attempt
// to re-derive the private key.
key, err = deriveFromKeyLoc(
scopedMgr, addrmgrNs, keyLoc,
)
if err != nil {
return err
}
}
return err
})
if err != nil {
return nil, err
}
return key, nil
}
// fetchPrivKey attempts to retrieve the raw private key corresponding to the
// passed public key if populated, or the key descriptor path (if non-empty).
func (b *BtcWallet) fetchPrivKey(keyDesc *keychain.KeyDescriptor) (*btcec.PrivateKey, error) {
// If the key locator within the descriptor *isn't* empty, then we can
// directly derive the keys raw.
emptyLocator := keyDesc.KeyLocator.IsEmpty()
if !emptyLocator {
return b.deriveKeyByLocator(keyDesc.KeyLocator)
}
hash160 := btcutil.Hash160(keyDesc.PubKey.SerializeCompressed())
addr, err := btcutil.NewAddressWitnessPubKeyHash(hash160, b.netParams)
if err != nil {
return nil, err
}
// Otherwise, we'll attempt to derive the key based on the address.
// This will only work if we've already derived this address in the
// past, since the wallet relies on a mapping of addr -> key.
key, err := b.wallet.PrivKeyForAddress(addr)
switch {
// If we didn't find this key in the wallet, then there's a chance that
// this is actually an "empty" key locator. The legacy KeyLocator
// format failed to properly distinguish an empty key locator from the
// very first in the index (0, 0).IsEmpty() == true.
case waddrmgr.IsError(err, waddrmgr.ErrAddressNotFound) && emptyLocator:
return b.deriveKeyByLocator(keyDesc.KeyLocator)
case err != nil:
return nil, err
default:
return key, nil
}
}
// maybeTweakPrivKey examines the single and double tweak parameters on the
// passed sign descriptor and may perform a mapping on the passed private key
// in order to utilize the tweaks, if populated.
func maybeTweakPrivKey(signDesc *input.SignDescriptor,
privKey *btcec.PrivateKey) (*btcec.PrivateKey, error) {
var retPriv *btcec.PrivateKey
switch {
case signDesc.SingleTweak != nil:
retPriv = input.TweakPrivKey(privKey,
signDesc.SingleTweak)
case signDesc.DoubleTweak != nil:
retPriv = input.DeriveRevocationPrivKey(privKey,
signDesc.DoubleTweak)
default:
retPriv = privKey
}
return retPriv, nil
}
// SignOutputRaw generates a signature for the passed transaction according to
// the data within the passed SignDescriptor.
//
// This is a part of the WalletController interface.
func (b *BtcWallet) SignOutputRaw(tx *wire.MsgTx,
signDesc *input.SignDescriptor) (input.Signature, error) {
witnessScript := signDesc.WitnessScript
// First attempt to fetch the private key which corresponds to the
// specified public key.
privKey, err := b.fetchPrivKey(&signDesc.KeyDesc)
if err != nil {
return nil, err
}
// If a tweak (single or double) is specified, then we'll need to use
// this tweak to derive the final private key to be used for signing
// this output.
privKey, err = maybeTweakPrivKey(signDesc, privKey)
if err != nil {
return nil, err
}
// TODO(roasbeef): generate sighash midstate if not present?
amt := signDesc.Output.Value
sig, err := txscript.RawTxInWitnessSignature(
tx, signDesc.SigHashes, signDesc.InputIndex, amt,
witnessScript, signDesc.HashType, privKey,
)
if err != nil {
return nil, err
}
// Chop off the sighash flag at the end of the signature.
return btcec.ParseDERSignature(sig[:len(sig)-1], btcec.S256())
}
// ComputeInputScript generates a complete InputScript for the passed
// transaction with the signature as defined within the passed SignDescriptor.
// This method is capable of generating the proper input script for both
// regular p2wkh output and p2wkh outputs nested within a regular p2sh output.
//
// This is a part of the WalletController interface.
func (b *BtcWallet) ComputeInputScript(tx *wire.MsgTx,
signDesc *input.SignDescriptor) (*input.Script, error) {
// If a tweak (single or double) is specified, then we'll need to use
// this tweak to derive the final private key to be used for signing
// this output.
privKeyTweaker := func(k *btcec.PrivateKey) (*btcec.PrivateKey, error) {
return maybeTweakPrivKey(signDesc, k)
}
// Let the wallet compute the input script now.
witness, sigScript, err := b.wallet.ComputeInputScript(
tx, signDesc.Output, signDesc.InputIndex, signDesc.SigHashes,
signDesc.HashType, privKeyTweaker,
)
if err != nil {
return nil, err
}
return &input.Script{
Witness: witness,
SigScript: sigScript,
}, nil
}
// A compile time check to ensure that BtcWallet implements the Signer
// interface.
var _ input.Signer = (*BtcWallet)(nil)
// SignMessage attempts to sign a target message with the private key that
// corresponds to the passed public key. If the target private key is unable to
// be found, then an error will be returned. The actual digest signed is the
// double SHA-256 of the passed message.
//
// NOTE: This is a part of the MessageSigner interface.
func (b *BtcWallet) SignMessage(pubKey *btcec.PublicKey,
msg []byte) (input.Signature, error) {
// First attempt to fetch the private key which corresponds to the
// specified public key.
privKey, err := b.fetchPrivKey(&keychain.KeyDescriptor{
PubKey: pubKey,
})
if err != nil {
return nil, err
}
// Double hash and sign the data.
msgDigest := chainhash.DoubleHashB(msg)
sign, err := privKey.Sign(msgDigest)
if err != nil {
return nil, errors.Errorf("unable sign the message: %v", err)
}
return sign, nil
}
// A compile time check to ensure that BtcWallet implements the MessageSigner
// interface.
var _ lnwallet.MessageSigner = (*BtcWallet)(nil)