@@ -104,7 +104,7 @@ func NewAuxChanCloser(cfg AuxChanCloserCfg) *AuxChanCloser {
104104// createCloseAlloc is a helper function that creates an allocation for an asset
105105// close. This does not set a script key, as the script key will be set for each
106106// packet after the coins have been distributed.
107- func createCloseAlloc (isLocal , isInitiator bool , outputSum uint64 ,
107+ func createCloseAlloc (isLocal bool , outputSum uint64 ,
108108 shutdownMsg tapchannelmsg.AuxShutdownMsg ) (* tapsend.Allocation , error ) {
109109
110110 // The sort pkScript for the allocation will just be the internal key,
@@ -146,7 +146,6 @@ func createCloseAlloc(isLocal, isInitiator bool, outputSum uint64,
146146
147147 return tapsend .CommitAllocationToRemote
148148 }(),
149- SplitRoot : isInitiator ,
150149 InternalKey : shutdownMsg .AssetInternalKey .Val ,
151150 GenScriptKey : scriptKeyGen ,
152151 Amount : outputSum ,
@@ -157,24 +156,58 @@ func createCloseAlloc(isLocal, isInitiator bool, outputSum uint64,
157156 }, nil
158157}
159158
160- // fundingSpendWitness creates a complete witness to spend the OP_TRUE funding
161- // script of an asset funding output .
162- func fundingSpendWitness () lfn. Result [wire. TxWitness ] {
163- fundingScriptTree := tapscript . NewChannelFundingScriptTree ()
159+ // signCommitVirtualPackets signs the commit virtual packets with the funding
160+ // witness, which is just the script and control block for the OP_TRUE spend .
161+ func signCommitVirtualPackets ( ctx context. Context ,
162+ vPackets [] * tappsbt. VPacket ) error {
164163
165- tapscriptTree := fundingScriptTree .TapscriptTree
166- ctrlBlock := tapscriptTree .LeafMerkleProofs [0 ].ToControlBlock (
167- & input .TaprootNUMSKey ,
168- )
169- ctrlBlockBytes , err := ctrlBlock .ToBytes ()
170- if err != nil {
171- return lfn .Errf [wire.TxWitness ]("unable to serialize control " +
172- "block: %w" , err )
164+ useUniqueScriptKey := len (vPackets ) > 1
165+ for idx := range vPackets {
166+ assetID , err := vPackets [idx ].AssetID ()
167+ if err != nil {
168+ return fmt .Errorf ("unable to get asset ID: %w" , err )
169+ }
170+
171+ // First, we'll prepare the funding witness which includes the
172+ // OP_TRUE ctrl block.
173+ fundingWitness , err := tapscript .ChannelFundingSpendWitness (
174+ useUniqueScriptKey , assetID ,
175+ )
176+ if err != nil {
177+ return fmt .Errorf ("unable to make funding witness: %w" ,
178+ err )
179+ }
180+
181+ err = tapsend .PrepareOutputAssets (ctx , vPackets [idx ])
182+ if err != nil {
183+ return fmt .Errorf ("unable to prepare output " +
184+ "assets: %w" , err )
185+ }
186+
187+ // With the packets prepared, we'll swap in the correct witness
188+ // for each of them. We need to do this _after_ calling
189+ // PrepareOutputAsset, because that method will overwrite any
190+ // asset in the virtual outputs. Which means we'll also need to
191+ // set the witness on _every_ output of the packet, to make sure
192+ // each split output's root asset reference also gets the
193+ // correct witness.
194+ for outIdx := range vPackets [idx ].Outputs {
195+ outAsset := vPackets [idx ].Outputs [outIdx ].Asset
196+
197+ // There is always only a single input, as we're
198+ // spending a single funding output w/ each vPkt.
199+ const inputIndex = 0
200+ err := outAsset .UpdateTxWitness (
201+ inputIndex , fundingWitness ,
202+ )
203+ if err != nil {
204+ return fmt .Errorf ("error updating witness: %w" ,
205+ err )
206+ }
207+ }
173208 }
174209
175- return lfn .Ok (wire.TxWitness {
176- tapscript .AnyoneCanSpendScript (), ctrlBlockBytes ,
177- })
210+ return nil
178211}
179212
180213// AuxCloseOutputs returns the set of close outputs to use for this co-op close
@@ -273,7 +306,7 @@ func (a *AuxChanCloser) AuxCloseOutputs(
273306 remoteSum := fn .Reduce (commitState .RemoteAssets .Val .Outputs , sumAmounts )
274307 if localSum > 0 {
275308 localAlloc , err = createCloseAlloc (
276- true , desc . Initiator , localSum , localShutdown ,
309+ true , localSum , localShutdown ,
277310 )
278311 if err != nil {
279312 return none , err
@@ -285,7 +318,7 @@ func (a *AuxChanCloser) AuxCloseOutputs(
285318 }
286319 if remoteSum > 0 {
287320 remoteAlloc , err = createCloseAlloc (
288- false , ! desc . Initiator , remoteSum , remoteShutdown ,
321+ false , remoteSum , remoteShutdown ,
289322 )
290323 if err != nil {
291324 return none , err
@@ -378,35 +411,12 @@ func (a *AuxChanCloser) AuxCloseOutputs(
378411 return none , fmt .Errorf ("unable to distribute coins: %w" , err )
379412 }
380413
381- // With the vPackets created we'll now prepare all the split
382- // information encoded in the vPackets.
383- fundingWitness , err := fundingSpendWitness ().Unpack ()
384- if err != nil {
385- return none , fmt .Errorf ("unable to make funding " +
386- "witness: %w" , err )
387- }
388- ctx := context .Background ()
389- for idx := range vPackets {
390- err := tapsend .PrepareOutputAssets (ctx , vPackets [idx ])
391- if err != nil {
392- return none , fmt .Errorf ("unable to prepare output " +
393- "assets: %w" , err )
394- }
395-
396- for outIdx := range vPackets [idx ].Outputs {
397- outAsset := vPackets [idx ].Outputs [outIdx ].Asset
398-
399- // There is always only a single input, which is the
400- // funding output.
401- const inputIndex = 0
402- err := outAsset .UpdateTxWitness (
403- inputIndex , fundingWitness ,
404- )
405- if err != nil {
406- return none , fmt .Errorf ("error updating " +
407- "witness: %w" , err )
408- }
409- }
414+ // We can now add the witness for the OP_TRUE spend of the commitment
415+ // output to the vPackets.
416+ ctxb := context .Background ()
417+ if err := signCommitVirtualPackets (ctxb , vPackets ); err != nil {
418+ return none , fmt .Errorf ("error signing commit virtual " +
419+ "packets: %w" , err )
410420 }
411421
412422 // With the outputs prepared, we can now create the set of output
@@ -484,8 +494,9 @@ func (a *AuxChanCloser) ShutdownBlob(
484494 none := lfn .None [lnwire.CustomRecords ]()
485495
486496 // If there's no custom blob, then we don't need to do anything.
487- if req .CommitBlob .IsNone () {
488- log .Debugf ("No commit blob for ChannelPoint(%v)" , req .ChanPoint )
497+ if req .FundingBlob .IsNone () {
498+ log .Debugf ("No funding blob for ChannelPoint(%v)" ,
499+ req .ChanPoint )
489500 return none , nil
490501 }
491502
@@ -500,16 +511,16 @@ func (a *AuxChanCloser) ShutdownBlob(
500511 log .Infof ("Creating shutdown blob for close of ChannelPoint(%v)" ,
501512 req .ChanPoint )
502513
503- // Otherwise, we'll decode the commitment , so we can examine the current
504- // state .
505- var commitState tapchannelmsg.Commitment
506- err = lfn .MapOptionZ (req .CommitBlob , func (blob tlv.Blob ) error {
507- c , err := tapchannelmsg .DecodeCommitment (blob )
514+ // Otherwise, we'll decode the funding state , so we can examine the
515+ // different asset IDs in the channel .
516+ var fundingState tapchannelmsg.OpenChannel
517+ err = lfn .MapOptionZ (req .FundingBlob , func (blob tlv.Blob ) error {
518+ c , err := tapchannelmsg .DecodeOpenChannel (blob )
508519 if err != nil {
509520 return err
510521 }
511522
512- commitState = * c
523+ fundingState = * c
513524
514525 return nil
515526 })
@@ -528,38 +539,34 @@ func (a *AuxChanCloser) ShutdownBlob(
528539 return none , err
529540 }
530541
531- // Next, we'll collect all the assets that we own in this channel.
532- assets := commitState .LocalAssets .Val .Outputs
542+ // Next, we'll collect all the asset IDs that were committed to the
543+ // channel.
544+ assetIDs := fn .Map (
545+ fundingState .FundedAssets .Val .Outputs ,
546+ func (o * tapchannelmsg.AssetOutput ) asset.ID {
547+ return o .AssetID .Val
548+ },
549+ )
533550
534551 // Now that we have all the asset IDs, we'll query for a new key for
535552 // each of them which we'll use as both the internal key and the script
536553 // key.
537554 scriptKeys := make (tapchannelmsg.ScriptKeyMap )
538- for idx := range assets {
539- channelAsset := assets [idx ]
540-
555+ for _ , assetID := range assetIDs {
541556 newKey , err := a .cfg .AddrBook .NextScriptKey (
542557 ctx , asset .TaprootAssetsKeyFamily ,
543558 )
544559 if err != nil {
545560 return none , err
546561 }
547562
548- // We now add the a
549- // TODO(guggero): This only works if there's only a single asset
550- // in the channel. We need to extend this to support multiple
551- // assets.
552- _ , err = a .cfg .AddrBook .NewAddressWithKeys (
553- ctx , address .V1 , channelAsset .AssetID .Val ,
554- channelAsset .Amount .Val , newKey , newInternalKey , nil ,
555- * a .cfg .DefaultCourierAddr ,
556- )
563+ err = a .cfg .AddrBook .InsertScriptKey (ctx , newKey , true )
557564 if err != nil {
558- return none , fmt .Errorf ("error adding new address: %w" ,
559- err )
565+ return none , fmt .Errorf ("error declaring script key: " +
566+ "%w" , err )
560567 }
561568
562- scriptKeys [channelAsset . AssetID . Val ] = * newKey .PubKey
569+ scriptKeys [assetID ] = * newKey .PubKey
563570 }
564571
565572 // Finally, we'll map the extra shutdown info to a TLV record map we
0 commit comments