@@ -20,6 +20,7 @@ import (
2020 "github.com/btcsuite/btcd/chaincfg/chainhash"
2121 "github.com/btcsuite/btcd/wire"
2222 "github.com/davecgh/go-spew/spew"
23+ "github.com/lightninglabs/lndclient"
2324 "github.com/lightninglabs/taproot-assets/address"
2425 "github.com/lightninglabs/taproot-assets/asset"
2526 "github.com/lightninglabs/taproot-assets/commitment"
@@ -52,6 +53,28 @@ const (
5253 // ackTimeout is the amount of time we'll wait to receive the protocol
5354 // level ACK from the remote party before timing out.
5455 ackTimeout = time .Second * 30
56+
57+ // maxNumAssetIDs is the maximum number of fungible asset pieces (asset
58+ // IDs) that can be committed to a single channel. The number needs to
59+ // be limited to prevent the number of required HTLC signatures to be
60+ // too large for a single CommitSig wire message to carry them. This
61+ // value is tightly coupled with the number of HTLCs that can be added
62+ // to a channel at the same time (maxNumHTLCs). The values were
63+ // determined with the TestMaxCommitSigMsgSize test in
64+ // aux_leaf_signer_test.go then a set was chosen that would allow for
65+ // a decent number of HTLCs (and also a number that is divisible by two
66+ // because each side will only be allowed to add half of the total).
67+ maxNumAssetIDs = 3
68+
69+ // maxNumHTLCs is the maximum number of HTLCs there can be in an asset
70+ // channel to avoid the number of signatures exceeding the maximum
71+ // message size of a CommitSig message. See maxNumAssetIDs for more
72+ // information.
73+ maxNumHTLCs = 166
74+
75+ // maxNumHTLCsPerParty is the maximum number of HTLCs that can be added
76+ // by a single party to a channel.
77+ maxNumHTLCsPerParty = maxNumHTLCs / 2
5578)
5679
5780// ErrorReporter is used to report an error back to the caller and/or peer that
@@ -94,6 +117,11 @@ type OpenChanReq struct {
94117 // PushAmt is the amount of BTC to push to the remote peer.
95118 PushAmt btcutil.Amount
96119
120+ // RemoteMaxHtlc is the maximum number of HTLCs we allow the remote to
121+ // add to the channel. If this is zero, then the default value defined
122+ // by lnd (and dependent on the channel capacity) will be used.
123+ RemoteMaxHtlc uint32
124+
97125 // PeerPub is the identity public key of the remote peer we wish to
98126 // open the channel with.
99127 PeerPub btcec.PublicKey
@@ -133,6 +161,11 @@ type PsbtChannelFunder interface {
133161 // process. Afterward, the funding transaction should be signed and
134162 // broadcast.
135163 OpenChannel (context.Context , OpenChanReq ) (AssetChanIntent , error )
164+
165+ // ChannelAcceptor is used to accept and potentially influence
166+ // parameters of incoming channels.
167+ ChannelAcceptor (ctx context.Context ,
168+ acceptor lndclient.AcceptorFunction ) (chan error , error )
136169}
137170
138171// TxPublisher is an interface used to publish transactions.
@@ -217,6 +250,9 @@ type FundingControllerCfg struct {
217250 // FeatureBits is used to verify that the peer has the required feature
218251 // to fund asset channels.
219252 FeatureBits FeatureBitVerifer
253+
254+ // ErrChan is used to report errors back to the main server.
255+ ErrChan chan <- error
220256}
221257
222258// bindFundingReq is a request to bind a pending channel ID to a complete aux
@@ -293,6 +329,36 @@ func (f *FundingController) Start() error {
293329 f .Wg .Add (1 )
294330 go f .chanFunder ()
295331
332+ f .Wg .Add (1 )
333+ go func () {
334+ defer f .Wg .Done ()
335+
336+ ctx , cancel := f .WithCtxQuitNoTimeout ()
337+ defer cancel ()
338+
339+ errChan , err := f .cfg .ChannelFunder .ChannelAcceptor (
340+ ctx , f .channelAcceptor ,
341+ )
342+ if err != nil {
343+ err = fmt .Errorf ("unable to start channel acceptor: %w" ,
344+ err )
345+ f .cfg .ErrChan <- err
346+ return
347+ }
348+
349+ // We'll accept channels for as long as the funding controller
350+ // is running or until we receive an error.
351+ select {
352+ case err := <- errChan :
353+ err = fmt .Errorf ("channel acceptor error: %w" , err )
354+ f .cfg .ErrChan <- err
355+
356+ case <- f .Quit :
357+ log .Infof ("Stopping channel acceptor, funding " +
358+ "controller shutting down" )
359+ }
360+ }()
361+
296362 return nil
297363}
298364
@@ -1003,10 +1069,11 @@ func (f *FundingController) completeChannelFunding(ctx context.Context,
10031069 // Now that we have the initial PSBT template, we can start the funding
10041070 // flow with lnd.
10051071 fundingReq := OpenChanReq {
1006- ChanAmt : 100_000 ,
1007- PushAmt : fundingState .pushAmt ,
1008- PeerPub : fundingState .peerPub ,
1009- TempPID : fundingState .pid ,
1072+ ChanAmt : 100_000 ,
1073+ PushAmt : fundingState .pushAmt ,
1074+ PeerPub : fundingState .peerPub ,
1075+ TempPID : fundingState .pid ,
1076+ RemoteMaxHtlc : maxNumHTLCsPerParty ,
10101077 }
10111078 assetChanIntent , err := f .cfg .ChannelFunder .OpenChannel (ctx , fundingReq )
10121079 if err != nil {
@@ -1430,6 +1497,26 @@ func (f *FundingController) processFundingReq(fundingFlows fundingFlowIndex,
14301497 }
14311498 }()
14321499
1500+ // We need to limit the number of different fungible assets (asset IDs)
1501+ // we allow to be commited to a single channel. This is to make sure we
1502+ // have a decent number of HTLCs available. See Godoc of maxNumAssetIDs
1503+ // for more information.
1504+ //
1505+ // TODO(guggero): This following code is obviously wrong and needs to be
1506+ // changed when we support committing fungible assets into a channel. To
1507+ // avoid this TODO from being overlooked, we add a dummy implementation
1508+ // with a condition that currently will never be true (since there's
1509+ // only a single vPacket being selected currently anyway).
1510+ assetIDSet := lfn .NewSet [asset.ID ]()
1511+ for _ , out := range fundingVpkt .VPacket .Outputs {
1512+ assetIDSet .Add (out .Asset .ID ())
1513+ }
1514+ if assetIDSet .Size () > maxNumAssetIDs {
1515+ return fmt .Errorf ("too many different asset IDs in channel " +
1516+ "funding, got %d, max is %d" , len (assetIDSet .ToSlice ()),
1517+ maxNumAssetIDs )
1518+ }
1519+
14331520 // Now that we know the final funding asset root along with the splits,
14341521 // we can derive the tapscript root that'll be used alongside the
14351522 // internal key (which we'll only learn from lnd later as we finalize
@@ -1677,6 +1764,43 @@ func (f *FundingController) chanFunder() {
16771764 }
16781765}
16791766
1767+ // channelAcceptor is a callback that's called by the lnd client when a new
1768+ // channel is proposed. This function is responsible for deciding whether to
1769+ // accept the channel based on the channel parameters, and to also set some
1770+ // channel parameters for our own side.
1771+ func (f * FundingController ) channelAcceptor (_ context.Context ,
1772+ req * lndclient.AcceptorRequest ) (* lndclient.AcceptorResponse , error ) {
1773+
1774+ // Avoid nil pointer dereference.
1775+ if req .CommitmentType == nil {
1776+ return nil , fmt .Errorf ("commitment type is required" )
1777+ }
1778+
1779+ // Ignore any non-asset channels, just accept them.
1780+ if * req .CommitmentType != lnwallet .CommitmentTypeSimpleTaprootOverlay {
1781+ return & lndclient.AcceptorResponse {
1782+ Accept : true ,
1783+ }, nil
1784+ }
1785+
1786+ // Reject custom channels that don't observe the max HTLC limit.
1787+ if req .MaxAcceptedHtlcs > maxNumHTLCsPerParty {
1788+ return & lndclient.AcceptorResponse {
1789+ Accept : false ,
1790+ Error : fmt .Sprintf ("max accepted HTLCs must be at " +
1791+ "most %d, got %d" , maxNumHTLCsPerParty ,
1792+ req .MaxAcceptedHtlcs ),
1793+ }, nil
1794+ }
1795+
1796+ // Everything looks good, we can now set our own max HTLC limit we'll
1797+ // observe for this channel.
1798+ return & lndclient.AcceptorResponse {
1799+ Accept : true ,
1800+ MaxHtlcCount : maxNumHTLCsPerParty ,
1801+ }, nil
1802+ }
1803+
16801804// validateProofs validates the inclusion/exclusion/split proofs and the
16811805// transfer witness of the given proofs.
16821806func (f * FundingController ) validateProofs (proofs []* proof.Proof ) error {
0 commit comments