-
Notifications
You must be signed in to change notification settings - Fork 137
Limit number of HTLCs in custom channel #1132
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
bd5dc59
52d68a6
2e39848
6c77ecd
e652800
9a7a4c1
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -20,6 +20,7 @@ import ( | |
| "github.com/btcsuite/btcd/chaincfg/chainhash" | ||
| "github.com/btcsuite/btcd/wire" | ||
| "github.com/davecgh/go-spew/spew" | ||
| "github.com/lightninglabs/lndclient" | ||
| "github.com/lightninglabs/taproot-assets/address" | ||
| "github.com/lightninglabs/taproot-assets/asset" | ||
| "github.com/lightninglabs/taproot-assets/commitment" | ||
|
|
@@ -52,6 +53,28 @@ const ( | |
| // ackTimeout is the amount of time we'll wait to receive the protocol | ||
| // level ACK from the remote party before timing out. | ||
| ackTimeout = time.Second * 30 | ||
|
|
||
| // maxNumAssetIDs is the maximum number of fungible asset pieces (asset | ||
| // IDs) that can be committed to a single channel. The number needs to | ||
| // be limited to prevent the number of required HTLC signatures to be | ||
| // too large for a single CommitSig wire message to carry them. This | ||
| // value is tightly coupled with the number of HTLCs that can be added | ||
| // to a channel at the same time (maxNumHTLCs). The values were | ||
| // determined with the TestMaxCommitSigMsgSize test in | ||
| // aux_leaf_signer_test.go then a set was chosen that would allow for | ||
| // a decent number of HTLCs (and also a number that is divisible by two | ||
| // because each side will only be allowed to add half of the total). | ||
| maxNumAssetIDs = 3 | ||
ffranr marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| // maxNumHTLCs is the maximum number of HTLCs there can be in an asset | ||
| // channel to avoid the number of signatures exceeding the maximum | ||
| // message size of a CommitSig message. See maxNumAssetIDs for more | ||
| // information. | ||
| maxNumHTLCs = 166 | ||
|
|
||
| // maxNumHTLCsPerParty is the maximum number of HTLCs that can be added | ||
| // by a single party to a channel. | ||
| maxNumHTLCsPerParty = maxNumHTLCs / 2 | ||
Roasbeef marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| ) | ||
|
|
||
| // ErrorReporter is used to report an error back to the caller and/or peer that | ||
|
|
@@ -94,6 +117,11 @@ type OpenChanReq struct { | |
| // PushAmt is the amount of BTC to push to the remote peer. | ||
| PushAmt btcutil.Amount | ||
|
|
||
| // RemoteMaxHtlc is the maximum number of HTLCs we allow the remote to | ||
| // add to the channel. If this is zero, then the default value defined | ||
| // by lnd (and dependent on the channel capacity) will be used. | ||
| RemoteMaxHtlc uint32 | ||
|
|
||
| // PeerPub is the identity public key of the remote peer we wish to | ||
| // open the channel with. | ||
| PeerPub btcec.PublicKey | ||
|
|
@@ -133,6 +161,11 @@ type PsbtChannelFunder interface { | |
| // process. Afterward, the funding transaction should be signed and | ||
| // broadcast. | ||
| OpenChannel(context.Context, OpenChanReq) (AssetChanIntent, error) | ||
|
|
||
| // ChannelAcceptor is used to accept and potentially influence | ||
| // parameters of incoming channels. | ||
| ChannelAcceptor(ctx context.Context, | ||
| acceptor lndclient.AcceptorFunction) (chan error, error) | ||
| } | ||
|
|
||
| // TxPublisher is an interface used to publish transactions. | ||
|
|
@@ -217,6 +250,9 @@ type FundingControllerCfg struct { | |
| // FeatureBits is used to verify that the peer has the required feature | ||
| // to fund asset channels. | ||
| FeatureBits FeatureBitVerifer | ||
|
|
||
| // ErrChan is used to report errors back to the main server. | ||
| ErrChan chan<- error | ||
| } | ||
|
|
||
| // bindFundingReq is a request to bind a pending channel ID to a complete aux | ||
|
|
@@ -293,6 +329,36 @@ func (f *FundingController) Start() error { | |
| f.Wg.Add(1) | ||
| go f.chanFunder() | ||
|
|
||
| f.Wg.Add(1) | ||
| go func() { | ||
| defer f.Wg.Done() | ||
|
|
||
| ctx, cancel := f.WithCtxQuitNoTimeout() | ||
| defer cancel() | ||
|
|
||
| errChan, err := f.cfg.ChannelFunder.ChannelAcceptor( | ||
| ctx, f.channelAcceptor, | ||
| ) | ||
| if err != nil { | ||
| err = fmt.Errorf("unable to start channel acceptor: %w", | ||
| err) | ||
| f.cfg.ErrChan <- err | ||
| return | ||
| } | ||
|
|
||
| // We'll accept channels for as long as the funding controller | ||
| // is running or until we receive an error. | ||
| select { | ||
| case err := <-errChan: | ||
| err = fmt.Errorf("channel acceptor error: %w", err) | ||
| f.cfg.ErrChan <- err | ||
|
|
||
| case <-f.Quit: | ||
| log.Infof("Stopping channel acceptor, funding " + | ||
| "controller shutting down") | ||
| } | ||
| }() | ||
|
|
||
| return nil | ||
| } | ||
|
|
||
|
|
@@ -1003,10 +1069,11 @@ func (f *FundingController) completeChannelFunding(ctx context.Context, | |
| // Now that we have the initial PSBT template, we can start the funding | ||
| // flow with lnd. | ||
| fundingReq := OpenChanReq{ | ||
| ChanAmt: 100_000, | ||
| PushAmt: fundingState.pushAmt, | ||
| PeerPub: fundingState.peerPub, | ||
| TempPID: fundingState.pid, | ||
| ChanAmt: 100_000, | ||
| PushAmt: fundingState.pushAmt, | ||
| PeerPub: fundingState.peerPub, | ||
| TempPID: fundingState.pid, | ||
| RemoteMaxHtlc: maxNumHTLCsPerParty, | ||
| } | ||
| assetChanIntent, err := f.cfg.ChannelFunder.OpenChannel(ctx, fundingReq) | ||
| if err != nil { | ||
|
|
@@ -1430,6 +1497,26 @@ func (f *FundingController) processFundingReq(fundingFlows fundingFlowIndex, | |
| } | ||
| }() | ||
|
|
||
| // We need to limit the number of different fungible assets (asset IDs) | ||
| // we allow to be commited to a single channel. This is to make sure we | ||
| // have a decent number of HTLCs available. See Godoc of maxNumAssetIDs | ||
| // for more information. | ||
| // | ||
| // TODO(guggero): This following code is obviously wrong and needs to be | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this TODO still applicable? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, we need to actually count the number of different asset IDs (tranches) that are used for funding a channel, once we allow grouped assets to be used. So this will change in 0.6. |
||
| // changed when we support committing fungible assets into a channel. To | ||
| // avoid this TODO from being overlooked, we add a dummy implementation | ||
| // with a condition that currently will never be true (since there's | ||
| // only a single vPacket being selected currently anyway). | ||
guggero marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| assetIDSet := lfn.NewSet[asset.ID]() | ||
| for _, out := range fundingVpkt.VPacket.Outputs { | ||
| assetIDSet.Add(out.Asset.ID()) | ||
| } | ||
| if assetIDSet.Size() > maxNumAssetIDs { | ||
| return fmt.Errorf("too many different asset IDs in channel "+ | ||
| "funding, got %d, max is %d", len(assetIDSet.ToSlice()), | ||
| maxNumAssetIDs) | ||
| } | ||
|
|
||
| // Now that we know the final funding asset root along with the splits, | ||
| // we can derive the tapscript root that'll be used alongside the | ||
| // internal key (which we'll only learn from lnd later as we finalize | ||
|
|
@@ -1677,6 +1764,43 @@ func (f *FundingController) chanFunder() { | |
| } | ||
| } | ||
|
|
||
| // channelAcceptor is a callback that's called by the lnd client when a new | ||
| // channel is proposed. This function is responsible for deciding whether to | ||
| // accept the channel based on the channel parameters, and to also set some | ||
| // channel parameters for our own side. | ||
| func (f *FundingController) channelAcceptor(_ context.Context, | ||
| req *lndclient.AcceptorRequest) (*lndclient.AcceptorResponse, error) { | ||
|
|
||
| // Avoid nil pointer dereference. | ||
| if req.CommitmentType == nil { | ||
| return nil, fmt.Errorf("commitment type is required") | ||
| } | ||
|
|
||
| // Ignore any non-asset channels, just accept them. | ||
| if *req.CommitmentType != lnwallet.CommitmentTypeSimpleTaprootOverlay { | ||
| return &lndclient.AcceptorResponse{ | ||
| Accept: true, | ||
| }, nil | ||
| } | ||
|
|
||
| // Reject custom channels that don't observe the max HTLC limit. | ||
| if req.MaxAcceptedHtlcs > maxNumHTLCsPerParty { | ||
| return &lndclient.AcceptorResponse{ | ||
| Accept: false, | ||
| Error: fmt.Sprintf("max accepted HTLCs must be at "+ | ||
| "most %d, got %d", maxNumHTLCsPerParty, | ||
| req.MaxAcceptedHtlcs), | ||
| }, nil | ||
| } | ||
|
|
||
| // Everything looks good, we can now set our own max HTLC limit we'll | ||
| // observe for this channel. | ||
| return &lndclient.AcceptorResponse{ | ||
| Accept: true, | ||
| MaxHtlcCount: maxNumHTLCsPerParty, | ||
| }, nil | ||
| } | ||
|
|
||
| // validateProofs validates the inclusion/exclusion/split proofs and the | ||
| // transfer witness of the given proofs. | ||
| func (f *FundingController) validateProofs(proofs []*proof.Proof) error { | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.