@@ -85,29 +85,19 @@ func createFundedPacketWithInputs(ctx context.Context, exporter proof.Exporter,
85
85
// a new internal key for the anchor outputs. We assume any output that
86
86
// hasn't got an internal key set is going to a local anchor, and we
87
87
// provide the internal key for that.
88
- for idx := range vPkt .Outputs {
89
- vOut := vPkt .Outputs [idx ]
90
- if vOut .AnchorOutputInternalKey != nil {
91
- continue
92
- }
93
-
94
- newInternalKey , err := keyRing .DeriveNextKey (
95
- ctx , asset .TaprootAssetsKeyFamily ,
96
- )
97
- if err != nil {
98
- return nil , err
99
- }
100
- vOut .SetAnchorInternalKey (
101
- newInternalKey , vPkt .ChainParams .HDCoinType ,
102
- )
88
+ packets := []* tappsbt.VPacket {vPkt }
89
+ err = generateOutputAnchorInternalKeys (ctx , packets , keyRing )
90
+ if err != nil {
91
+ return nil , fmt .Errorf ("unable to generate output anchor " +
92
+ "internal keys: %w" , err )
103
93
}
104
94
105
95
if err := tapsend .PrepareOutputAssets (ctx , vPkt ); err != nil {
106
96
return nil , fmt .Errorf ("unable to prepare outputs: %w" , err )
107
97
}
108
98
109
99
return & FundedVPacket {
110
- VPackets : [] * tappsbt. VPacket { vPkt } ,
100
+ VPackets : packets ,
111
101
InputCommitments : inputCommitments ,
112
102
}, nil
113
103
}
@@ -238,6 +228,117 @@ func createChangeOutput(ctx context.Context, vPkt *tappsbt.VPacket,
238
228
return nil
239
229
}
240
230
231
+ // generateOutputAnchorInternalKeys generates internal keys for the anchor
232
+ // outputs of the given virtual packets. If an output already has an internal
233
+ // key set, it will be used. If not, a new key will be derived and set.
234
+ // At the same time we make sure that we don't use different keys for the same
235
+ // anchor output index in case there are multiple packets.
236
+ func generateOutputAnchorInternalKeys (ctx context.Context ,
237
+ packets []* tappsbt.VPacket , keyRing KeyRing ) error {
238
+
239
+ // We need to make sure we don't use different keys for the same anchor
240
+ // output index in case there are multiple packets. So we'll keep track
241
+ // of any set keys here. This will be a merged set of existing and new
242
+ // keys.
243
+ anchorKeys := make (map [uint32 ]tappsbt.Anchor )
244
+
245
+ // addAnchorKey adds the anchor key to the map.
246
+ addAnchorKey := func (vOut * tappsbt.VOutput ) {
247
+ // nolint: lll
248
+ anchorKeys [vOut .AnchorOutputIndex ] = tappsbt.Anchor {
249
+ InternalKey : vOut .AnchorOutputInternalKey ,
250
+ Bip32Derivation : vOut .AnchorOutputBip32Derivation ,
251
+ TrBip32Derivation : vOut .AnchorOutputTaprootBip32Derivation ,
252
+ }
253
+ }
254
+
255
+ // extractAnchorKey is a helper function that extracts the anchor key
256
+ // from a virtual output and makes sure it is consistent with the
257
+ // existing anchor keys from previous outputs of the same or different
258
+ // packets.
259
+ extractAnchorKey := func (vOut * tappsbt.VOutput ) error {
260
+ if vOut .AnchorOutputInternalKey == nil {
261
+ return nil
262
+ }
263
+
264
+ anchorIndex := vOut .AnchorOutputIndex
265
+ anchorKey := vOut .AnchorOutputInternalKey
266
+
267
+ // Handle the case where we already have an anchor defined for
268
+ // this index.
269
+ if _ , ok := anchorKeys [anchorIndex ]; ok {
270
+ existingPubKey := anchorKeys [anchorIndex ].InternalKey
271
+ if ! existingPubKey .IsEqual (anchorKey ) {
272
+ return fmt .Errorf ("anchor output index %d " +
273
+ "already has a different internal key " +
274
+ "set: %x" , anchorIndex ,
275
+ existingPubKey .SerializeCompressed ())
276
+ }
277
+
278
+ // The keys are the same, so this is already correct.
279
+ return nil
280
+ }
281
+
282
+ // There is no anchor yet, so we add it to the map.
283
+ addAnchorKey (vOut )
284
+
285
+ return nil
286
+ }
287
+
288
+ // Do a first pass through all packets to collect all existing anchor
289
+ // keys. At the same time we make sure we don't already have diverging
290
+ // information.
291
+ for _ , vPkt := range packets {
292
+ for _ , vOut := range vPkt .Outputs {
293
+ if err := extractAnchorKey (vOut ); err != nil {
294
+ return err
295
+ }
296
+ }
297
+ }
298
+
299
+ // We now do a second pass through all packets and set the internal keys
300
+ // for all outputs that don't have one yet. If we don't have any key for
301
+ // an output index, we create a new one.
302
+ // nolint: lll
303
+ for _ , vPkt := range packets {
304
+ for idx := range vPkt .Outputs {
305
+ vOut := vPkt .Outputs [idx ]
306
+
307
+ // Skip any outputs that already have an internal key.
308
+ if vOut .AnchorOutputInternalKey != nil {
309
+ continue
310
+ }
311
+
312
+ // Check if we can use an existing key for this output
313
+ // index.
314
+ anchor , ok := anchorKeys [vOut .AnchorOutputIndex ]
315
+ if ok {
316
+ vOut .AnchorOutputInternalKey = anchor .InternalKey
317
+ vOut .AnchorOutputBip32Derivation = anchor .Bip32Derivation
318
+ vOut .AnchorOutputTaprootBip32Derivation = anchor .TrBip32Derivation
319
+
320
+ continue
321
+ }
322
+
323
+ newInternalKey , err := keyRing .DeriveNextKey (
324
+ ctx , asset .TaprootAssetsKeyFamily ,
325
+ )
326
+ if err != nil {
327
+ return err
328
+ }
329
+ vOut .SetAnchorInternalKey (
330
+ newInternalKey , vPkt .ChainParams .HDCoinType ,
331
+ )
332
+
333
+ // Store this anchor information in case we have other
334
+ // outputs in other packets that need it.
335
+ addAnchorKey (vOut )
336
+ }
337
+ }
338
+
339
+ return nil
340
+ }
341
+
241
342
// setVPacketInputs sets the inputs of the given vPkt to the given send eligible
242
343
// commitments. It also returns the assets that were used as inputs.
243
344
func setVPacketInputs (ctx context.Context , exporter proof.Exporter ,
0 commit comments