Skip to content

Commit cf34c65

Browse files
committed
tapchannel: check for feature bits before opening chans
With the way the protocol works as is, we signal the optional feature bit, so connections can be made with older peers. Rather than timeout after they don't recognize our custom message, we'll instead use the feature bits to detect early if they don't understand the new protocol. Fixes #1038
1 parent d2a71e0 commit cf34c65

File tree

3 files changed

+87
-0
lines changed

3 files changed

+87
-0
lines changed

feature_bit_verifier.go

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
package taprootassets
2+
3+
import (
4+
"context"
5+
6+
"github.com/btcsuite/btcd/btcec/v2"
7+
"github.com/lightninglabs/lndclient"
8+
"github.com/lightninglabs/taproot-assets/tapchannel"
9+
"github.com/lightningnetwork/lnd/lnwire"
10+
"github.com/lightningnetwork/lnd/routing/route"
11+
)
12+
13+
// LndFeatureBitVerifier is a struct that verifies that the feature bits of a
14+
// target connected peer, using our registered lnd node.
15+
type LndFeatureBitVerifier struct {
16+
lnd *lndclient.LndServices
17+
}
18+
19+
// NewLndFeatureBitVerifier creates a new LndFeatureBitVerifier instance.
20+
func NewLndFeatureBitVerifier(
21+
lnd *lndclient.LndServices) *LndFeatureBitVerifier {
22+
23+
return &LndFeatureBitVerifier{
24+
lnd: lnd,
25+
}
26+
}
27+
28+
// HasFeature returns true if the peer has the given feature bit set. If the
29+
// peer can't be found, then ErrNoPeer is returned.
30+
func (l *LndFeatureBitVerifier) HasFeature(ctx context.Context,
31+
peerPub btcec.PublicKey, bit lnwire.FeatureBit) (bool, error) {
32+
33+
peerBytes := route.NewVertex(&peerPub)
34+
35+
peers, err := l.lnd.Client.ListPeers(ctx)
36+
if err != nil {
37+
return false, err
38+
}
39+
40+
for _, peer := range peers {
41+
if peer.Pubkey != peerBytes {
42+
continue
43+
}
44+
45+
return peer.Features.HasFeature(bit), nil
46+
}
47+
48+
// If we get to this point, we weren't able to find the peer.
49+
return false, tapchannel.ErrNoPeer
50+
}
51+
52+
// A compile-time check to ensure that LndFeatureBitVerifier implements the
53+
// FeatureBitVerifier interface.
54+
var _ tapchannel.FeatureBitVerifer = (*LndFeatureBitVerifier)(nil)

tapcfg/server.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ func genServerConfig(cfg *Config, cfgLogger btclog.Logger,
102102
msgTransportClient := tap.NewLndMsgTransportClient(lndServices)
103103
lndRouterClient := tap.NewLndRouterClient(lndServices)
104104
lndInvoicesClient := tap.NewLndInvoicesClient(lndServices)
105+
lndFeatureBitsVerifier := tap.NewLndFeatureBitVerifier(lndServices)
105106

106107
uniDB := tapdb.NewTransactionExecutor(
107108
db, func(tx *sql.Tx) tapdb.BaseUniverseStore {
@@ -434,6 +435,7 @@ func genServerConfig(cfg *Config, cfgLogger btclog.Logger,
434435
TxSender: chainPorter,
435436
DefaultCourierAddr: proofCourierAddr,
436437
AssetSyncer: addrBook,
438+
FeatureBits: lndFeatureBitsVerifier,
437439
},
438440
)
439441
auxTrafficShaper := tapchannel.NewAuxTrafficShaper(

tapchannel/aux_funding_controller.go

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,18 @@ type PeerMessenger interface {
7070
msg lnwire.Message) error
7171
}
7272

73+
// ErrNoPeer is returned when a peer can't be found.
74+
var ErrNoPeer = errors.New("peer not found")
75+
76+
// FeatureBitVerifer is an interface that allows us to verify that a peer has a
77+
// given feature bit set.
78+
type FeatureBitVerifer interface {
79+
// HasFeature returns true if the peer has the given feature bit set.
80+
// If the peer can't be found, then ErrNoPeer is returned.
81+
HasFeature(ctx context.Context, peerPub btcec.PublicKey,
82+
bit lnwire.FeatureBit) (bool, error)
83+
}
84+
7385
// OpenChanReq is a request to open a new asset channel with a remote peer.
7486
type OpenChanReq struct {
7587
// ChanAmt is the amount of BTC to put into the channel. Some BTC is
@@ -200,6 +212,10 @@ type FundingControllerCfg struct {
200212
// AssetSyncer is used to ensure that we've already verified the asset
201213
// genesis for any assets used within channels.
202214
AssetSyncer AssetSyncer
215+
216+
// FeatureBits is used to verify that the peer has the required feature
217+
// to fund asset channels.
218+
FeatureBits FeatureBitVerifer
203219
}
204220

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

1340+
// Before we even attempt funding, let's make sure that the remote peer
1341+
// actually supports the feature bit.
1342+
supportsAssetChans, err := f.cfg.FeatureBits.HasFeature(
1343+
fundReq.ctx, fundReq.PeerPub,
1344+
lnwire.SimpleTaprootOverlayChansOptional,
1345+
)
1346+
if err != nil {
1347+
return fmt.Errorf("unable to query peer feature bits: %w", err)
1348+
}
1349+
1350+
if !supportsAssetChans {
1351+
return fmt.Errorf("peer %x does not support asset channels",
1352+
fundReq.PeerPub.SerializeCompressed())
1353+
}
1354+
13241355
// To start, we'll make a new pending asset funding desc. This'll be
13251356
// our scratch pad during the asset funding process.
13261357
tempPID, err := newPendingChanID()

0 commit comments

Comments
 (0)