Skip to content
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
8 changes: 8 additions & 0 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -738,6 +738,10 @@ type P2PConfig struct { //nolint: maligned
// Comma separated list of nodes to keep persistent connections to
PersistentPeers string `mapstructure:"persistent-peers"`

// If true, only peers from persistent-peers and bootstrap-peers are allowed
// to connect (inbound and outbound).
AllowlistOnly bool `mapstructure:"allowlist-only"`

// UPNP port forwarding
UPNP bool `mapstructure:"upnp"`

Expand Down Expand Up @@ -812,6 +816,7 @@ func DefaultP2PConfig() *P2PConfig {
HandshakeTimeout: 20 * time.Second,
DialTimeout: 3 * time.Second,
QueueType: "simple-priority",
AllowlistOnly: false,
}
}

Expand Down Expand Up @@ -839,6 +844,9 @@ func (cfg *P2PConfig) ValidateBasic() error {
if cfg.IncomingConnectionWindow < 1*time.Millisecond {
return errors.New("incoming-connection-window must be set to at least 1ms")
}
if cfg.AllowlistOnly && strings.TrimSpace(cfg.PersistentPeers) == "" && strings.TrimSpace(cfg.BootstrapPeers) == "" {
return errors.New("allowlist-only requires at least one of persistent-peers or bootstrap-peers")
}
return nil
}

Expand Down
8 changes: 8 additions & 0 deletions config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,14 @@ func TestP2PConfigValidateBasic(t *testing.T) {
assert.Error(t, cfg.ValidateBasic())
reflect.ValueOf(cfg).Elem().FieldByName(fieldName).SetInt(0)
}

cfg.AllowlistOnly = true
cfg.PersistentPeers = ""
cfg.BootstrapPeers = ""
assert.Error(t, cfg.ValidateBasic())

cfg.PersistentPeers = "id@127.0.0.1:26656"
assert.NoError(t, cfg.ValidateBasic())
}

// Given some invalid node key file, when I try to load it, I get an error
Expand Down
4 changes: 4 additions & 0 deletions config/toml.go
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,10 @@ bootstrap-peers = "{{ .P2P.BootstrapPeers }}"
# Comma separated list of nodes to keep persistent connections to
persistent-peers = "{{ .P2P.PersistentPeers }}"

# If true, only peers from persistent-peers and bootstrap-peers are allowed
# to connect (inbound and outbound).
allowlist-only = {{ .P2P.AllowlistOnly }}

# UPNP port forwarding
upnp = {{ .P2P.UPNP }}

Expand Down
1 change: 1 addition & 0 deletions docs/nodes/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -521,6 +521,7 @@ This section will cover settings within the p2p section of the `config.toml`.
- > We recommend setting an external address. When used in a private network, Tendermint Core currently doesn't advertise the node's public address. There is active and ongoing work to improve the P2P system, but this is a helpful workaround for now.
- `persistent-peers` = is a list of comma separated peers that you will always want to be connected to. If you're already connected to the maximum number of peers, persistent peers will not be added.
- `pex` = turns the peer exchange reactor on or off. Validator node will want the `pex` turned off so it would not begin gossiping to unknown peers on the network. PeX can also be turned off for statically configured networks with fixed network connectivity. For full nodes on open, dynamic networks, it should be turned on.
- `allowlist-only` = if true, only peers from `persistent-peers` and `bootstrap-peers` are allowed to connect (inbound and outbound).
- `private-peer-ids` = is a comma-separated list of node ids that will _not_ be exposed to other peers (i.e., you will not tell other peers about the ids in this list). This can be filled with a validator's node id.

