Skip to content
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

tapchannel: check for feature bits before opening chans #1041

Merged
merged 2 commits into from
Aug 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 54 additions & 0 deletions feature_bit_verifier.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package taprootassets

import (
"context"

"github.com/btcsuite/btcd/btcec/v2"
"github.com/lightninglabs/lndclient"
"github.com/lightninglabs/taproot-assets/tapchannel"
"github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/routing/route"
)

// LndFeatureBitVerifier is a struct that verifies that the feature bits of a
// target connected peer, using our registered lnd node.
type LndFeatureBitVerifier struct {
lnd *lndclient.LndServices
}

// NewLndFeatureBitVerifier creates a new LndFeatureBitVerifier instance.
func NewLndFeatureBitVerifier(
lnd *lndclient.LndServices) *LndFeatureBitVerifier {

return &LndFeatureBitVerifier{
lnd: lnd,
}
}

// HasFeature returns true if the peer has the given feature bit set. If the
// peer can't be found, then ErrNoPeer is returned.
func (l *LndFeatureBitVerifier) HasFeature(ctx context.Context,
peerPub btcec.PublicKey, bit lnwire.FeatureBit) (bool, error) {

peerBytes := route.NewVertex(&peerPub)

peers, err := l.lnd.Client.ListPeers(ctx)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would GetNodeInfo also work here? IIUC the info returned from that call should be as up-to-date and what's returned from ListPeers, for connected nodes. But AFAICT it doesn't show if we're actually connected to that node.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So each time we connect to a peer, we'll get the fresh set of feature bits from the init message. Compared to waiting for the next node_announcement, this should be more up to date.

if err != nil {
return false, err
}

for _, peer := range peers {
if peer.Pubkey != peerBytes {
continue
}

return peer.Features.HasFeature(bit), nil
}

// If we get to this point, we weren't able to find the peer.
return false, tapchannel.ErrNoPeer
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems weird to be returning an error from a package using this code... I know it's probably unavoidable with the current way the packages are depending on each other. But we should likely move all the lnd client related code into its own package.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah not a bad idea to move them.

Re returning the error from another package: I think it can be a useful pattern (assuming we use a new type for the errors) to restrict the type of errors that an implementation can return.

}

