44 "bytes"
55 "encoding/base64"
66 "encoding/binary"
7+ "errors"
78 "fmt"
89 "os"
910
@@ -15,6 +16,10 @@ import (
1516 "github.com/spf13/cobra"
1617)
1718
19+ var (
20+ errNoPathFound = fmt .Errorf ("no matching derivation path found" )
21+ )
22+
1823type signPSBTCommand struct {
1924 Psbt string
2025 FromRawPsbtFile string
@@ -134,93 +139,117 @@ func (c *signPSBTCommand) Execute(_ *cobra.Command, _ []string) error {
134139func signPsbt (rootKey * hdkeychain.ExtendedKey ,
135140 packet * psbt.Packet , signer * lnd.Signer ) error {
136141
137- // Check that we have an input with a derivation path that belongs to
138- // the root key.
139- derivationPath , inputIndex , err := findMatchingDerivationPath (
140- rootKey , packet ,
141- )
142- if err != nil {
143- return fmt .Errorf ("could not find matching derivation path: %w" ,
144- err )
145- }
142+ for inputIndex := range packet .Inputs {
143+ pIn := & packet .Inputs [inputIndex ]
146144
147- if len (derivationPath ) < 5 {
148- return fmt .Errorf ("invalid derivation path, expected at least " +
149- "5 elements, got %d" , len (derivationPath ))
150- }
145+ // Check that we have an input with a derivation path that
146+ // belongs to the root key.
147+ derivationPath , err := findMatchingDerivationPath (rootKey , pIn )
148+ if errors .Is (err , errNoPathFound ) {
149+ log .Infof ("No matching derivation path found for " +
150+ "input %d, skipping" , inputIndex )
151+ continue
152+ }
153+ if err != nil {
154+ return fmt .Errorf ("could not find matching derivation " +
155+ "path: %w" , err )
156+ }
151157
152- localKey , err := lnd .DeriveChildren (rootKey , derivationPath )
153- if err != nil {
154- return fmt .Errorf ("could not derive local key: %w" , err )
155- }
158+ if len (derivationPath ) < 5 {
159+ return fmt .Errorf ("invalid derivation path, expected " +
160+ "at least 5 elements, got %d" ,
161+ len (derivationPath ))
162+ }
156163
157- if packet .Inputs [inputIndex ].WitnessUtxo == nil {
158- return fmt .Errorf ("invalid PSBT, input %d is missing witness " +
159- "UTXO" , inputIndex )
160- }
161- utxo := packet .Inputs [inputIndex ].WitnessUtxo
162-
163- // The signing is a bit different for P2WPKH, we need to specify the
164- // pk script as the witness script.
165- var witnessScript []byte
166- if txscript .IsPayToWitnessPubKeyHash (utxo .PkScript ) {
167- witnessScript = utxo .PkScript
168- } else {
169- if len (packet .Inputs [inputIndex ].WitnessScript ) == 0 {
164+ localKey , err := lnd .DeriveChildren (rootKey , derivationPath )
165+ if err != nil {
166+ return fmt .Errorf ("could not derive local key: %w" , err )
167+ }
168+
169+ if pIn .WitnessUtxo == nil {
170170 return fmt .Errorf ("invalid PSBT, input %d is missing " +
171- "witness script" , inputIndex )
171+ "witness UTXO" , inputIndex )
172+ }
173+ utxo := pIn .WitnessUtxo
174+
175+ // The signing is a bit different for P2WPKH, we need to specify
176+ // the pk script as the witness script.
177+ var witnessScript []byte
178+ if txscript .IsPayToWitnessPubKeyHash (utxo .PkScript ) {
179+ witnessScript = utxo .PkScript
180+ } else {
181+ if len (pIn .WitnessScript ) == 0 {
182+ return fmt .Errorf ("invalid PSBT, input %d is " +
183+ "missing witness script" , inputIndex )
184+ }
185+ witnessScript = pIn .WitnessScript
172186 }
173- witnessScript = packet .Inputs [inputIndex ].WitnessScript
174- }
175187
176- localPrivateKey , err := localKey .ECPrivKey ()
177- if err != nil {
178- return fmt .Errorf ("error getting private key: %w" , err )
179- }
180- err = signer .AddPartialSignatureForPrivateKey (
181- packet , localPrivateKey , utxo , witnessScript , inputIndex ,
182- )
183- if err != nil {
184- return fmt .Errorf ("error adding partial signature: %w" , err )
188+ localPrivateKey , err := localKey .ECPrivKey ()
189+ if err != nil {
190+ return fmt .Errorf ("error getting private key: %w" , err )
191+ }
192+
193+ // Do we already have a partial signature for our key?
194+ localPubKey := localPrivateKey .PubKey ().SerializeCompressed ()
195+ haveSig := false
196+ for _ , partialSig := range pIn .PartialSigs {
197+ if bytes .Equal (partialSig .PubKey , localPubKey ) {
198+ haveSig = true
199+ }
200+ }
201+ if haveSig {
202+ log .Infof ("Already have a partial signature for input " +
203+ "%d and local key %x, skipping" , inputIndex ,
204+ localPubKey )
205+ continue
206+ }
207+
208+ err = signer .AddPartialSignatureForPrivateKey (
209+ packet , localPrivateKey , utxo , witnessScript ,
210+ inputIndex ,
211+ )
212+ if err != nil {
213+ return fmt .Errorf ("error adding partial signature: %w" ,
214+ err )
215+ }
185216 }
186217
187218 return nil
188219}
189220
190221func findMatchingDerivationPath (rootKey * hdkeychain.ExtendedKey ,
191- packet * psbt.Packet ) ([]uint32 , int , error ) {
222+ pIn * psbt.PInput ) ([]uint32 , error ) {
192223
193224 pubKey , err := rootKey .ECPubKey ()
194225 if err != nil {
195- return nil , 0 , fmt .Errorf ("error getting public key: %w" , err )
226+ return nil , fmt .Errorf ("error getting public key: %w" , err )
196227 }
197228
198229 pubKeyHash := btcutil .Hash160 (pubKey .SerializeCompressed ())
199230 fingerprint := binary .LittleEndian .Uint32 (pubKeyHash [:4 ])
200231
201- for idx , input := range packet .Inputs {
202- if len (input .Bip32Derivation ) == 0 {
203- continue
204- }
232+ if len (pIn .Bip32Derivation ) == 0 {
233+ return nil , errNoPathFound
234+ }
205235
206- for _ , derivation := range input .Bip32Derivation {
207- // A special case where there is only a single
208- // derivation path and the master key fingerprint is not
209- // set, we assume we are the correct signer... This
210- // might not be correct, but we have no way of knowing.
211- if derivation .MasterKeyFingerprint == 0 &&
212- len (input .Bip32Derivation ) == 1 {
236+ for _ , derivation := range pIn .Bip32Derivation {
237+ // A special case where there is only a single derivation path
238+ // and the master key fingerprint is not set, we assume we are
239+ // the correct signer... This might not be correct, but we have
240+ // no way of knowing.
241+ if derivation .MasterKeyFingerprint == 0 &&
242+ len (pIn .Bip32Derivation ) == 1 {
213243
214- return derivation .Bip32Path , idx , nil
215- }
244+ return derivation .Bip32Path , nil
245+ }
216246
217- // The normal case, where a derivation path has the
218- // master fingerprint set.
219- if derivation .MasterKeyFingerprint == fingerprint {
220- return derivation .Bip32Path , idx , nil
221- }
247+ // The normal case, where a derivation path has the master
248+ // fingerprint set.
249+ if derivation .MasterKeyFingerprint == fingerprint {
250+ return derivation .Bip32Path , nil
222251 }
223252 }
224253
225- return nil , 0 , fmt . Errorf ( "no matching derivation path found" )
254+ return nil , errNoPathFound
226255}
0 commit comments