Skip to content

Commit 5c4b3a2

Browse files
committed
cln+lnd: implement signer interface
1 parent 3d432f7 commit 5c4b3a2

File tree

2 files changed

+192
-4
lines changed

2 files changed

+192
-4
lines changed

cln/signer.go

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
package cln
2+
3+
import (
4+
"errors"
5+
"fmt"
6+
7+
"github.com/btcsuite/btcd/btcec/v2"
8+
"github.com/btcsuite/btcd/btcutil/psbt"
9+
"github.com/btcsuite/btcd/txscript"
10+
"github.com/btcsuite/btcd/wire"
11+
"github.com/btcsuite/btcwallet/wallet"
12+
"github.com/lightninglabs/chantools/lnd"
13+
"github.com/lightningnetwork/lnd/input"
14+
"github.com/lightningnetwork/lnd/keychain"
15+
)
16+
17+
type Signer struct {
18+
HsmSecret [32]byte
19+
}
20+
21+
func (s *Signer) SignOutputRaw(tx *wire.MsgTx,
22+
signDesc *input.SignDescriptor) (input.Signature, error) {
23+
24+
// First attempt to fetch the private key which corresponds to the
25+
// specified public key.
26+
privKey, err := s.FetchPrivateKey(&signDesc.KeyDesc)
27+
if err != nil {
28+
return nil, err
29+
}
30+
31+
return lnd.SignOutputRawWithPrivateKey(tx, signDesc, privKey)
32+
}
33+
34+
func (s *Signer) FetchPrivateKey(
35+
descriptor *keychain.KeyDescriptor) (*btcec.PrivateKey, error) {
36+
37+
_, privKey, err := DeriveKeyPair(s.HsmSecret, descriptor)
38+
return privKey, err
39+
}
40+
41+
func (s *Signer) FindMultisigKey(targetPubkey, peerPubKey *btcec.PublicKey,
42+
maxNumKeys uint32) (*keychain.KeyDescriptor, error) {
43+
44+
// Loop through the local multisig keys to find the target key.
45+
for index := range maxNumKeys {
46+
privKey, err := s.FetchPrivateKey(&keychain.KeyDescriptor{
47+
PubKey: peerPubKey,
48+
KeyLocator: keychain.KeyLocator{
49+
Family: keychain.KeyFamilyMultiSig,
50+
Index: index,
51+
},
52+
})
53+
if err != nil {
54+
return nil, fmt.Errorf("error deriving funding "+
55+
"private key: %w", err)
56+
}
57+
58+
currentPubkey := privKey.PubKey()
59+
if !targetPubkey.IsEqual(currentPubkey) {
60+
continue
61+
}
62+
63+
return &keychain.KeyDescriptor{
64+
PubKey: peerPubKey,
65+
KeyLocator: keychain.KeyLocator{
66+
Family: keychain.KeyFamilyMultiSig,
67+
Index: index,
68+
},
69+
}, nil
70+
}
71+
72+
return nil, errors.New("no matching pubkeys found")
73+
}
74+
75+
func (s *Signer) AddPartialSignature(packet *psbt.Packet,
76+
keyDesc keychain.KeyDescriptor, utxo *wire.TxOut, witnessScript []byte,
77+
inputIndex int) error {
78+
79+
// Now we add our partial signature.
80+
prevOutFetcher := wallet.PsbtPrevOutputFetcher(packet)
81+
signDesc := &input.SignDescriptor{
82+
KeyDesc: keyDesc,
83+
WitnessScript: witnessScript,
84+
Output: utxo,
85+
InputIndex: inputIndex,
86+
HashType: txscript.SigHashAll,
87+
PrevOutputFetcher: prevOutFetcher,
88+
SigHashes: txscript.NewTxSigHashes(
89+
packet.UnsignedTx, prevOutFetcher,
90+
),
91+
}
92+
ourSigRaw, err := s.SignOutputRaw(packet.UnsignedTx, signDesc)
93+
if err != nil {
94+
return fmt.Errorf("error signing with our key: %w", err)
95+
}
96+
ourSig := append(ourSigRaw.Serialize(), byte(txscript.SigHashAll))
97+
98+
// Because of the way we derive keys in CLN, the public key in the key
99+
// descriptor is the peer's public key, not our own. So we need to
100+
// derive our own public key from the private key.
101+
ourPrivKey, err := s.FetchPrivateKey(&keyDesc)
102+
if err != nil {
103+
return fmt.Errorf("error fetching private key for descriptor "+
104+
"%v: %w", keyDesc, err)
105+
}
106+
ourPubKey := ourPrivKey.PubKey()
107+
108+
// Great, we were able to create our sig, let's add it to the PSBT.
109+
updater, err := psbt.NewUpdater(packet)
110+
if err != nil {
111+
return fmt.Errorf("error creating PSBT updater: %w", err)
112+
}
113+
status, err := updater.Sign(
114+
inputIndex, ourSig, ourPubKey.SerializeCompressed(), nil,
115+
witnessScript,
116+
)
117+
if err != nil {
118+
return fmt.Errorf("error adding signature to PSBT: %w", err)
119+
}
120+
if status != 0 {
121+
return fmt.Errorf("unexpected status for signature update, "+
122+
"got %d wanted 0", status)
123+
}
124+
125+
return nil
126+
}
127+
128+
var _ lnd.ChannelSigner = (*Signer)(nil)

