@@ -28,6 +28,7 @@ import (
2828 "github.com/lightninglabs/taproot-assets/rfq"
2929 cmsg "github.com/lightninglabs/taproot-assets/tapchannelmsg"
3030 "github.com/lightninglabs/taproot-assets/tapdb"
31+ "github.com/lightninglabs/taproot-assets/tapfeatures"
3132 "github.com/lightninglabs/taproot-assets/tapfreighter"
3233 "github.com/lightninglabs/taproot-assets/tapgarden"
3334 "github.com/lightninglabs/taproot-assets/tappsbt"
@@ -41,6 +42,7 @@ import (
4142 "github.com/lightningnetwork/lnd/lnwallet/chainfee"
4243 "github.com/lightningnetwork/lnd/lnwire"
4344 "github.com/lightningnetwork/lnd/msgmux"
45+ "github.com/lightningnetwork/lnd/routing/route"
4446)
4547
4648const (
@@ -269,6 +271,11 @@ type FundingControllerCfg struct {
269271 // a proof should be ignored.
270272 IgnoreChecker lfn.Option [proof.IgnoreChecker ]
271273
274+ // AuxChanNegotiator is responsible for producing the extra tlv blob
275+ // that is encapsulated in the init and reestablish peer messages. This
276+ // helps us communicate custom feature bits with our peer.
277+ AuxChanNegotiator * tapfeatures.AuxChannelNegotiator
278+
272279 // ErrChan is used to report errors back to the main server.
273280 ErrChan chan <- error
274281}
@@ -419,6 +426,8 @@ type pendingAssetFunding struct {
419426
420427 initiator bool
421428
429+ stxo bool
430+
422431 amt uint64
423432
424433 pushAmt btcutil.Amount
@@ -462,14 +471,32 @@ func (p *pendingAssetFunding) assetOutputs() []*cmsg.AssetOutput {
462471}
463472
464473// addToFundingCommitment adds a new asset to the funding commitment.
465- func (p * pendingAssetFunding ) addToFundingCommitment (a * asset.Asset ) error {
474+ func (p * pendingAssetFunding ) addToFundingCommitment (a * asset.Asset ,
475+ stxo bool ) error {
476+
466477 newCommitment , err := commitment .FromAssets (
467478 fn .Ptr (commitment .TapCommitmentV2 ), a ,
468479 )
469480 if err != nil {
470481 return fmt .Errorf ("unable to create commitment: %w" , err )
471482 }
472483
484+ // If our peer supports STXO we go ahead and append the
485+ // appropriate alt leaves to the VOutput.
486+ if stxo {
487+ altLeaves , err := asset .CollectSTXO (a )
488+ if err != nil {
489+ return err
490+ }
491+
492+ err = newCommitment .MergeAltLeaves (altLeaves )
493+ if err != nil {
494+ return err
495+ }
496+
497+ p .stxo = stxo
498+ }
499+
473500 newCommitment , err = commitment .TrimSplitWitnesses (
474501 & newCommitment .Version , newCommitment ,
475502 )
@@ -524,7 +551,8 @@ func (p *pendingAssetFunding) addInputProofChunk(
524551func newCommitBlobAndLeaves (pendingFunding * pendingAssetFunding ,
525552 lndOpenChan lnwallet.AuxChanState , assetOpenChan * cmsg.OpenChannel ,
526553 keyRing lntypes.Dual [lnwallet.CommitmentKeyRing ],
527- whoseCommit lntypes.ChannelParty ) ([]byte , lnwallet.CommitAuxLeaves ,
554+ whoseCommit lntypes.ChannelParty ,
555+ stxo bool ) ([]byte , lnwallet.CommitAuxLeaves ,
528556 error ) {
529557
530558 chanAssets := assetOpenChan .FundedAssets .Val .Outputs
@@ -559,6 +587,7 @@ func newCommitBlobAndLeaves(pendingFunding *pendingAssetFunding,
559587 // needs the sum of the remote+local assets, so we'll populate that.
560588 fakePrevState := cmsg .NewCommitment (
561589 localAssets , remoteAssets , nil , nil , lnwallet.CommitAuxLeaves {},
590+ stxo ,
562591 )
563592
564593 // Just like above, we don't have a real HTLC view here, so we'll pass
@@ -571,6 +600,7 @@ func newCommitBlobAndLeaves(pendingFunding *pendingAssetFunding,
571600 fakePrevState , lndOpenChan , assetOpenChan , whoseCommit ,
572601 localSatBalance , remoteSatBalance , fakeView ,
573602 pendingFunding .chainParams , keyRing .GetForParty (whoseCommit ),
603+ stxo ,
574604 )
575605 if err != nil {
576606 return nil , lnwallet.CommitAuxLeaves {}, err
@@ -613,12 +643,14 @@ func (p *pendingAssetFunding) toAuxFundingDesc(req *bindFundingReq,
613643 // This will be the information for the very first state (state 0).
614644 localCommitBlob , localAuxLeaves , err := newCommitBlobAndLeaves (
615645 p , req .openChan , openChanDesc , req .keyRing , lntypes .Local ,
646+ p .stxo ,
616647 )
617648 if err != nil {
618649 return nil , err
619650 }
620651 remoteCommitBlob , remoteAuxLeaves , err := newCommitBlobAndLeaves (
621652 p , req .openChan , openChanDesc , req .keyRing , lntypes .Remote ,
653+ p .stxo ,
622654 )
623655 if err != nil {
624656 return nil , err
@@ -1086,14 +1118,19 @@ func (f *FundingController) signAllVPackets(ctx context.Context,
10861118// complete, but unsigned PSBT packet that can be used to create out asset
10871119// channel.
10881120func (f * FundingController ) anchorVPackets (fundedPkt * tapsend.FundedPsbt ,
1089- allPackets []* tappsbt.VPacket ) ([]* proof.Proof , error ) {
1121+ allPackets []* tappsbt.VPacket , stxo bool ) ([]* proof.Proof , error ) {
10901122
10911123 log .Infof ("Anchoring funding vPackets to funding PSBT" )
10921124
1125+ var opts []tapsend.OutputCommitmentOption
1126+ if ! stxo {
1127+ opts = append (opts , tapsend .WithNoSTXOProofs ())
1128+ }
1129+
10931130 // Given the set of vPackets we've created, we'll now merge them all to
10941131 // create a map from output index to final tap commitment.
10951132 outputCommitments , err := tapsend .CreateOutputCommitments (
1096- allPackets , tapsend . WithNoSTXOProofs () ,
1133+ allPackets , opts ... ,
10971134 )
10981135 if err != nil {
10991136 return nil , fmt .Errorf ("unable to create new output " +
@@ -1122,11 +1159,16 @@ func (f *FundingController) anchorVPackets(fundedPkt *tapsend.FundedPsbt,
11221159 for idx := range allPackets {
11231160 vPkt := allPackets [idx ]
11241161
1162+ var opts []proof.GenOption
1163+ if ! stxo {
1164+ opts = append (opts , proof .WithNoSTXOProofs ())
1165+ }
1166+
11251167 for vOutIdx := range vPkt .Outputs {
11261168 proofSuffix , err := tapsend .CreateProofSuffix (
11271169 fundedPkt .Pkt .UnsignedTx , fundedPkt .Pkt .Outputs ,
11281170 vPkt , outputCommitments , vOutIdx , allPackets ,
1129- proof . WithNoSTXOProofs () ,
1171+ opts ... ,
11301172 )
11311173 if err != nil {
11321174 return nil , fmt .Errorf ("unable to create " +
@@ -1219,7 +1261,8 @@ func (f *FundingController) sendAssetFundingCreated(ctx context.Context,
12191261// ultimately broadcasting the funding transaction.
12201262func (f * FundingController ) completeChannelFunding (ctx context.Context ,
12211263 fundingState * pendingAssetFunding ,
1222- fundedVpkt * tapfreighter.FundedVPacket ) (* wire.OutPoint , error ) {
1264+ fundedVpkt * tapfreighter.FundedVPacket ,
1265+ stxoEnabled bool ) (* wire.OutPoint , error ) {
12231266
12241267 log .Debugf ("Finalizing funding vPackets and PSBT..." )
12251268
@@ -1330,7 +1373,7 @@ func (f *FundingController) completeChannelFunding(ctx context.Context,
13301373 // PSBT. This'll update all the pkScripts for our funding output and
13311374 // change.
13321375 fundingOutputProofs , err := f .anchorVPackets (
1333- finalFundedPsbt , signedPkts ,
1376+ finalFundedPsbt , signedPkts , stxoEnabled ,
13341377 )
13351378 if err != nil {
13361379 return nil , fmt .Errorf ("unable to anchor vPackets: %w" , err )
@@ -1545,11 +1588,17 @@ func (f *FundingController) processFundingMsg(ctx context.Context,
15451588 "proof: %w" , err )
15461589 }
15471590
1591+ features := f .cfg .AuxChanNegotiator .GetPeerFeatures (
1592+ route .Vertex (msg .PeerPub .SerializeCompressed ()),
1593+ )
1594+
1595+ supportSTXO := features .HasFeature (tapfeatures .STXOOptional )
1596+
15481597 // If we reached this point, then the asset output and all
15491598 // inputs are valid, so we'll store the funding asset
15501599 // commitment.
15511600 err = assetFunding .addToFundingCommitment (
1552- & assetProof .AssetOutput .Val ,
1601+ & assetProof .AssetOutput .Val , supportSTXO ,
15531602 )
15541603 if err != nil {
15551604 return tempPID , fmt .Errorf ("unable to create " +
@@ -1738,6 +1787,15 @@ func (f *FundingController) processFundingReq(fundingFlows fundingFlowIndex,
17381787 maxNumAssetIDs )
17391788 }
17401789
1790+ // Now let's see if we should be using STXOs for this channel funding.
1791+ features := f .cfg .AuxChanNegotiator .GetPeerFeatures (
1792+ route .Vertex (fundReq .PeerPub .SerializeCompressed ()),
1793+ )
1794+
1795+ supportSTXO := features .HasFeature (tapfeatures .STXOOptional )
1796+
1797+ fundingState .stxo = supportSTXO
1798+
17411799 // Now that we know the final funding asset root along with the splits,
17421800 // we can derive the tapscript root that'll be used alongside the
17431801 // internal key (which we'll only learn from lnd later as we finalize
@@ -1750,7 +1808,7 @@ func (f *FundingController) processFundingReq(fundingFlows fundingFlowIndex,
17501808 }
17511809
17521810 err = fundingState .addToFundingCommitment (
1753- fundingOut .Asset .Copy (),
1811+ fundingOut .Asset .Copy (), supportSTXO ,
17541812 )
17551813 if err != nil {
17561814 return fmt .Errorf ("unable to add asset to funding " +
@@ -1814,7 +1872,7 @@ func (f *FundingController) processFundingReq(fundingFlows fundingFlowIndex,
18141872 }
18151873
18161874 chanPoint , err := f .completeChannelFunding (
1817- fundReq .ctx , fundingState , fundingVpkt ,
1875+ fundReq .ctx , fundingState , fundingVpkt , supportSTXO ,
18181876 )
18191877 if err != nil {
18201878 // If anything went wrong during the funding process,
0 commit comments