diff --git a/CHANGELOG.md b/CHANGELOG.md index bece3471..d528c95e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -37,6 +37,9 @@ IMPROVEMENTS: (`persistent_kvstore`) (name "dummy" is deprecated and will not work in release after this one) +FEATURES: +- [config] added the `--p2p.private_peer_ids` flag and `PrivatePeerIDs` config variable (see config for description) + ## 0.16.0 (February 20th, 2017) BREAKING CHANGES: diff --git a/cmd/tendermint/commands/run_node.go b/cmd/tendermint/commands/run_node.go index 0e18be03..0fcab3e3 100644 --- a/cmd/tendermint/commands/run_node.go +++ b/cmd/tendermint/commands/run_node.go @@ -31,11 +31,12 @@ func AddNodeFlags(cmd *cobra.Command) { // p2p flags cmd.Flags().String("p2p.laddr", config.P2P.ListenAddress, "Node listen address. (0.0.0.0:0 means any interface, any port)") - cmd.Flags().String("p2p.seeds", config.P2P.Seeds, "Comma delimited host:port seed nodes") - cmd.Flags().String("p2p.persistent_peers", config.P2P.PersistentPeers, "Comma delimited host:port persistent peers") + cmd.Flags().String("p2p.seeds", config.P2P.Seeds, "Comma-delimited ID@host:port seed nodes") + cmd.Flags().String("p2p.persistent_peers", config.P2P.PersistentPeers, "Comma-delimited ID@host:port persistent peers") cmd.Flags().Bool("p2p.skip_upnp", config.P2P.SkipUPNP, "Skip UPNP configuration") cmd.Flags().Bool("p2p.pex", config.P2P.PexReactor, "Enable/disable Peer-Exchange") cmd.Flags().Bool("p2p.seed_mode", config.P2P.SeedMode, "Enable/disable seed mode") + cmd.Flags().String("p2p.private_peer_ids", config.P2P.PrivatePeerIDs, "Comma-delimited private peer IDs") // consensus flags cmd.Flags().Bool("consensus.create_empty_blocks", config.Consensus.CreateEmptyBlocks, "Set this to false to only produce blocks when there are txs or when the AppHash changes") diff --git a/config/config.go b/config/config.go index a433047f..6b3f87b5 100644 --- a/config/config.go +++ b/config/config.go @@ -250,8 +250,8 @@ type P2PConfig struct { // We only use these if we can’t connect to peers in the addrbook Seeds string `mapstructure:"seeds"` - // Comma separated list of persistent peers to connect to - // We always connect to these + // Comma separated list of nodes to keep persistent connections to + // Do not add private peers to this list if you don't want them advertised PersistentPeers string `mapstructure:"persistent_peers"` // Skip UPNP port forwarding @@ -289,6 +289,9 @@ type P2PConfig struct { // Authenticated encryption AuthEnc bool `mapstructure:"auth_enc"` + + // Comma separated list of peer IDs to keep private (will not be gossiped to other peers) + PrivatePeerIDs string `mapstructure:"private_peer_ids"` } // DefaultP2PConfig returns a default configuration for the peer-to-peer layer diff --git a/config/toml.go b/config/toml.go index 35e0985f..305e98d1 100644 --- a/config/toml.go +++ b/config/toml.go @@ -127,6 +127,7 @@ laddr = "{{ .P2P.ListenAddress }}" seeds = "" # Comma separated list of nodes to keep persistent connections to +# Do not add private peers to this list if you don't want them advertised persistent_peers = "" # Path to address book @@ -162,6 +163,9 @@ seed_mode = {{ .P2P.SeedMode }} # Authenticated encryption auth_enc = {{ .P2P.AuthEnc }} +# Comma separated list of peer IDs to keep private (will not be gossiped to other peers) +private_peer_ids = {{ .P2P.PrivatePeerIDs }} + ##### mempool configuration options ##### [mempool] diff --git a/docs/specification/configuration.rst b/docs/specification/configuration.rst index 983ac87c..2a9ad794 100644 --- a/docs/specification/configuration.rst +++ b/docs/specification/configuration.rst @@ -89,6 +89,7 @@ like the file below, however, double check by inspecting the seeds = "" # Comma separated list of nodes to keep persistent connections to + # Do not add private peers to this list if you don't want them advertised persistent_peers = "" # Path to address book @@ -124,6 +125,9 @@ like the file below, however, double check by inspecting the # Authenticated encryption auth_enc = true + # Comma separated list of peer IDs to keep private (will not be gossiped to other peers) + private_peer_ids = "" + ##### mempool configuration options ##### [mempool] diff --git a/node/node.go b/node/node.go index dffdb83e..d978dcde 100644 --- a/node/node.go +++ b/node/node.go @@ -281,8 +281,15 @@ func NewNode(config *cfg.Config, if config.P2P.Seeds != "" { seeds = strings.Split(config.P2P.Seeds, ",") } + var privatePeerIDs []string + if config.P2P.PrivatePeerIDs != "" { + privatePeerIDs = strings.Split(config.P2P.PrivatePeerIDs, ",") + } pexReactor := pex.NewPEXReactor(addrBook, - &pex.PEXReactorConfig{Seeds: seeds, SeedMode: config.P2P.SeedMode}) + &pex.PEXReactorConfig{ + Seeds: seeds, + SeedMode: config.P2P.SeedMode, + PrivatePeerIDs: privatePeerIDs}) pexReactor.SetLogger(p2pLogger) sw.AddReactor("PEX", pexReactor) } diff --git a/p2p/pex/pex_reactor.go b/p2p/pex/pex_reactor.go index 441010aa..2abf2593 100644 --- a/p2p/pex/pex_reactor.go +++ b/p2p/pex/pex_reactor.go @@ -74,6 +74,10 @@ type PEXReactorConfig struct { // Seeds is a list of addresses reactor may use // if it can't connect to peers in the addrbook. Seeds []string + + // PrivatePeerIDs is a list of peer IDs, which must not be gossiped to other + // peers. + PrivatePeerIDs []string } type _attemptsToDial struct { @@ -152,7 +156,9 @@ func (r *PEXReactor) AddPeer(p Peer) { // Let the ensurePeersRoutine handle asking for more // peers when we need - we don't trust inbound peers as much. addr := p.NodeInfo().NetAddress() - r.book.AddAddress(addr, addr) + if !isAddrPrivate(addr, r.config.PrivatePeerIDs) { + r.book.AddAddress(addr, addr) + } } } @@ -252,7 +258,7 @@ func (r *PEXReactor) ReceiveAddrs(addrs []*p2p.NetAddress, src Peer) error { srcAddr := src.NodeInfo().NetAddress() for _, netAddr := range addrs { - if netAddr != nil { + if netAddr != nil && !isAddrPrivate(netAddr, r.config.PrivatePeerIDs) { r.book.AddAddress(netAddr, srcAddr) } } @@ -580,6 +586,16 @@ func (r *PEXReactor) attemptDisconnects() { } } +// isAddrPrivate returns true if addr is private. +func isAddrPrivate(addr *p2p.NetAddress, privatePeerIDs []string) bool { + for _, id := range privatePeerIDs { + if string(addr.ID) == id { + return true + } + } + return false +} + //----------------------------------------------------------------------------- // Messages diff --git a/p2p/pex/pex_reactor_test.go b/p2p/pex/pex_reactor_test.go index f5d81503..95cedfea 100644 --- a/p2p/pex/pex_reactor_test.go +++ b/p2p/pex/pex_reactor_test.go @@ -268,6 +268,25 @@ func TestPEXReactorCrawlStatus(t *testing.T) { // TODO: test } +func TestPEXReactorDoesNotAddPrivatePeersToAddrBook(t *testing.T) { + peer := p2p.CreateRandomPeer(false) + + pexR, book := createReactor(&PEXReactorConfig{PrivatePeerIDs: []string{string(peer.NodeInfo().ID())}}) + defer teardownReactor(book) + + // we have to send a request to receive responses + pexR.RequestAddrs(peer) + + size := book.Size() + addrs := []*p2p.NetAddress{peer.NodeInfo().NetAddress()} + msg := wire.BinaryBytes(struct{ PexMessage }{&pexAddrsMessage{Addrs: addrs}}) + pexR.Receive(PexChannel, peer, msg) + assert.Equal(t, size, book.Size()) + + pexR.AddPeer(peer) + assert.Equal(t, size, book.Size()) +} + func TestPEXReactorDialPeer(t *testing.T) { pexR, book := createReactor(&PEXReactorConfig{}) defer teardownReactor(book) @@ -376,7 +395,7 @@ func createReactor(config *PEXReactorConfig) (r *PEXReactor, book *addrBook) { book = NewAddrBook(filepath.Join(dir, "addrbook.json"), true) book.SetLogger(log.TestingLogger()) - r = NewPEXReactor(book, &PEXReactorConfig{}) + r = NewPEXReactor(book, config) r.SetLogger(log.TestingLogger()) return } diff --git a/rpc/core/net.go b/rpc/core/net.go index 14e7389d..9b04926a 100644 --- a/rpc/core/net.go +++ b/rpc/core/net.go @@ -2,6 +2,7 @@ package core import ( "github.com/pkg/errors" + ctypes "github.com/tendermint/tendermint/rpc/core/types" )