Recently the Tendermint Team conducted a refactor of the p2p layer. This lead to multiple config parameters being deprecated and/or replaced.
Expand Down
92 changes: 79 additions & 13 deletions node/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import (
"fmt"
"net"
"net/http"
"strconv"
"strings"
"time"

Expand All @@ -21,6 +20,7 @@ import (
"github.com/dashpay/tenderdash/internal/eventbus"
"github.com/dashpay/tenderdash/internal/eventlog"
"github.com/dashpay/tenderdash/internal/evidence"
tmstrings "github.com/dashpay/tenderdash/internal/libs/strings"
"github.com/dashpay/tenderdash/internal/mempool"
"github.com/dashpay/tenderdash/internal/p2p"
p2pclient "github.com/dashpay/tenderdash/internal/p2p/client"
Expand Down Expand Up @@ -737,16 +737,32 @@ func loadStateFromDBOrGenesisDocProvider(stateStore sm.Store, genDoc *types.Gene
return state, nil
}

func getRouterConfig(conf *config.Config, appClient abciclient.Client) p2p.RouterOptions {
func getRouterConfig(conf *config.Config, appClient abciclient.Client) (p2p.RouterOptions, error) {
opts := p2p.RouterOptions{
QueueType: conf.P2P.QueueType,
HandshakeTimeout: conf.P2P.HandshakeTimeout,
DialTimeout: conf.P2P.DialTimeout,
IncomingConnectionWindow: conf.P2P.IncomingConnectionWindow,
}

var filterByID func(context.Context, types.NodeID) error

if conf.P2P.AllowlistOnly {
allowedIDs, err := buildAllowlist(conf)
if err != nil {
return p2p.RouterOptions{}, err
}

filterByID = func(_ context.Context, id types.NodeID) error {
if _, ok := allowedIDs[id]; !ok {
return fmt.Errorf("peer %s is not in allowlist", id)
}
return nil
}
}

if conf.FilterPeers && appClient != nil {
opts.FilterPeerByID = func(ctx context.Context, id types.NodeID) error {
abciFilterByID := func(ctx context.Context, id types.NodeID) error {
res, err := appClient.Query(ctx, &abci.RequestQuery{
Path: fmt.Sprintf("/p2p/filter/id/%s", id),
})
Expand All @@ -760,23 +776,73 @@ func getRouterConfig(conf *config.Config, appClient abciclient.Client) p2p.Route
return nil
}

opts.FilterPeerByIP = func(ctx context.Context, ip net.IP, port uint16) error {
res, err := appClient.Query(ctx, &abci.RequestQuery{
Path: fmt.Sprintf("/p2p/filter/addr/%s", net.JoinHostPort(ip.String(), strconv.Itoa(int(port)))),
})
if err != nil {
filterByID = chainFilterByID(filterByID, abciFilterByID)
}

opts.FilterPeerByID = filterByID

return opts, nil
}

func chainFilterByID(filters ...func(context.Context, types.NodeID) error) func(context.Context, types.NodeID) error {
var chained []func(context.Context, types.NodeID) error
for _, f := range filters {
if f != nil {
chained = append(chained, f)
}
}
if len(chained) == 0 {
return nil
}

return func(ctx context.Context, id types.NodeID) error {
for _, f := range chained {
if err := f(ctx, id); err != nil {
return err
}
if res.IsErr() {
return fmt.Errorf("error querying abci app: %v", res)
}
}
return nil
}
}

return nil
func buildAllowlist(conf *config.Config) (map[types.NodeID]struct{}, error) {
allowedIDs := make(map[types.NodeID]struct{})

addPeer := func(p string) error {
address, err := p2p.ParseNodeAddress(p)
if err != nil {
return fmt.Errorf("invalid allowlist peer address %q: %w", p, err)
}

if address.NodeID != "" {
allowedIDs[address.NodeID] = struct{}{}
}

return nil
}

for _, p := range tmstrings.SplitAndTrimEmpty(conf.P2P.PersistentPeers, ",", " ") {
if err := addPeer(p); err != nil {
return nil, err
}
}

for _, p := range tmstrings.SplitAndTrimEmpty(conf.P2P.BootstrapPeers, ",", " ") {
if err := addPeer(p); err != nil {
return nil, err
}
}

if len(allowedIDs) == 0 {
return nil, fmt.Errorf(
"allowlist-only enabled but no NodeIDs found in persistent-peers or bootstrap-peers; "+
"include NodeID@host:port entries or disable allowlist-only (persistent-peers=%q bootstrap-peers=%q)",
conf.P2P.PersistentPeers,
conf.P2P.BootstrapPeers,
)
}

return opts
return allowedIDs, nil
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.

// DefaultDashCoreRPCClient returns RPC client for the Dash Core node.
Expand Down
36 changes: 36 additions & 0 deletions node/node_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"math"
"net"
"os"
"strings"
"testing"
"time"

Expand Down Expand Up @@ -139,6 +140,41 @@ func TestNodeDelayedStart(t *testing.T) {
assert.Equal(t, true, startTime.After(n.GenesisDoc().GenesisTime))
}

func TestGetRouterConfigAllowlistOnlyFilters(t *testing.T) {
cfg := config.TestConfig()
cfg.P2P.AllowlistOnly = true

id1 := types.NodeID(strings.Repeat("a", 40))
id2 := types.NodeID(strings.Repeat("b", 40))
id3 := types.NodeID(strings.Repeat("c", 40))

cfg.P2P.PersistentPeers = fmt.Sprintf("tcp://%s@127.0.0.1:26656", id1)
cfg.P2P.BootstrapPeers = fmt.Sprintf("tcp://%s@127.0.0.2:26656", id2)

opts, err := getRouterConfig(cfg, nil)
require.NoError(t, err)
require.NotNil(t, opts.FilterPeerByID)

require.NoError(t, opts.FilterPeerByID(context.Background(), id1))
require.NoError(t, opts.FilterPeerByID(context.Background(), id2))
require.Error(t, opts.FilterPeerByID(context.Background(), id3))
}

func TestGetRouterConfigAllowlistOnlyAcceptsOnlyNodeIDs(t *testing.T) {
cfg := config.TestConfig()
cfg.P2P.AllowlistOnly = true

id1 := types.NodeID(strings.Repeat("a", 40))
cfg.P2P.PersistentPeers = fmt.Sprintf("tcp:%s", id1)

opts, err := getRouterConfig(cfg, nil)
require.NoError(t, err)
require.NotNil(t, opts.FilterPeerByID)

require.NoError(t, opts.FilterPeerByID(context.Background(), id1))
require.Error(t, opts.FilterPeerByID(context.Background(), types.NodeID(strings.Repeat("b", 40))))
}

func TestNodeSetAppVersion(t *testing.T) {
cfg, err := config.ResetTestRoot(t.TempDir(), t.Name())
require.NoError(t, err)
Expand Down
7 changes: 6 additions & 1 deletion node/setup.go
Original file line number Diff line number Diff line change
Expand Up @@ -329,6 +329,11 @@ func createRouter(
return nil, err
}

opts, err := getRouterConfig(cfg, appClient)
if err != nil {
return nil, err
}

return p2p.NewRouter(
p2pLogger,
p2pMetrics,
Expand All @@ -337,7 +342,7 @@ func createRouter(
nodeInfoProducer,
transport,
ep,
getRouterConfig(cfg, appClient),
opts,
)
}

Expand Down
Loading