Skip to content

Commit 8578ea4

Browse files
committed
multi: add channel acceptor, enforce max HTLC limit
1 parent f7223fd commit 8578ea4

File tree

3 files changed

+113
-4
lines changed

3 files changed

+113
-4
lines changed

psbt_channel_funder.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,16 @@ func (l *LndPbstChannelFunder) OpenChannel(ctx context.Context,
144144
}
145145
}
146146

147+
// ChannelAcceptor is used to accept and potentially influence parameters of
148+
// incoming channels.
149+
func (l *LndPbstChannelFunder) ChannelAcceptor(ctx context.Context,
150+
acceptor lndclient.AcceptorFunction) (chan error, error) {
151+
152+
return l.lnd.Client.ChannelAcceptor(
153+
ctx, tapchannel.DefaultTimeout/2, acceptor,
154+
)
155+
}
156+
147157
// A compile-time check to ensure that LndPbstChannelFunder fully implements
148158
// the tapchannel.PsbtChannelFunder interface.
149159
var _ tapchannel.PsbtChannelFunder = (*LndPbstChannelFunder)(nil)

tapcfg/server.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -448,6 +448,7 @@ func genServerConfig(cfg *Config, cfgLogger btclog.Logger,
448448
DefaultCourierAddr: proofCourierAddr,
449449
AssetSyncer: addrBook,
450450
FeatureBits: lndFeatureBitsVerifier,
451+
ErrChan: mainErrChan,
451452
},
452453
)
453454
auxTrafficShaper := tapchannel.NewAuxTrafficShaper(

tapchannel/aux_funding_controller.go

Lines changed: 102 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -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
@@ -138,6 +161,11 @@ type PsbtChannelFunder interface {
138161
// process. Afterward, the funding transaction should be signed and
139162
// broadcast.
140163
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)
141169
}
142170

143171
// TxPublisher is an interface used to publish transactions.
@@ -222,6 +250,9 @@ type FundingControllerCfg struct {
222250
// FeatureBits is used to verify that the peer has the required feature
223251
// to fund asset channels.
224252
FeatureBits FeatureBitVerifer
253+
254+
// ErrChan is used to report errors back to the main server.
255+
ErrChan chan<- error
225256
}
226257

227258
// bindFundingReq is a request to bind a pending channel ID to a complete aux
@@ -298,6 +329,36 @@ func (f *FundingController) Start() error {
298329
f.Wg.Add(1)
299330
go f.chanFunder()
300331

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+
301362
return nil
302363
}
303364

@@ -1008,10 +1069,11 @@ func (f *FundingController) completeChannelFunding(ctx context.Context,
10081069
// Now that we have the initial PSBT template, we can start the funding
10091070
// flow with lnd.
10101071
fundingReq := OpenChanReq{
1011-
ChanAmt: 100_000,
1012-
PushAmt: fundingState.pushAmt,
1013-
PeerPub: fundingState.peerPub,
1014-
TempPID: fundingState.pid,
1072+
ChanAmt: 100_000,
1073+
PushAmt: fundingState.pushAmt,
1074+
PeerPub: fundingState.peerPub,
1075+
TempPID: fundingState.pid,
1076+
RemoteMaxHtlc: maxNumHTLCsPerParty,
10151077
}
10161078
assetChanIntent, err := f.cfg.ChannelFunder.OpenChannel(ctx, fundingReq)
10171079
if err != nil {
@@ -1670,6 +1732,42 @@ func (f *FundingController) chanFunder() {
16701732
}
16711733
}
16721734

1735+
// channelAcceptor is a callback that's called by the lnd client when a new
1736+
// channel is proposed. This function is responsible for deciding whether to
1737+
// accept the channel based on the channel parameters, and to also set some
1738+
// channel parameters for our own side.
1739+
func (f *FundingController) channelAcceptor(_ context.Context,
1740+
req *lndclient.AcceptorRequest) (*lndclient.AcceptorResponse, error) {
1741+
1742+
// Avoid nil pointer dereference.
1743+
if req.CommitmentType == nil {
1744+
return nil, fmt.Errorf("commitment type is required")
1745+
}
1746+
1747+
// Ignore any non-asset channels, just accept them.
1748+
if *req.CommitmentType != lnwallet.CommitmentTypeSimpleTaprootOverlay {
1749+
return &lndclient.AcceptorResponse{
1750+
Accept: true,
1751+
}, nil
1752+
}
1753+
1754+
// Reject custom channels that don't observe the max HTLC limit.
1755+
if req.MaxAcceptedHtlcs != maxNumHTLCsPerParty {
1756+
return &lndclient.AcceptorResponse{
1757+
Accept: false,
1758+
Error: fmt.Sprintf("max accepted HTLCs must be 123, "+
1759+
"got %d", req.MaxAcceptedHtlcs),
1760+
}, nil
1761+
}
1762+
1763+
// Everything looks good, we can now set our own max HTLC limit we'll
1764+
// observe for this channel.
1765+
return &lndclient.AcceptorResponse{
1766+
Accept: true,
1767+
MaxHtlcCount: maxNumHTLCsPerParty,
1768+
}, nil
1769+
}
1770+
16731771
// validateProofs validates the inclusion/exclusion/split proofs and the
16741772
// transfer witness of the given proofs.
16751773
func (f *FundingController) validateProofs(proofs []*proof.Proof) error {

0 commit comments

Comments
 (0)