Browse Source

rpc: add private & unconditional to /dial_peer (#5293)

## Description

Allow dialing of private and unconditional peers through the RPC

Closes: #1705
pull/5318/merge
Marko 4 years ago
committed by GitHub
parent
commit
b6a5f7b126
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 124 additions and 42 deletions
  1. +1
    -0
      CHANGELOG_PENDING.md
  2. +16
    -0
      p2p/switch.go
  3. +3
    -29
      p2p/switch_test.go
  4. +32
    -0
      p2p/test_util.go
  5. +2
    -2
      rpc/client/local/local.go
  6. +2
    -2
      rpc/client/mock/client.go
  7. +2
    -0
      rpc/core/env.go
  8. +41
    -2
      rpc/core/net.go
  9. +12
    -6
      rpc/core/net_test.go
  10. +1
    -1
      rpc/core/routes.go
  11. +12
    -0
      rpc/openapi/openapi.yaml

+ 1
- 0
CHANGELOG_PENDING.md View File

@ -20,6 +20,7 @@ Friendly reminder, we have a [bug bounty program](https://hackerone.com/tendermi
## IMPROVEMENTS ## IMPROVEMENTS
- [blockchain] \#5278 Verify only +2/3 of the signatures in a block when fast syncing. (@marbar3778) - [blockchain] \#5278 Verify only +2/3 of the signatures in a block when fast syncing. (@marbar3778)
- [rpc] \#5293 `/dial_peers` has added `private` and `unconditional` as parameters. (@marbar3778)
## BUG FIXES ## BUG FIXES


+ 16
- 0
p2p/switch.go View File

@ -46,6 +46,7 @@ func MConnConfig(cfg *config.P2PConfig) conn.MConnConfig {
// to store peer addresses. // to store peer addresses.
type AddrBook interface { type AddrBook interface {
AddAddress(addr *NetAddress, src *NetAddress) error AddAddress(addr *NetAddress, src *NetAddress) error
AddPrivateIDs([]string)
AddOurAddress(*NetAddress) AddOurAddress(*NetAddress)
OurAddress(*NetAddress) bool OurAddress(*NetAddress) bool
MarkGood(ID) MarkGood(ID)
@ -582,6 +583,21 @@ func (sw *Switch) AddUnconditionalPeerIDs(ids []string) error {
return nil return nil
} }
func (sw *Switch) AddPrivatePeerIDs(ids []string) error {
validIDs := make([]string, 0, len(ids))
for i, id := range ids {
err := validateID(ID(id))
if err != nil {
return fmt.Errorf("wrong ID #%d: %w", i, err)
}
validIDs = append(validIDs, id)
}
sw.addrBook.AddPrivateIDs(validIDs)
return nil
}
func (sw *Switch) IsPeerPersistent(na *NetAddress) bool { func (sw *Switch) IsPeerPersistent(na *NetAddress) bool {
for _, pa := range sw.persistentPeersAddrs { for _, pa := range sw.persistentPeersAddrs {
if pa.Equals(na) { if pa.Equals(na) {


+ 3
- 29
p2p/switch_test.go View File

@ -98,9 +98,9 @@ func MakeSwitchPair(t testing.TB, initSwitch func(int, *Switch) *Switch) (*Switc
} }
func initSwitchFunc(i int, sw *Switch) *Switch { func initSwitchFunc(i int, sw *Switch) *Switch {
sw.SetAddrBook(&addrBookMock{
addrs: make(map[string]struct{}),
ourAddrs: make(map[string]struct{})})
sw.SetAddrBook(&AddrBookMock{
Addrs: make(map[string]struct{}),
OurAddrs: make(map[string]struct{})})
// Make two reactors of two channels each // Make two reactors of two channels each
sw.AddReactor("foo", NewTestReactor([]*conn.ChannelDescriptor{ sw.AddReactor("foo", NewTestReactor([]*conn.ChannelDescriptor{
@ -827,29 +827,3 @@ func BenchmarkSwitchBroadcast(b *testing.B) {
b.Logf("success: %v, failure: %v", numSuccess, numFailure) b.Logf("success: %v, failure: %v", numSuccess, numFailure)
} }
type addrBookMock struct {
addrs map[string]struct{}
ourAddrs map[string]struct{}
}
var _ AddrBook = (*addrBookMock)(nil)
func (book *addrBookMock) AddAddress(addr *NetAddress, src *NetAddress) error {
book.addrs[addr.String()] = struct{}{}
return nil
}
func (book *addrBookMock) AddOurAddress(addr *NetAddress) { book.ourAddrs[addr.String()] = struct{}{} }
func (book *addrBookMock) OurAddress(addr *NetAddress) bool {
_, ok := book.ourAddrs[addr.String()]
return ok
}
func (book *addrBookMock) MarkGood(ID) {}
func (book *addrBookMock) HasAddress(addr *NetAddress) bool {
_, ok := book.addrs[addr.String()]
return ok
}
func (book *addrBookMock) RemoveAddress(addr *NetAddress) {
delete(book.addrs, addr.String())
}
func (book *addrBookMock) Save() {}

+ 32
- 0
p2p/test_util.go View File

@ -280,3 +280,35 @@ func getFreePort() int {
} }
return port return port
} }
type AddrBookMock struct {
Addrs map[string]struct{}
OurAddrs map[string]struct{}
PrivateAddrs map[string]struct{}
}
var _ AddrBook = (*AddrBookMock)(nil)
func (book *AddrBookMock) AddAddress(addr *NetAddress, src *NetAddress) error {
book.Addrs[addr.String()] = struct{}{}
return nil
}
func (book *AddrBookMock) AddOurAddress(addr *NetAddress) { book.OurAddrs[addr.String()] = struct{}{} }
func (book *AddrBookMock) OurAddress(addr *NetAddress) bool {
_, ok := book.OurAddrs[addr.String()]
return ok
}
func (book *AddrBookMock) MarkGood(ID) {}
func (book *AddrBookMock) HasAddress(addr *NetAddress) bool {
_, ok := book.Addrs[addr.String()]
return ok
}
func (book *AddrBookMock) RemoveAddress(addr *NetAddress) {
delete(book.Addrs, addr.String())
}
func (book *AddrBookMock) Save() {}
func (book *AddrBookMock) AddPrivateIDs(addrs []string) {
for _, addr := range addrs {
book.PrivateAddrs[addr] = struct{}{}
}
}

+ 2
- 2
rpc/client/local/local.go View File

@ -132,8 +132,8 @@ func (c *Local) DialSeeds(seeds []string) (*ctypes.ResultDialSeeds, error) {
return core.UnsafeDialSeeds(c.ctx, seeds) return core.UnsafeDialSeeds(c.ctx, seeds)
} }
func (c *Local) DialPeers(peers []string, persistent bool) (*ctypes.ResultDialPeers, error) {
return core.UnsafeDialPeers(c.ctx, peers, persistent)
func (c *Local) DialPeers(peers []string, persistent, unconditional, private bool) (*ctypes.ResultDialPeers, error) {
return core.UnsafeDialPeers(c.ctx, peers, persistent, unconditional, private)
} }
func (c *Local) BlockchainInfo(minHeight, maxHeight int64) (*ctypes.ResultBlockchainInfo, error) { func (c *Local) BlockchainInfo(minHeight, maxHeight int64) (*ctypes.ResultBlockchainInfo, error) {


+ 2
- 2
rpc/client/mock/client.go View File

@ -138,8 +138,8 @@ func (c Client) DialSeeds(seeds []string) (*ctypes.ResultDialSeeds, error) {
return core.UnsafeDialSeeds(&rpctypes.Context{}, seeds) return core.UnsafeDialSeeds(&rpctypes.Context{}, seeds)
} }
func (c Client) DialPeers(peers []string, persistent bool) (*ctypes.ResultDialPeers, error) {
return core.UnsafeDialPeers(&rpctypes.Context{}, peers, persistent)
func (c Client) DialPeers(peers []string, persistent, unconditional, private bool) (*ctypes.ResultDialPeers, error) {
return core.UnsafeDialPeers(&rpctypes.Context{}, peers, persistent, unconditional, private)
} }
func (c Client) BlockchainInfo(minHeight, maxHeight int64) (*ctypes.ResultBlockchainInfo, error) { func (c Client) BlockchainInfo(minHeight, maxHeight int64) (*ctypes.ResultBlockchainInfo, error) {


+ 2
- 0
rpc/core/env.go View File

@ -58,6 +58,8 @@ type transport interface {
type peers interface { type peers interface {
AddPersistentPeers([]string) error AddPersistentPeers([]string) error
AddUnconditionalPeerIDs([]string) error
AddPrivatePeerIDs([]string) error
DialPeersAsync([]string) error DialPeersAsync([]string) error
Peers() p2p.IPeerSet Peers() p2p.IPeerSet
} }


+ 41
- 2
rpc/core/net.go View File

@ -3,6 +3,7 @@ package core
import ( import (
"errors" "errors"
"fmt" "fmt"
"strings"
"github.com/tendermint/tendermint/p2p" "github.com/tendermint/tendermint/p2p"
ctypes "github.com/tendermint/tendermint/rpc/core/types" ctypes "github.com/tendermint/tendermint/rpc/core/types"
@ -51,19 +52,42 @@ func UnsafeDialSeeds(ctx *rpctypes.Context, seeds []string) (*ctypes.ResultDialS
// UnsafeDialPeers dials the given peers (comma-separated id@IP:PORT), // UnsafeDialPeers dials the given peers (comma-separated id@IP:PORT),
// optionally making them persistent. // optionally making them persistent.
func UnsafeDialPeers(ctx *rpctypes.Context, peers []string, persistent bool) (*ctypes.ResultDialPeers, error) {
func UnsafeDialPeers(ctx *rpctypes.Context, peers []string, persistent, unconditional, private bool) (
*ctypes.ResultDialPeers, error) {
if len(peers) == 0 { if len(peers) == 0 {
return &ctypes.ResultDialPeers{}, errors.New("no peers provided") return &ctypes.ResultDialPeers{}, errors.New("no peers provided")
} }
env.Logger.Info("DialPeers", "peers", peers, "persistent", persistent)
ids, err := getIDs(peers)
if err != nil {
return &ctypes.ResultDialPeers{}, err
}
env.Logger.Info("DialPeers", "peers", peers, "persistent",
persistent, "unconditional", unconditional, "private", private)
if persistent { if persistent {
if err := env.P2PPeers.AddPersistentPeers(peers); err != nil { if err := env.P2PPeers.AddPersistentPeers(peers); err != nil {
return &ctypes.ResultDialPeers{}, err return &ctypes.ResultDialPeers{}, err
} }
} }
if private {
if err := env.P2PPeers.AddPrivatePeerIDs(ids); err != nil {
return &ctypes.ResultDialPeers{}, err
}
}
if unconditional {
if err := env.P2PPeers.AddUnconditionalPeerIDs(ids); err != nil {
return &ctypes.ResultDialPeers{}, err
}
}
if err := env.P2PPeers.DialPeersAsync(peers); err != nil { if err := env.P2PPeers.DialPeersAsync(peers); err != nil {
return &ctypes.ResultDialPeers{}, err return &ctypes.ResultDialPeers{}, err
} }
return &ctypes.ResultDialPeers{Log: "Dialing peers in progress. See /net_info for details"}, nil return &ctypes.ResultDialPeers{Log: "Dialing peers in progress. See /net_info for details"}, nil
} }
@ -72,3 +96,18 @@ func UnsafeDialPeers(ctx *rpctypes.Context, peers []string, persistent bool) (*c
func Genesis(ctx *rpctypes.Context) (*ctypes.ResultGenesis, error) { func Genesis(ctx *rpctypes.Context) (*ctypes.ResultGenesis, error) {
return &ctypes.ResultGenesis{Genesis: env.GenDoc}, nil return &ctypes.ResultGenesis{Genesis: env.GenDoc}, nil
} }
func getIDs(peers []string) ([]string, error) {
ids := make([]string, 0, len(peers))
for _, peer := range peers {
spl := strings.Split(peer, "@")
if len(spl) != 2 {
return nil, p2p.ErrNetAddressNoID{Addr: peer}
}
ids = append(ids, spl[0])
}
return ids, nil
}

+ 12
- 6
rpc/core/net_test.go View File

@ -49,6 +49,11 @@ func TestUnsafeDialSeeds(t *testing.T) {
func TestUnsafeDialPeers(t *testing.T) { func TestUnsafeDialPeers(t *testing.T) {
sw := p2p.MakeSwitch(cfg.DefaultP2PConfig(), 1, "testing", "123.123.123", sw := p2p.MakeSwitch(cfg.DefaultP2PConfig(), 1, "testing", "123.123.123",
func(n int, sw *p2p.Switch) *p2p.Switch { return sw }) func(n int, sw *p2p.Switch) *p2p.Switch { return sw })
sw.SetAddrBook(&p2p.AddrBookMock{
Addrs: make(map[string]struct{}),
OurAddrs: make(map[string]struct{}),
PrivateAddrs: make(map[string]struct{}),
})
err := sw.Start() err := sw.Start()
require.NoError(t, err) require.NoError(t, err)
t.Cleanup(func() { t.Cleanup(func() {
@ -61,16 +66,17 @@ func TestUnsafeDialPeers(t *testing.T) {
env.P2PPeers = sw env.P2PPeers = sw
testCases := []struct { testCases := []struct {
peers []string
isErr bool
peers []string
persistence, unconditional, private bool
isErr bool
}{ }{
{[]string{}, true},
{[]string{"d51fb70907db1c6c2d5237e78379b25cf1a37ab4@127.0.0.1:41198"}, false},
{[]string{"127.0.0.1:41198"}, true},
{[]string{}, false, false, false, true},
{[]string{"d51fb70907db1c6c2d5237e78379b25cf1a37ab4@127.0.0.1:41198"}, true, true, true, false},
{[]string{"127.0.0.1:41198"}, true, true, false, true},
} }
for _, tc := range testCases { for _, tc := range testCases {
res, err := UnsafeDialPeers(&rpctypes.Context{}, tc.peers, false)
res, err := UnsafeDialPeers(&rpctypes.Context{}, tc.peers, tc.persistence, tc.unconditional, tc.private)
if tc.isErr { if tc.isErr {
assert.Error(t, err) assert.Error(t, err)
} else { } else {


+ 1
- 1
rpc/core/routes.go View File

@ -50,6 +50,6 @@ var Routes = map[string]*rpc.RPCFunc{
func AddUnsafeRoutes() { func AddUnsafeRoutes() {
// control API // control API
Routes["dial_seeds"] = rpc.NewRPCFunc(UnsafeDialSeeds, "seeds") Routes["dial_seeds"] = rpc.NewRPCFunc(UnsafeDialSeeds, "seeds")
Routes["dial_peers"] = rpc.NewRPCFunc(UnsafeDialPeers, "peers,persistent")
Routes["dial_peers"] = rpc.NewRPCFunc(UnsafeDialPeers, "peers,persistent,unconditional,private")
Routes["unsafe_flush_mempool"] = rpc.NewRPCFunc(UnsafeFlushMempool, "") Routes["unsafe_flush_mempool"] = rpc.NewRPCFunc(UnsafeFlushMempool, "")
} }

+ 12
- 0
rpc/openapi/openapi.yaml View File

@ -569,6 +569,18 @@ paths:
schema: schema:
type: boolean type: boolean
example: true example: true
- in: query
name: unconditional
description: Have the peers you are dialing be unconditional
schema:
type: boolean
example: true
- in: query
name: private
description: Have the peers you are dialing be private
schema:
type: boolean
example: true
- in: query - in: query
name: peers name: peers
description: array of peers to dial description: array of peers to dial


Loading…
Cancel
Save