Skip to content

Commit a6ab848

Browse files
committed
tapchannel: include stxo proofs on channel funding
This commit updates the aux funding controller to conditionally use stxo proofs, depending on whether the feature bit was negotiated or not with the remote peer. Our funding commitment will now include the alt leaves for stxos if the flag is set. The funder needs to construct the correct tapscript early on, as LND will query the tapscript root before we reach the finalization phase. That's why we manually calculate and merge the stxo alt leaves in the funding process. The fundee now also needs to calculate and merge the alt leaves that result from the asset outputs, in order to arrive to the same tapscript root.
1 parent 3ed3231 commit a6ab848

File tree

2 files changed

+68
-11
lines changed

2 files changed

+68
-11
lines changed

tapcfg/server.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -628,6 +628,7 @@ func genServerConfig(cfg *Config, cfgLogger btclog.Logger,
628628
AssetSyncer: addrBook,
629629
FeatureBits: lndFeatureBitsVerifier,
630630
IgnoreChecker: ignoreCheckerOpt,
631+
AuxChanNegotiator: auxChanNegotiator,
631632
ErrChan: mainErrChan,
632633
},
633634
)

tapchannel/aux_funding_controller.go

Lines changed: 67 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -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

4648
const (
@@ -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(
524551
func 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
@@ -571,7 +599,7 @@ func newCommitBlobAndLeaves(pendingFunding *pendingAssetFunding,
571599
fakePrevState, lndOpenChan, assetOpenChan, whoseCommit,
572600
localSatBalance, remoteSatBalance, fakeView,
573601
pendingFunding.chainParams, keyRing.GetForParty(whoseCommit),
574-
false,
602+
stxo,
575603
)
576604
if err != nil {
577605
return nil, lnwallet.CommitAuxLeaves{}, err
@@ -614,12 +642,14 @@ func (p *pendingAssetFunding) toAuxFundingDesc(req *bindFundingReq,
614642
// This will be the information for the very first state (state 0).
615643
localCommitBlob, localAuxLeaves, err := newCommitBlobAndLeaves(
616644
p, req.openChan, openChanDesc, req.keyRing, lntypes.Local,
645+
p.stxo,
617646
)
618647
if err != nil {
619648
return nil, err
620649
}
621650
remoteCommitBlob, remoteAuxLeaves, err := newCommitBlobAndLeaves(
622651
p, req.openChan, openChanDesc, req.keyRing, lntypes.Remote,
652+
p.stxo,
623653
)
624654
if err != nil {
625655
return nil, err
@@ -1087,14 +1117,19 @@ func (f *FundingController) signAllVPackets(ctx context.Context,
10871117
// complete, but unsigned PSBT packet that can be used to create out asset
10881118
// channel.
10891119
func (f *FundingController) anchorVPackets(fundedPkt *tapsend.FundedPsbt,
1090-
allPackets []*tappsbt.VPacket) ([]*proof.Proof, error) {
1120+
allPackets []*tappsbt.VPacket, stxo bool) ([]*proof.Proof, error) {
10911121

10921122
log.Infof("Anchoring funding vPackets to funding PSBT")
10931123

1124+
var opts []tapsend.OutputCommitmentOption
1125+
if !stxo {
1126+
opts = append(opts, tapsend.WithNoSTXOProofs())
1127+
}
1128+
10941129
// Given the set of vPackets we've created, we'll now merge them all to
10951130
// create a map from output index to final tap commitment.
10961131
outputCommitments, err := tapsend.CreateOutputCommitments(
1097-
allPackets, tapsend.WithNoSTXOProofs(),
1132+
allPackets, opts...,
10981133
)
10991134
if err != nil {
11001135
return nil, fmt.Errorf("unable to create new output "+
@@ -1123,11 +1158,16 @@ func (f *FundingController) anchorVPackets(fundedPkt *tapsend.FundedPsbt,
11231158
for idx := range allPackets {
11241159
vPkt := allPackets[idx]
11251160

1161+
var opts []proof.GenOption
1162+
if !stxo {
1163+
opts = append(opts, proof.WithNoSTXOProofs())
1164+
}
1165+
11261166
for vOutIdx := range vPkt.Outputs {
11271167
proofSuffix, err := tapsend.CreateProofSuffix(
11281168
fundedPkt.Pkt.UnsignedTx, fundedPkt.Pkt.Outputs,
11291169
vPkt, outputCommitments, vOutIdx, allPackets,
1130-
proof.WithNoSTXOProofs(),
1170+
opts...,
11311171
)
11321172
if err != nil {
11331173
return nil, fmt.Errorf("unable to create "+
@@ -1220,7 +1260,8 @@ func (f *FundingController) sendAssetFundingCreated(ctx context.Context,
12201260
// ultimately broadcasting the funding transaction.
12211261
func (f *FundingController) completeChannelFunding(ctx context.Context,
12221262
fundingState *pendingAssetFunding,
1223-
fundedVpkt *tapfreighter.FundedVPacket) (*wire.OutPoint, error) {
1263+
fundedVpkt *tapfreighter.FundedVPacket,
1264+
stxoEnabled bool) (*wire.OutPoint, error) {
12241265

12251266
log.Debugf("Finalizing funding vPackets and PSBT...")
12261267

@@ -1331,7 +1372,7 @@ func (f *FundingController) completeChannelFunding(ctx context.Context,
13311372
// PSBT. This'll update all the pkScripts for our funding output and
13321373
// change.
13331374
fundingOutputProofs, err := f.anchorVPackets(
1334-
finalFundedPsbt, signedPkts,
1375+
finalFundedPsbt, signedPkts, stxoEnabled,
13351376
)
13361377
if err != nil {
13371378
return nil, fmt.Errorf("unable to anchor vPackets: %w", err)
@@ -1546,11 +1587,17 @@ func (f *FundingController) processFundingMsg(ctx context.Context,
15461587
"proof: %w", err)
15471588
}
15481589

1590+
features := f.cfg.AuxChanNegotiator.GetPeerFeatures(
1591+
route.Vertex(msg.PeerPub.SerializeCompressed()),
1592+
)
1593+
1594+
supportSTXO := features.HasFeature(tapfeatures.STXOOptional)
1595+
15491596
// If we reached this point, then the asset output and all
15501597
// inputs are valid, so we'll store the funding asset
15511598
// commitment.
15521599
err = assetFunding.addToFundingCommitment(
1553-
&assetProof.AssetOutput.Val,
1600+
&assetProof.AssetOutput.Val, supportSTXO,
15541601
)
15551602
if err != nil {
15561603
return tempPID, fmt.Errorf("unable to create "+
@@ -1739,6 +1786,15 @@ func (f *FundingController) processFundingReq(fundingFlows fundingFlowIndex,
17391786
maxNumAssetIDs)
17401787
}
17411788

1789+
// Now let's see if we should be using STXOs for this channel funding.
1790+
features := f.cfg.AuxChanNegotiator.GetPeerFeatures(
1791+
route.Vertex(fundReq.PeerPub.SerializeCompressed()),
1792+
)
1793+
1794+
supportSTXO := features.HasFeature(tapfeatures.STXOOptional)
1795+
1796+
fundingState.stxo = supportSTXO
1797+
17421798
// Now that we know the final funding asset root along with the splits,
17431799
// we can derive the tapscript root that'll be used alongside the
17441800
// internal key (which we'll only learn from lnd later as we finalize
@@ -1751,7 +1807,7 @@ func (f *FundingController) processFundingReq(fundingFlows fundingFlowIndex,
17511807
}
17521808

17531809
err = fundingState.addToFundingCommitment(
1754-
fundingOut.Asset.Copy(),
1810+
fundingOut.Asset.Copy(), supportSTXO,
17551811
)
17561812
if err != nil {
17571813
return fmt.Errorf("unable to add asset to funding "+
@@ -1815,7 +1871,7 @@ func (f *FundingController) processFundingReq(fundingFlows fundingFlowIndex,
18151871
}
18161872

18171873
chanPoint, err := f.completeChannelFunding(
1818-
fundReq.ctx, fundingState, fundingVpkt,
1874+
fundReq.ctx, fundingState, fundingVpkt, supportSTXO,
18191875
)
18201876
if err != nil {
18211877
// If anything went wrong during the funding process,

0 commit comments

Comments
 (0)