// A compile-time check to ensure that LndFeatureBitVerifier implements the
// FeatureBitVerifier interface.
var _ tapchannel.FeatureBitVerifer = (*LndFeatureBitVerifier)(nil)
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ require (
github.com/lib/pq v1.10.9
github.com/lightninglabs/aperture v0.1.21-beta.0.20230705004936-87bb996a4030
github.com/lightninglabs/lightning-node-connect/hashmailrpc v1.0.2
github.com/lightninglabs/lndclient v1.0.1-0.20240607082608-4ce52a1a3f27
github.com/lightninglabs/lndclient v1.0.1-0.20240723001046-925d3c8297bf
github.com/lightninglabs/neutrino/cache v1.1.2
github.com/lightningnetwork/lnd v0.18.0-beta.rc4.0.20240723043204-f09d4042aee4
github.com/lightningnetwork/lnd/cert v1.2.2
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -480,8 +480,8 @@ github.com/lightninglabs/lightning-node-connect v0.2.5-alpha h1:ZRVChwczFXK0CEbx
github.com/lightninglabs/lightning-node-connect v0.2.5-alpha/go.mod h1:A9Pof9fETkH+F67BnOmrBDThPKstqp73wlImWOZvTXQ=
github.com/lightninglabs/lightning-node-connect/hashmailrpc v1.0.2 h1:Er1miPZD2XZwcfE4xoS5AILqP1mj7kqnhbBSxW9BDxY=
github.com/lightninglabs/lightning-node-connect/hashmailrpc v1.0.2/go.mod h1:antQGRDRJiuyQF6l+k6NECCSImgCpwaZapATth2Chv4=
github.com/lightninglabs/lndclient v1.0.1-0.20240607082608-4ce52a1a3f27 h1:vm8a13EzH2Qe6j4eZx+tHPeEVoNhJ7coihFPX6K2kco=
github.com/lightninglabs/lndclient v1.0.1-0.20240607082608-4ce52a1a3f27/go.mod h1:bxd2a15cIaW8KKcmOf9nNDI/GTxxj0upEYs1EIkttqw=
github.com/lightninglabs/lndclient v1.0.1-0.20240723001046-925d3c8297bf h1:VcTK/juPtAqwEBckCcSHCsVRSbHGbWtDZgnXL5JOLkg=
github.com/lightninglabs/lndclient v1.0.1-0.20240723001046-925d3c8297bf/go.mod h1:bxd2a15cIaW8KKcmOf9nNDI/GTxxj0upEYs1EIkttqw=
github.com/lightninglabs/neutrino v0.16.1-0.20240425105051-602843d34ffd h1:D8aRocHpoCv43hL8egXEMYyPmyOiefFHZ66338KQB2s=
github.com/lightninglabs/neutrino v0.16.1-0.20240425105051-602843d34ffd/go.mod h1:x3OmY2wsA18+Kc3TSV2QpSUewOCiscw2mKpXgZv2kZk=
github.com/lightninglabs/neutrino/cache v1.1.2 h1:C9DY/DAPaPxbFC+xNNEI/z1SJY9GS3shmlu5hIQ798g=
Expand Down
2 changes: 2 additions & 0 deletions tapcfg/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ func genServerConfig(cfg *Config, cfgLogger btclog.Logger,
msgTransportClient := tap.NewLndMsgTransportClient(lndServices)
lndRouterClient := tap.NewLndRouterClient(lndServices)
lndInvoicesClient := tap.NewLndInvoicesClient(lndServices)
lndFeatureBitsVerifier := tap.NewLndFeatureBitVerifier(lndServices)

uniDB := tapdb.NewTransactionExecutor(
db, func(tx *sql.Tx) tapdb.BaseUniverseStore {
Expand Down Expand Up @@ -434,6 +435,7 @@ func genServerConfig(cfg *Config, cfgLogger btclog.Logger,
TxSender: chainPorter,
DefaultCourierAddr: proofCourierAddr,
AssetSyncer: addrBook,
FeatureBits: lndFeatureBitsVerifier,
},
)
auxTrafficShaper := tapchannel.NewAuxTrafficShaper(
Expand Down
31 changes: 31 additions & 0 deletions tapchannel/aux_funding_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,18 @@ type PeerMessenger interface {
msg lnwire.Message) error
}

// ErrNoPeer is returned when a peer can't be found.
var ErrNoPeer = errors.New("peer not found")

// FeatureBitVerifer is an interface that allows us to verify that a peer has a
// given feature bit set.
type FeatureBitVerifer interface {
// HasFeature returns true if the peer has the given feature bit set.
// If the peer can't be found, then ErrNoPeer is returned.
HasFeature(ctx context.Context, peerPub btcec.PublicKey,
bit lnwire.FeatureBit) (bool, error)
}

// OpenChanReq is a request to open a new asset channel with a remote peer.
type OpenChanReq struct {
// ChanAmt is the amount of BTC to put into the channel. Some BTC is
Expand Down Expand Up @@ -200,6 +212,10 @@ type FundingControllerCfg struct {
// AssetSyncer is used to ensure that we've already verified the asset
// genesis for any assets used within channels.
AssetSyncer AssetSyncer

// FeatureBits is used to verify that the peer has the required feature
// to fund asset channels.
FeatureBits FeatureBitVerifer
}

// bindFundingReq is a request to bind a pending channel ID to a complete aux
Expand Down Expand Up @@ -1321,6 +1337,21 @@ func (f *FundingController) processFundingMsg(ctx context.Context,
func (f *FundingController) processFundingReq(fundingFlows fundingFlowIndex,
fundReq *FundReq) error {

// Before we even attempt funding, let's make sure that the remote peer
// actually supports the feature bit.
supportsAssetChans, err := f.cfg.FeatureBits.HasFeature(
fundReq.ctx, fundReq.PeerPub,
lnwire.SimpleTaprootOverlayChansOptional,
)
if err != nil {
return fmt.Errorf("unable to query peer feature bits: %w", err)
}

if !supportsAssetChans {
return fmt.Errorf("peer %x does not support asset channels",
fundReq.PeerPub.SerializeCompressed())
}

// To start, we'll make a new pending asset funding desc. This'll be
// our scratch pad during the asset funding process.
tempPID, err := newPendingChanID()
Expand Down
Loading