lnd/signer.go

Lines changed: 64 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,21 @@ import (
2020
"github.com/lightningnetwork/lnd/keychain"
2121
)
2222

23+
type ChannelSigner interface {
24+
SignOutputRaw(tx *wire.MsgTx,
25+
signDesc *input.SignDescriptor) (input.Signature, error)
26+
27+
FetchPrivateKey(descriptor *keychain.KeyDescriptor) (
28+
*btcec.PrivateKey, error)
29+
30+
FindMultisigKey(targetPubkey, peerPubKey *btcec.PublicKey,
31+
maxNumKeys uint32) (*keychain.KeyDescriptor, error)
32+
33+
AddPartialSignature(packet *psbt.Packet,
34+
keyDesc keychain.KeyDescriptor, utxo *wire.TxOut,
35+
witnessScript []byte, inputIndex int) error
36+
}
37+
2338
type Signer struct {
2439
*input.MusigSessionManager
2540

@@ -37,10 +52,10 @@ func (s *Signer) SignOutputRaw(tx *wire.MsgTx,
3752
return nil, err
3853
}
3954

40-
return s.SignOutputRawWithPrivateKey(tx, signDesc, privKey)
55+
return SignOutputRawWithPrivateKey(tx, signDesc, privKey)
4156
}
4257

43-
func (s *Signer) SignOutputRawWithPrivateKey(tx *wire.MsgTx,
58+
func SignOutputRawWithPrivateKey(tx *wire.MsgTx,
4459
signDesc *input.SignDescriptor,
4560
privKey *secp256k1.PrivateKey) (input.Signature, error) {
4661

@@ -140,6 +155,51 @@ func (s *Signer) FetchPrivateKey(descriptor *keychain.KeyDescriptor) (
140155
return key.ECPrivKey()
141156
}
142157

158+
func (s *Signer) FindMultisigKey(targetPubkey, _ *btcec.PublicKey,
159+
maxNumKeys uint32) (*keychain.KeyDescriptor, error) {
160+
161+
// First, we need to derive the correct branch from the local root key.
162+
multisigBranch, err := DeriveChildren(s.ExtendedKey, []uint32{
163+
HardenedKeyStart + uint32(keychain.BIP0043Purpose),
164+
HardenedKeyStart + s.ChainParams.HDCoinType,
165+
HardenedKeyStart + uint32(keychain.KeyFamilyMultiSig),
166+
0,
167+
})
168+
if err != nil {
169+
return nil, fmt.Errorf("could not derive local multisig key: "+
170+
"%w", err)
171+
}
172+
173+
// Loop through the local multisig keys to find the target key.
174+
for index := range maxNumKeys {
175+
currentKey, err := multisigBranch.DeriveNonStandard(index)
176+
if err != nil {
177+
return nil, fmt.Errorf("error deriving child key: %w",
178+
err)
179+
}
180+
181+
currentPubkey, err := currentKey.ECPubKey()
182+
if err != nil {
183+
return nil, fmt.Errorf("error deriving public key: %w",
184+
err)
185+
}
186+
187+
if !targetPubkey.IsEqual(currentPubkey) {
188+
continue
189+
}
190+
191+
return &keychain.KeyDescriptor{
192+
PubKey: currentPubkey,
193+
KeyLocator: keychain.KeyLocator{
194+
Family: keychain.KeyFamilyMultiSig,
195+
Index: index,
196+
},
197+
}, nil
198+
}
199+
200+
return nil, errors.New("no matching pubkeys found")
201+
}
202+
143203
func (s *Signer) AddPartialSignature(packet *psbt.Packet,
144204
keyDesc keychain.KeyDescriptor, utxo *wire.TxOut, witnessScript []byte,
145205
inputIndex int) error {
@@ -199,7 +259,7 @@ func (s *Signer) AddPartialSignatureForPrivateKey(packet *psbt.Packet,
199259
packet.UnsignedTx, prevOutFetcher,
200260
),
201261
}
202-
ourSigRaw, err := s.SignOutputRawWithPrivateKey(
262+
ourSigRaw, err := SignOutputRawWithPrivateKey(
203263
packet.UnsignedTx, signDesc, privateKey,
204264
)
205265
if err != nil {
@@ -258,7 +318,7 @@ func (s *Signer) AddTaprootSignature(packet *psbt.Packet, inputIndex int,
258318
signDesc.TapTweak = pIn.TaprootMerkleRoot
259319
}
260320

261-
ourSigRaw, err := s.SignOutputRawWithPrivateKey(
321+
ourSigRaw, err := SignOutputRawWithPrivateKey(
262322
packet.UnsignedTx, signDesc, privateKey,
263323
)
264324
if err != nil {

0 commit comments

Comments
 (0)