@@ -28,6 +28,16 @@ type SignerClient interface {
2828 signDescriptors []* SignDescriptor ,
2929 prevOutputs []* wire.TxOut ) ([][]byte , error )
3030
31+ // SignOutputRawKeyLocator is a copy of the SignOutputRaw that fixes a
32+ // specific issue around how the key locator is populated in the sign
33+ // descriptor. We copy this method instead of fixing the original to
34+ // make sure we don't break any existing applications that have already
35+ // adjusted themselves to use the specific behavior of the original
36+ // SignOutputRaw method.
37+ SignOutputRawKeyLocator (ctx context.Context , tx * wire.MsgTx ,
38+ signDescriptors []* SignDescriptor ,
39+ prevOutputs []* wire.TxOut ) ([][]byte , error )
40+
3141 // ComputeInputScript generates the proper input script for P2WPKH
3242 // output and NP2WPKH outputs. This method only requires that the
3343 // `Output`, `HashType`, `SigHashes` and `InputIndex` fields are
@@ -215,26 +225,70 @@ func (s *signerClient) RawClientWithMacAuth(
215225 return s .signerMac .WithMacaroonAuth (parentCtx ), s .timeout , s .client
216226}
217227
218- func marshallSignDescriptors (
219- signDescriptors [] * SignDescriptor ) []* signrpc.SignDescriptor {
228+ func marshallSignDescriptors (signDescriptors [] * SignDescriptor ,
229+ fullDescriptors bool ) []* signrpc.SignDescriptor {
220230
221- rpcSignDescs := make ([]* signrpc.SignDescriptor , len (signDescriptors ))
222- for i , signDesc := range signDescriptors {
223- var keyBytes []byte
224- var keyLocator * signrpc.KeyLocator
225- if signDesc .KeyDesc .PubKey != nil {
226- keyBytes = signDesc .KeyDesc .PubKey .SerializeCompressed ()
231+ // partialDescriptor is a helper method that creates a partially
232+ // populated sign descriptor that is backward compatible with the way
233+ // some applications like Loop expect the call to lnd to be made. This
234+ // function only populates _either_ the public key or the key locator in
235+ // the descriptor, but not both.
236+ partialDescriptor := func (
237+ d keychain.KeyDescriptor ) * signrpc.KeyDescriptor {
238+
239+ keyDesc := & signrpc.KeyDescriptor {}
240+ if d .PubKey != nil {
241+ keyDesc .RawKeyBytes = d .PubKey .SerializeCompressed ()
227242 } else {
228- keyLocator = & signrpc.KeyLocator {
229- KeyFamily : int32 (
230- signDesc .KeyDesc .KeyLocator .Family ,
231- ),
232- KeyIndex : int32 (
233- signDesc .KeyDesc .KeyLocator .Index ,
234- ),
243+ keyDesc .KeyLoc = & signrpc.KeyLocator {
244+ KeyFamily : int32 (d .KeyLocator .Family ),
245+ KeyIndex : int32 (d .KeyLocator .Index ),
235246 }
236247 }
237248
249+ return keyDesc
250+ }
251+
252+ // fullDescriptor is a helper method that creates a fully populated sign
253+ // descriptor that includes both the public key and the key locator (if
254+ // available). For the locator we explicitly check that both the family
255+ // _and_ the index is non-zero. In some applications it's possible that
256+ // the family is always set (because only a specific family is used),
257+ // but the index might be zero because it's the first key, or because it
258+ // isn't known at that particular moment.
259+ // We aim to be compatible with this method in lnd's wallet:
260+ // https://github.com/lightningnetwork/lnd/blob/master/lnwallet/btcwallet/signer.go#L286
261+ // Because we know all custom families (0 to 255) are derived at wallet
262+ // creation, and the very first index of each family/account is always
263+ // derived, we know that only using the public key for that very first
264+ // index will work. But for a freshly initialized wallet (e.g. restored
265+ // from seed), we won't know any indexes greater than 0, so we _need_ to
266+ // also specify the key locator and not just the public key.
267+ fullDescriptor := func (
268+ d keychain.KeyDescriptor ) * signrpc.KeyDescriptor {
269+
270+ keyDesc := & signrpc.KeyDescriptor {}
271+ if d .PubKey != nil {
272+ keyDesc .RawKeyBytes = d .PubKey .SerializeCompressed ()
273+ }
274+
275+ if d .KeyLocator .Family != 0 && d .KeyLocator .Index != 0 {
276+ keyDesc .KeyLoc = & signrpc.KeyLocator {
277+ KeyFamily : int32 (d .KeyLocator .Family ),
278+ KeyIndex : int32 (d .KeyLocator .Index ),
279+ }
280+ }
281+
282+ return keyDesc
283+ }
284+
285+ rpcSignDescs := make ([]* signrpc.SignDescriptor , len (signDescriptors ))
286+ for i , signDesc := range signDescriptors {
287+ keyDesc := partialDescriptor (signDesc .KeyDesc )
288+ if fullDescriptors {
289+ keyDesc = fullDescriptor (signDesc .KeyDesc )
290+ }
291+
238292 var doubleTweak []byte
239293 if signDesc .DoubleTweak != nil {
240294 doubleTweak = signDesc .DoubleTweak .Serialize ()
@@ -247,12 +301,9 @@ func marshallSignDescriptors(
247301 PkScript : signDesc .Output .PkScript ,
248302 Value : signDesc .Output .Value ,
249303 },
250- Sighash : uint32 (signDesc .HashType ),
251- InputIndex : int32 (signDesc .InputIndex ),
252- KeyDesc : & signrpc.KeyDescriptor {
253- RawKeyBytes : keyBytes ,
254- KeyLoc : keyLocator ,
255- },
304+ Sighash : uint32 (signDesc .HashType ),
305+ InputIndex : int32 (signDesc .InputIndex ),
306+ KeyDesc : keyDesc ,
256307 SingleTweak : signDesc .SingleTweak ,
257308 DoubleTweak : doubleTweak ,
258309 TapTweak : signDesc .TapTweak ,
@@ -283,11 +334,32 @@ func (s *signerClient) SignOutputRaw(ctx context.Context, tx *wire.MsgTx,
283334 signDescriptors []* SignDescriptor , prevOutputs []* wire.TxOut ) ([][]byte ,
284335 error ) {
285336
337+ return s .signOutputRaw (ctx , tx , signDescriptors , prevOutputs , false )
338+ }
339+
340+ // SignOutputRawKeyLocator is a copy of the SignOutputRaw that fixes a specific
341+ // issue around how the key locator is populated in the sign descriptor. We copy
342+ // this method instead of fixing the original to make sure we don't break any
343+ // existing applications that have already adjusted themselves to use the
344+ // specific behavior of the original SignOutputRaw method.
345+ func (s * signerClient ) SignOutputRawKeyLocator (ctx context.Context ,
346+ tx * wire.MsgTx , signDescriptors []* SignDescriptor ,
347+ prevOutputs []* wire.TxOut ) ([][]byte , error ) {
348+
349+ return s .signOutputRaw (ctx , tx , signDescriptors , prevOutputs , true )
350+ }
351+
352+ // signOutputRaw is a helper method that performs the actual signing of the
353+ // transaction.
354+ func (s * signerClient ) signOutputRaw (ctx context.Context , tx * wire.MsgTx ,
355+ signDescriptors []* SignDescriptor , prevOutputs []* wire.TxOut ,
356+ fullDescriptor bool ) ([][]byte , error ) {
357+
286358 txRaw , err := encodeTx (tx )
287359 if err != nil {
288360 return nil , err
289361 }
290- rpcSignDescs := marshallSignDescriptors (signDescriptors )
362+ rpcSignDescs := marshallSignDescriptors (signDescriptors , fullDescriptor )
291363 rpcPrevOutputs := marshallTxOut (prevOutputs )
292364
293365 rpcCtx , cancel := context .WithTimeout (ctx , s .timeout )
@@ -321,7 +393,7 @@ func (s *signerClient) ComputeInputScript(ctx context.Context, tx *wire.MsgTx,
321393 if err != nil {
322394 return nil , err
323395 }
324- rpcSignDescs := marshallSignDescriptors (signDescriptors )
396+ rpcSignDescs := marshallSignDescriptors (signDescriptors , false )
325397 rpcPrevOutputs := marshallTxOut (prevOutputs )
326398
327399 rpcCtx , cancel := context .WithTimeout (ctx , s .timeout )
0 commit comments