@@ -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"
@@ -51,6 +52,28 @@ const (
5152 // ackTimeout is the amount of time we'll wait to receive the protocol
5253 // level ACK from the remote party before timing out.
5354 ackTimeout = time .Second * 30
55+
56+ // maxNumAssetIDs is the maximum number of fungible asset pieces (asset
57+ // IDs) that can be committed to a single channel. The number needs to
58+ // be limited to prevent the number of required HTLC signatures to be
59+ // too large for a single CommitSig wire message to carry them. This
60+ // value is tightly coupled with the number of HTLCs that can be added
61+ // to a channel at the same time (maxNumHTLCs). The values were
62+ // determined with the TestMaxCommitSigMsgSize test in
63+ // aux_leaf_signer_test.go then a set was chosen that would allow for
64+ // a decent number of HTLCs (and also a number that is divisible by two
65+ // because each side will only be allowed to add half of the total).
66+ maxNumAssetIDs = 3
67+
68+ // maxNumHTLCs is the maximum number of HTLCs there can be in an asset
69+ // channel to avoid the number of signatures exceeding the maximum
70+ // message size of a CommitSig message. See maxNumAssetIDs for more
71+ // information.
72+ maxNumHTLCs = 166
73+
74+ // maxNumHTLCsPerParty is the maximum number of HTLCs that can be added
75+ // by a single party to a channel.
76+ maxNumHTLCsPerParty = maxNumHTLCs / 2
5477)
5578
5679// ErrorReporter is used to report an error back to the caller and/or peer that
@@ -137,6 +160,11 @@ type PsbtChannelFunder interface {
137160 // process. Afterward, the funding transaction should be signed and
138161 // broadcast.
139162 OpenChannel (context.Context , OpenChanReq ) (AssetChanIntent , error )
163+
164+ // ChannelAcceptor is used to accept and potentially influence
165+ // parameters of incoming channels.
166+ ChannelAcceptor (ctx context.Context ,
167+ acceptor lndclient.AcceptorFunction ) (chan error , error )
140168}
141169
142170// TxPublisher is an interface used to publish transactions.
@@ -221,6 +249,9 @@ type FundingControllerCfg struct {
221249 // FeatureBits is used to verify that the peer has the required feature
222250 // to fund asset channels.
223251 FeatureBits FeatureBitVerifer
252+
253+ // ErrChan is used to report errors back to the main server.
254+ ErrChan chan <- error
224255}
225256
226257// bindFundingReq is a request to bind a pending channel ID to a complete aux
@@ -297,6 +328,36 @@ func (f *FundingController) Start() error {
297328 f .Wg .Add (1 )
298329 go f .chanFunder ()
299330
331+ f .Wg .Add (1 )
332+ go func () {
333+ defer f .Wg .Done ()
334+
335+ ctx , cancel := f .WithCtxQuitNoTimeout ()
336+ defer cancel ()
337+
338+ errChan , err := f .cfg .ChannelFunder .ChannelAcceptor (
339+ ctx , f .channelAcceptor ,
340+ )
341+ if err != nil {
342+ err = fmt .Errorf ("unable to start channel acceptor: %w" ,
343+ err )
344+ f .cfg .ErrChan <- err
345+ return
346+ }
347+
348+ // We'll accept channels for as long as the funding controller
349+ // is running or until we receive an error.
350+ select {
351+ case err := <- errChan :
352+ err = fmt .Errorf ("channel acceptor error: %w" , err )
353+ f .cfg .ErrChan <- err
354+
355+ case <- f .Quit :
356+ log .Infof ("Stopping channel acceptor, funding " +
357+ "controller shutting down" )
358+ }
359+ }()
360+
300361 return nil
301362}
302363
@@ -1007,10 +1068,11 @@ func (f *FundingController) completeChannelFunding(ctx context.Context,
10071068 // Now that we have the initial PSBT template, we can start the funding
10081069 // flow with lnd.
10091070 fundingReq := OpenChanReq {
1010- ChanAmt : 100_000 ,
1011- PushAmt : fundingState .pushAmt ,
1012- PeerPub : fundingState .peerPub ,
1013- TempPID : fundingState .pid ,
1071+ ChanAmt : 100_000 ,
1072+ PushAmt : fundingState .pushAmt ,
1073+ PeerPub : fundingState .peerPub ,
1074+ TempPID : fundingState .pid ,
1075+ RemoteMaxHtlc : maxNumHTLCsPerParty ,
10141076 }
10151077 assetChanIntent , err := f .cfg .ChannelFunder .OpenChannel (ctx , fundingReq )
10161078 if err != nil {
@@ -1669,6 +1731,42 @@ func (f *FundingController) chanFunder() {
16691731 }
16701732}
16711733
1734+ // channelAcceptor is a callback that's called by the lnd client when a new
1735+ // channel is proposed. This function is responsible for deciding whether to
1736+ // accept the channel based on the channel parameters, and to also set some
1737+ // channel parameters for our own side.
1738+ func (f * FundingController ) channelAcceptor (_ context.Context ,
1739+ req * lndclient.AcceptorRequest ) (* lndclient.AcceptorResponse , error ) {
1740+
1741+ // Avoid nil pointer dereference.
1742+ if req .CommitmentType == nil {
1743+ return nil , fmt .Errorf ("commitment type is required" )
1744+ }
1745+
1746+ // Ignore any non-asset channels, just accept them.
1747+ if * req .CommitmentType != lnwallet .CommitmentTypeSimpleTaprootOverlay {
1748+ return & lndclient.AcceptorResponse {
1749+ Accept : true ,
1750+ }, nil
1751+ }
1752+
1753+ // Reject custom channels that don't observe the max HTLC limit.
1754+ if req .MaxAcceptedHtlcs != maxNumHTLCsPerParty {
1755+ return & lndclient.AcceptorResponse {
1756+ Accept : false ,
1757+ Error : fmt .Sprintf ("max accepted HTLCs must be 123, " +
1758+ "got %d" , req .MaxAcceptedHtlcs ),
1759+ }, nil
1760+ }
1761+
1762+ // Everything looks good, we can now set our own max HTLC limit we'll
1763+ // observe for this channel.
1764+ return & lndclient.AcceptorResponse {
1765+ Accept : true ,
1766+ MaxHtlcCount : maxNumHTLCsPerParty ,
1767+ }, nil
1768+ }
1769+
16721770// validateProofs validates the inclusion/exclusion/split proofs and the
16731771// transfer witness of the given proofs.
16741772func (f * FundingController ) validateProofs (proofs []* proof.Proof ) error {
0 commit comments