diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md index 160dce365..a78a03caa 100644 --- a/CHANGELOG_PENDING.md +++ b/CHANGELOG_PENDING.md @@ -20,6 +20,7 @@ Friendly reminder, we have a [bug bounty program](https://hackerone.com/tendermi ## IMPROVEMENTS - [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 diff --git a/p2p/switch.go b/p2p/switch.go index 4a5191c78..8fc42b3c2 100644 --- a/p2p/switch.go +++ b/p2p/switch.go @@ -46,6 +46,7 @@ func MConnConfig(cfg *config.P2PConfig) conn.MConnConfig { // to store peer addresses. type AddrBook interface { AddAddress(addr *NetAddress, src *NetAddress) error + AddPrivateIDs([]string) AddOurAddress(*NetAddress) OurAddress(*NetAddress) bool MarkGood(ID) @@ -582,6 +583,21 @@ func (sw *Switch) AddUnconditionalPeerIDs(ids []string) error { 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 { for _, pa := range sw.persistentPeersAddrs { if pa.Equals(na) { diff --git a/p2p/switch_test.go b/p2p/switch_test.go index 82cb6b06d..d0c4755f0 100644 --- a/p2p/switch_test.go +++ b/p2p/switch_test.go @@ -98,9 +98,9 @@ func MakeSwitchPair(t testing.TB, initSwitch func(int, *Switch) *Switch) (*Switc } 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 sw.AddReactor("foo", NewTestReactor([]*conn.ChannelDescriptor{ @@ -827,29 +827,3 @@ func BenchmarkSwitchBroadcast(b *testing.B) { 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() {} diff --git a/p2p/test_util.go b/p2p/test_util.go index 70e3aec0b..d52f918da 100644 --- a/p2p/test_util.go +++ b/p2p/test_util.go @@ -280,3 +280,35 @@ func getFreePort() int { } 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{}{} + } +} diff --git a/rpc/client/local/local.go b/rpc/client/local/local.go index 54a2041ea..491b27083 100644 --- a/rpc/client/local/local.go +++ b/rpc/client/local/local.go @@ -132,8 +132,8 @@ func (c *Local) DialSeeds(seeds []string) (*ctypes.ResultDialSeeds, error) { 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) { diff --git a/rpc/client/mock/client.go b/rpc/client/mock/client.go index 6fea29807..7344eb383 100644 --- a/rpc/client/mock/client.go +++ b/rpc/client/mock/client.go @@ -138,8 +138,8 @@ func (c Client) DialSeeds(seeds []string) (*ctypes.ResultDialSeeds, error) { 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) { diff --git a/rpc/core/env.go b/rpc/core/env.go index 489b61b42..bcd79eb28 100644 --- a/rpc/core/env.go +++ b/rpc/core/env.go @@ -58,6 +58,8 @@ type transport interface { type peers interface { AddPersistentPeers([]string) error + AddUnconditionalPeerIDs([]string) error + AddPrivatePeerIDs([]string) error DialPeersAsync([]string) error Peers() p2p.IPeerSet } diff --git a/rpc/core/net.go b/rpc/core/net.go index 53d1a6d0c..a8aedf9e0 100644 --- a/rpc/core/net.go +++ b/rpc/core/net.go @@ -3,6 +3,7 @@ package core import ( "errors" "fmt" + "strings" "github.com/tendermint/tendermint/p2p" 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), // 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 { 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 err := env.P2PPeers.AddPersistentPeers(peers); err != nil { 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 { return &ctypes.ResultDialPeers{}, err } + 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) { 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 +} diff --git a/rpc/core/net_test.go b/rpc/core/net_test.go index 537b9f390..c971776f3 100644 --- a/rpc/core/net_test.go +++ b/rpc/core/net_test.go @@ -49,6 +49,11 @@ func TestUnsafeDialSeeds(t *testing.T) { func TestUnsafeDialPeers(t *testing.T) { sw := p2p.MakeSwitch(cfg.DefaultP2PConfig(), 1, "testing", "123.123.123", 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() require.NoError(t, err) t.Cleanup(func() { @@ -61,16 +66,17 @@ func TestUnsafeDialPeers(t *testing.T) { env.P2PPeers = sw 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 { - res, err := UnsafeDialPeers(&rpctypes.Context{}, tc.peers, false) + res, err := UnsafeDialPeers(&rpctypes.Context{}, tc.peers, tc.persistence, tc.unconditional, tc.private) if tc.isErr { assert.Error(t, err) } else { diff --git a/rpc/core/routes.go b/rpc/core/routes.go index becc6a58d..639a4be93 100644 --- a/rpc/core/routes.go +++ b/rpc/core/routes.go @@ -50,6 +50,6 @@ var Routes = map[string]*rpc.RPCFunc{ func AddUnsafeRoutes() { // control API 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, "") } diff --git a/rpc/openapi/openapi.yaml b/rpc/openapi/openapi.yaml index c3cf2767f..8bb499dc9 100644 --- a/rpc/openapi/openapi.yaml +++ b/rpc/openapi/openapi.yaml @@ -569,6 +569,18 @@ paths: schema: type: boolean 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 name: peers description: array of peers to dial