@@ -31,7 +31,6 @@ import (
31
31
"github.com/lightninglabs/taproot-assets/tapfreighter"
32
32
"github.com/lightninglabs/taproot-assets/tapgarden"
33
33
"github.com/lightninglabs/taproot-assets/tappsbt"
34
- "github.com/lightninglabs/taproot-assets/tapscript"
35
34
"github.com/lightninglabs/taproot-assets/tapsend"
36
35
"github.com/lightninglabs/taproot-assets/vm"
37
36
lfn "github.com/lightningnetwork/lnd/fn/v2"
@@ -758,27 +757,18 @@ func (f *FundingController) fundVirtualPacket(ctx context.Context,
758
757
amt )
759
758
760
759
// Our funding script key will be the OP_TRUE addr that we'll use as
761
- // the funding script on the asset level.
762
- fundingScriptTree := tapscript .NewChannelFundingScriptTree ()
763
- fundingTaprootKey , _ := schnorr .ParsePubKey (
764
- schnorr .SerializePubKey (fundingScriptTree .TaprootKey ),
760
+ // the funding script on the asset level. We start with this one in case
761
+ // there is only a single asset ID in the channel. This is to remain
762
+ // backward compatible with previous versions of the channel funding
763
+ // process, so updated users can still fund channels with a single asset
764
+ // ID with older clients. Also, we don't know what asset IDs we're going
765
+ // to be using, so we couldn't derive a unique funding script key for
766
+ // each asset ID yet anyway.
767
+ fundingScriptKey , err := deriveFundingScriptKey (
768
+ ctx , f .cfg .AddrBook , nil ,
765
769
)
766
- fundingScriptKey := asset.ScriptKey {
767
- PubKey : fundingTaprootKey ,
768
- TweakedScriptKey : & asset.TweakedScriptKey {
769
- RawKey : keychain.KeyDescriptor {
770
- PubKey : fundingScriptTree .InternalKey ,
771
- },
772
- Tweak : fundingScriptTree .TapscriptRoot ,
773
- },
774
- }
775
-
776
- // We'll also need to import the funding script key into the wallet so
777
- // the asset will be materialized in the asset table and show up in the
778
- // balance correctly.
779
- err := f .cfg .AddrBook .InsertScriptKey (ctx , fundingScriptKey , true )
780
770
if err != nil {
781
- return nil , fmt .Errorf ("unable to insert script key: %w" , err )
771
+ return nil , fmt .Errorf ("unable to derive script key: %w" , err )
782
772
}
783
773
784
774
// Next, we'll use the asset wallet to fund a new vPSBT which'll be
@@ -805,7 +795,62 @@ func (f *FundingController) fundVirtualPacket(ctx context.Context,
805
795
806
796
// Fund the packet. This will derive an anchor internal key for us, but
807
797
// we'll overwrite that later on.
808
- return f .cfg .AssetWallet .FundPacket (ctx , fundDesc , pktTemplate )
798
+ fundedPkt , err := f .cfg .AssetWallet .FundPacket (
799
+ ctx , fundDesc , pktTemplate ,
800
+ )
801
+ if err != nil {
802
+ return nil , fmt .Errorf ("unable to fund vPacket: %w" , err )
803
+ }
804
+
805
+ // If there was just a single virtual packet created, it means we only
806
+ // have a single asset ID in the channel, and we can proceed without any
807
+ // workarounds.
808
+ if len (fundedPkt .VPackets ) == 1 {
809
+ return fundedPkt , nil
810
+ }
811
+
812
+ // For channels with multiple asset IDs, we'll need to create unique
813
+ // funding script keys for each asset ID. Otherwise, the proofs for the
814
+ // assets will collide in the universe because of group key, script key
815
+ // and outpoint all being equal.
816
+ for _ , vPkt := range fundedPkt .VPackets {
817
+ assetID , err := vPkt .AssetID ()
818
+ if err != nil {
819
+ return nil , fmt .Errorf ("unable to get asset ID: %w" ,
820
+ err )
821
+ }
822
+
823
+ // If there's change from the funding output, it'll be in the
824
+ // split root output. If there's no change, there will be no
825
+ // split root output, since the virtual transfer is interactive.
826
+ // So in either case we just need to get the first non-root
827
+ // output.
828
+ fundingOut , err := vPkt .FirstNonSplitRootOutput ()
829
+ if err != nil {
830
+ return nil , fmt .Errorf ("unable to get first non split " +
831
+ "root output: %w" , err )
832
+ }
833
+
834
+ fundingScriptKey , err := deriveFundingScriptKey (
835
+ ctx , f .cfg .AddrBook , & assetID ,
836
+ )
837
+ if err != nil {
838
+ return nil , fmt .Errorf ("unable to derive script key: " +
839
+ "%w" , err )
840
+ }
841
+
842
+ // We now set the unique script key. This requires us to
843
+ // re-calculate the split commitments, so we'll do that right
844
+ // afterward.
845
+ fundingOut .ScriptKey = fundingScriptKey
846
+
847
+ if err := tapsend .PrepareOutputAssets (ctx , vPkt ); err != nil {
848
+ return nil , fmt .Errorf ("unable to prepare output " +
849
+ "assets after funding key update: %w" , err )
850
+ }
851
+ }
852
+
853
+ return fundedPkt , nil
809
854
}
810
855
811
856
// sendInputOwnershipProofs sends the input ownership proofs to the remote
@@ -1014,8 +1059,7 @@ func (f *FundingController) signAllVPackets(ctx context.Context,
1014
1059
// complete, but unsigned PSBT packet that can be used to create out asset
1015
1060
// channel.
1016
1061
func (f * FundingController ) anchorVPackets (fundedPkt * tapsend.FundedPsbt ,
1017
- allPackets []* tappsbt.VPacket ,
1018
- fundingScriptKey asset.ScriptKey ) ([]* proof.Proof , error ) {
1062
+ allPackets []* tappsbt.VPacket ) ([]* proof.Proof , error ) {
1019
1063
1020
1064
log .Infof ("Anchoring funding vPackets to funding PSBT" )
1021
1065
@@ -1062,10 +1106,13 @@ func (f *FundingController) anchorVPackets(fundedPkt *tapsend.FundedPsbt,
1062
1106
1063
1107
vPkt .Outputs [vOutIdx ].ProofSuffix = proofSuffix
1064
1108
1065
- if proofSuffix .Asset .ScriptKey .PubKey .IsEqual (
1066
- fundingScriptKey .PubKey ,
1067
- ) {
1068
-
1109
+ // Any output that isn't a split root output is a
1110
+ // channel funding output, so we'll store the proofs
1111
+ // for those outputs. If there is change, that will be
1112
+ // the split root output. And if there is no change,
1113
+ // there is no split root output, as it's an interactive
1114
+ // transfer.
1115
+ if vPkt .Outputs [vOutIdx ].Type == tappsbt .TypeSimple {
1069
1116
fundingProofs = append (
1070
1117
fundingProofs , proofSuffix ,
1071
1118
)
@@ -1252,10 +1299,8 @@ func (f *FundingController) completeChannelFunding(ctx context.Context,
1252
1299
// With all the vPackets signed, we'll now anchor them to the funding
1253
1300
// PSBT. This'll update all the pkScripts for our funding output and
1254
1301
// change.
1255
- fundingScriptTree := tapscript .NewChannelFundingScriptTree ()
1256
- fundingScriptKey := asset .NewScriptKey (fundingScriptTree .TaprootKey )
1257
1302
fundingOutputProofs , err := f .anchorVPackets (
1258
- finalFundedPsbt , signedPkts , fundingScriptKey ,
1303
+ finalFundedPsbt , signedPkts ,
1259
1304
)
1260
1305
if err != nil {
1261
1306
return nil , fmt .Errorf ("unable to anchor vPackets: %w" , err )
0 commit comments