mirror of
https://github.com/lightningnetwork/lnd.git
synced 2025-03-04 09:48:19 +01:00
Here we introduce the access manager which has caches that will determine the access control status of our peers. Peers that have had their funding transaction confirm with us are protected. Peers that only have pending-open channels with us are temporary access and can have their access revoked. The rest of the peers are granted restricted access.
153 lines
4.9 KiB
Go
153 lines
4.9 KiB
Go
package lnd
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"github.com/btcsuite/btcd/btcec/v2"
|
|
"github.com/lightningnetwork/lnd/channeldb"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
// assertInboundConnection asserts that we're able to accept an inbound
|
|
// connection successfully without any access permissions being violated.
|
|
func assertInboundConnection(t *testing.T, a *accessMan,
|
|
remotePub *btcec.PublicKey, status peerAccessStatus) {
|
|
|
|
remotePubSer := string(remotePub.SerializeCompressed())
|
|
|
|
isSlotAvailable, err := a.checkIncomingConnBanScore(remotePub)
|
|
require.NoError(t, err)
|
|
require.True(t, isSlotAvailable)
|
|
|
|
peerAccess, err := a.assignPeerPerms(remotePub)
|
|
require.NoError(t, err)
|
|
require.Equal(t, status, peerAccess)
|
|
|
|
a.addPeerAccess(remotePub, peerAccess)
|
|
peerScore, ok := a.peerScores[remotePubSer]
|
|
require.True(t, ok)
|
|
require.Equal(t, status, peerScore.state)
|
|
}
|
|
|
|
func assertAccessState(t *testing.T, a *accessMan, remotePub *btcec.PublicKey,
|
|
expectedStatus peerAccessStatus) {
|
|
|
|
remotePubSer := string(remotePub.SerializeCompressed())
|
|
peerScore, ok := a.peerScores[remotePubSer]
|
|
require.True(t, ok)
|
|
require.Equal(t, expectedStatus, peerScore.state)
|
|
}
|
|
|
|
// TestAccessManRestrictedSlots tests that the configurable number of
|
|
// restricted slots are properly allocated. It also tests that certain peers
|
|
// with access permissions are allowed to bypass the slot mechanism.
|
|
func TestAccessManRestrictedSlots(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
// We'll pre-populate the map to mock the database fetch. We'll make
|
|
// three peers. One has an open/closed channel. One has both an open
|
|
// / closed channel and a pending channel. The last one has only a
|
|
// pending channel.
|
|
peerPriv1, err := btcec.NewPrivateKey()
|
|
require.NoError(t, err)
|
|
peerKey1 := peerPriv1.PubKey()
|
|
peerKeySer1 := string(peerKey1.SerializeCompressed())
|
|
|
|
peerPriv2, err := btcec.NewPrivateKey()
|
|
require.NoError(t, err)
|
|
peerKey2 := peerPriv2.PubKey()
|
|
peerKeySer2 := string(peerKey2.SerializeCompressed())
|
|
|
|
peerPriv3, err := btcec.NewPrivateKey()
|
|
require.NoError(t, err)
|
|
peerKey3 := peerPriv3.PubKey()
|
|
peerKeySer3 := string(peerKey3.SerializeCompressed())
|
|
|
|
initPerms := func() (map[string]channeldb.ChanCount, error) {
|
|
return map[string]channeldb.ChanCount{
|
|
peerKeySer1: {
|
|
HasOpenOrClosedChan: true,
|
|
},
|
|
peerKeySer2: {
|
|
HasOpenOrClosedChan: true,
|
|
PendingOpenCount: 1,
|
|
},
|
|
peerKeySer3: {
|
|
HasOpenOrClosedChan: false,
|
|
PendingOpenCount: 1,
|
|
},
|
|
}, nil
|
|
}
|
|
|
|
disconnect := func(*btcec.PublicKey) (bool, error) {
|
|
return false, nil
|
|
}
|
|
|
|
cfg := &accessManConfig{
|
|
initAccessPerms: initPerms,
|
|
shouldDisconnect: disconnect,
|
|
maxRestrictedSlots: 1,
|
|
}
|
|
|
|
a, err := newAccessMan(cfg)
|
|
require.NoError(t, err)
|
|
|
|
// Check that the peerCounts map is correctly populated with three
|
|
// peers.
|
|
require.Equal(t, 0, int(a.numRestricted))
|
|
require.Equal(t, 3, len(a.peerCounts))
|
|
|
|
peerCount1, ok := a.peerCounts[peerKeySer1]
|
|
require.True(t, ok)
|
|
require.True(t, peerCount1.HasOpenOrClosedChan)
|
|
require.Equal(t, 0, int(peerCount1.PendingOpenCount))
|
|
|
|
peerCount2, ok := a.peerCounts[peerKeySer2]
|
|
require.True(t, ok)
|
|
require.True(t, peerCount2.HasOpenOrClosedChan)
|
|
require.Equal(t, 1, int(peerCount2.PendingOpenCount))
|
|
|
|
peerCount3, ok := a.peerCounts[peerKeySer3]
|
|
require.True(t, ok)
|
|
require.False(t, peerCount3.HasOpenOrClosedChan)
|
|
require.Equal(t, 1, int(peerCount3.PendingOpenCount))
|
|
|
|
// We'll now start to connect the peers. We'll add a new fourth peer
|
|
// that will take up the restricted slot. The first three peers should
|
|
// be able to bypass this restricted slot mechanism.
|
|
peerPriv4, err := btcec.NewPrivateKey()
|
|
require.NoError(t, err)
|
|
peerKey4 := peerPriv4.PubKey()
|
|
|
|
// Follow the normal process of an incoming connection. We check if we
|
|
// can accommodate this peer in checkIncomingConnBanScore and then we
|
|
// assign its access permissions and then insert into the map.
|
|
assertInboundConnection(t, a, peerKey4, peerStatusRestricted)
|
|
|
|
// Connect the three peers. This should happen without any issue.
|
|
assertInboundConnection(t, a, peerKey1, peerStatusProtected)
|
|
assertInboundConnection(t, a, peerKey2, peerStatusProtected)
|
|
assertInboundConnection(t, a, peerKey3, peerStatusTemporary)
|
|
|
|
// Check that a pending-open channel promotes the restricted peer.
|
|
err = a.newPendingOpenChan(peerKey4)
|
|
require.NoError(t, err)
|
|
assertAccessState(t, a, peerKey4, peerStatusTemporary)
|
|
|
|
// Check that an open channel promotes the temporary peer.
|
|
err = a.newOpenChan(peerKey3)
|
|
require.NoError(t, err)
|
|
assertAccessState(t, a, peerKey3, peerStatusProtected)
|
|
|
|
// We should be able to accommodate a new peer.
|
|
peerPriv5, err := btcec.NewPrivateKey()
|
|
require.NoError(t, err)
|
|
peerKey5 := peerPriv5.PubKey()
|
|
|
|
assertInboundConnection(t, a, peerKey5, peerStatusRestricted)
|
|
|
|
// Check that a pending-close channel event for peer 4 demotes the
|
|
// peer.
|
|
err = a.newPendingCloseChan(peerKey4)
|
|
require.ErrorIs(t, err, ErrNoMoreRestrictedAccessSlots)
|
|
}
|