Skip to content

Commit b0fc46d

Browse files
authored
Merge branch 'dev' into stakers_model
2 parents adac490 + 8a8bfd4 commit b0fc46d

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+912
-839
lines changed

Dockerfile

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
# Changes to the minimum golang version must also be replicated in
22
# scripts/build_avalanche.sh
3-
# scripts/local.Dockerfile
43
# Dockerfile (here)
54
# README.md
65
# go.mod

go.mod

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ module github.com/ava-labs/avalanchego
22

33
// Changes to the minimum golang version must also be replicated in
44
// scripts/build_avalanche.sh
5-
// scripts/local.Dockerfile
65
// Dockerfile
76
// README.md
87
// go.mod (here, only major.minor can be specified)

network/p2p/client.go

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ type Client struct {
4141
handlerPrefix []byte
4242
router *Router
4343
sender common.AppSender
44+
// nodeSampler is used to select nodes to route AppRequestAny to
45+
nodeSampler NodeSampler
4446
}
4547

4648
// AppRequestAny issues an AppRequest to an arbitrary node decided by Client.
@@ -51,15 +53,12 @@ func (c *Client) AppRequestAny(
5153
appRequestBytes []byte,
5254
onResponse AppResponseCallback,
5355
) error {
54-
c.router.lock.RLock()
55-
peers := c.router.peers.Sample(1)
56-
c.router.lock.RUnlock()
57-
58-
if len(peers) != 1 {
56+
sampled := c.nodeSampler.Sample(ctx, 1)
57+
if len(sampled) != 1 {
5958
return ErrNoPeers
6059
}
6160

62-
nodeIDs := set.Of(peers[0])
61+
nodeIDs := set.Of(sampled...)
6362
return c.AppRequest(ctx, nodeIDs, appRequestBytes, onResponse)
6463
}
6564

network/p2p/handler.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ import (
1515
"github.com/ava-labs/avalanchego/utils/logging"
1616
)
1717

18+
var _ Handler = (*NoOpHandler)(nil)
19+
1820
// Handler is the server-side logic for virtual machine application protocols.
1921
type Handler interface {
2022
// AppGossip is called when handling an AppGossip message.
@@ -42,6 +44,20 @@ type Handler interface {
4244
) ([]byte, error)
4345
}
4446

47+
type NoOpHandler struct{}
48+
49+
func (NoOpHandler) AppGossip(context.Context, ids.NodeID, []byte) error {
50+
return nil
51+
}
52+
53+
func (NoOpHandler) AppRequest(context.Context, ids.NodeID, time.Time, []byte) ([]byte, error) {
54+
return nil, nil
55+
}
56+
57+
func (NoOpHandler) CrossChainAppRequest(context.Context, ids.ID, time.Time, []byte) ([]byte, error) {
58+
return nil, nil
59+
}
60+
4561
// responder automatically sends the response for a given request
4662
type responder struct {
4763
handlerID uint64

network/p2p/node_sampler.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// Copyright (C) 2019-2023, Ava Labs, Inc. All rights reserved.
2+
// See the file LICENSE for licensing terms.
3+
4+
package p2p
5+
6+
import (
7+
"context"
8+
9+
"github.com/ava-labs/avalanchego/ids"
10+
)
11+
12+
// NodeSampler samples nodes in network
13+
type NodeSampler interface {
14+
// Sample returns at most [limit] nodes. This may return fewer nodes if
15+
// fewer than [limit] are available.
16+
Sample(ctx context.Context, limit int) []ids.NodeID
17+
}

network/p2p/peers.go

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
// Copyright (C) 2019-2023, Ava Labs, Inc. All rights reserved.
2+
// See the file LICENSE for licensing terms.
3+
4+
package p2p
5+
6+
import (
7+
"context"
8+
"sync"
9+
10+
"github.com/ava-labs/avalanchego/ids"
11+
"github.com/ava-labs/avalanchego/snow/validators"
12+
"github.com/ava-labs/avalanchego/utils/set"
13+
"github.com/ava-labs/avalanchego/version"
14+
)
15+
16+
var (
17+
_ validators.Connector = (*Peers)(nil)
18+
_ NodeSampler = (*Peers)(nil)
19+
)
20+
21+
// Peers contains a set of nodes that we are connected to.
22+
type Peers struct {
23+
lock sync.RWMutex
24+
peers set.SampleableSet[ids.NodeID]
25+
}
26+
27+
func (p *Peers) Connected(_ context.Context, nodeID ids.NodeID, _ *version.Application) error {
28+
p.lock.Lock()
29+
defer p.lock.Unlock()
30+
31+
p.peers.Add(nodeID)
32+
33+
return nil
34+
}
35+
36+
func (p *Peers) Disconnected(_ context.Context, nodeID ids.NodeID) error {
37+
p.lock.Lock()
38+
defer p.lock.Unlock()
39+
40+
p.peers.Remove(nodeID)
41+
42+
return nil
43+
}
44+
45+
func (p *Peers) Sample(_ context.Context, limit int) []ids.NodeID {
46+
p.lock.RLock()
47+
defer p.lock.RUnlock()
48+
49+
return p.peers.Sample(limit)
50+
}

network/p2p/peers_test.go

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
// Copyright (C) 2019-2023, Ava Labs, Inc. All rights reserved.
2+
// See the file LICENSE for licensing terms.
3+
4+
package p2p
5+
6+
import (
7+
"context"
8+
"testing"
9+
10+
"github.com/stretchr/testify/require"
11+
12+
"go.uber.org/mock/gomock"
13+
14+
"github.com/ava-labs/avalanchego/ids"
15+
"github.com/ava-labs/avalanchego/snow/engine/common"
16+
"github.com/ava-labs/avalanchego/utils/logging"
17+
"github.com/ava-labs/avalanchego/utils/math"
18+
"github.com/ava-labs/avalanchego/utils/set"
19+
)
20+
21+
// Sample should always return up to [limit] peers, and less if fewer than
22+
// [limit] peers are available.
23+
func TestPeersSample(t *testing.T) {
24+
nodeID1 := ids.GenerateTestNodeID()
25+
nodeID2 := ids.GenerateTestNodeID()
26+
nodeID3 := ids.GenerateTestNodeID()
27+
28+
tests := []struct {
29+
name string
30+
connected set.Set[ids.NodeID]
31+
disconnected set.Set[ids.NodeID]
32+
limit int
33+
}{
34+
{
35+
name: "no peers",
36+
limit: 1,
37+
},
38+
{
39+
name: "one peer connected",
40+
connected: set.Of(nodeID1),
41+
limit: 1,
42+
},
43+
{
44+
name: "multiple peers connected",
45+
connected: set.Of(nodeID1, nodeID2, nodeID3),
46+
limit: 1,
47+
},
48+
{
49+
name: "peer connects and disconnects - 1",
50+
connected: set.Of(nodeID1),
51+
disconnected: set.Of(nodeID1),
52+
limit: 1,
53+
},
54+
{
55+
name: "peer connects and disconnects - 2",
56+
connected: set.Of(nodeID1, nodeID2),
57+
disconnected: set.Of(nodeID2),
58+
limit: 1,
59+
},
60+
{
61+
name: "peer connects and disconnects - 2",
62+
connected: set.Of(nodeID1, nodeID2, nodeID3),
63+
disconnected: set.Of(nodeID1, nodeID2),
64+
limit: 1,
65+
},
66+
{
67+
name: "less than limit peers",
68+
connected: set.Of(nodeID1, nodeID2, nodeID3),
69+
limit: 4,
70+
},
71+
{
72+
name: "limit peers",
73+
connected: set.Of(nodeID1, nodeID2, nodeID3),
74+
limit: 3,
75+
},
76+
{
77+
name: "more than limit peers",
78+
connected: set.Of(nodeID1, nodeID2, nodeID3),
79+
limit: 2,
80+
},
81+
}
82+
83+
for _, tt := range tests {
84+
t.Run(tt.name, func(t *testing.T) {
85+
require := require.New(t)
86+
peers := &Peers{}
87+
88+
for connected := range tt.connected {
89+
require.NoError(peers.Connected(context.Background(), connected, nil))
90+
}
91+
92+
for disconnected := range tt.disconnected {
93+
require.NoError(peers.Disconnected(context.Background(), disconnected))
94+
}
95+
96+
sampleable := set.Set[ids.NodeID]{}
97+
sampleable.Union(tt.connected)
98+
sampleable.Difference(tt.disconnected)
99+
100+
sampled := peers.Sample(context.Background(), tt.limit)
101+
require.Len(sampled, math.Min(tt.limit, len(sampleable)))
102+
require.Subset(sampleable, sampled)
103+
})
104+
}
105+
}
106+
107+
func TestAppRequestAnyNodeSelection(t *testing.T) {
108+
tests := []struct {
109+
name string
110+
peers []ids.NodeID
111+
expected error
112+
}{
113+
{
114+
name: "no peers",
115+
expected: ErrNoPeers,
116+
},
117+
{
118+
name: "has peers",
119+
peers: []ids.NodeID{ids.GenerateTestNodeID()},
120+
},
121+
}
122+
123+
for _, tt := range tests {
124+
t.Run(tt.name, func(t *testing.T) {
125+
require := require.New(t)
126+
ctrl := gomock.NewController(t)
127+
mockAppSender := common.NewMockSender(ctrl)
128+
129+
expectedCalls := 0
130+
if tt.expected == nil {
131+
expectedCalls = 1
132+
}
133+
mockAppSender.EXPECT().SendAppRequest(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Times(expectedCalls)
134+
135+
r := NewRouter(logging.NoLog{}, mockAppSender)
136+
peers := &Peers{}
137+
for _, peer := range tt.peers {
138+
require.NoError(peers.Connected(context.Background(), peer, nil))
139+
}
140+
141+
client, err := r.RegisterAppProtocol(1, nil, peers)
142+
require.NoError(err)
143+
144+
err = client.AppRequestAny(context.Background(), []byte("foobar"), nil)
145+
require.ErrorIs(err, tt.expected)
146+
})
147+
}
148+
}

network/p2p/router.go

Lines changed: 3 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -16,18 +16,14 @@ import (
1616
"github.com/ava-labs/avalanchego/ids"
1717
"github.com/ava-labs/avalanchego/message"
1818
"github.com/ava-labs/avalanchego/snow/engine/common"
19-
"github.com/ava-labs/avalanchego/snow/validators"
2019
"github.com/ava-labs/avalanchego/utils/logging"
21-
"github.com/ava-labs/avalanchego/utils/set"
22-
"github.com/ava-labs/avalanchego/version"
2320
)
2421

2522
var (
2623
ErrExistingAppProtocol = errors.New("existing app protocol")
2724
ErrUnrequestedResponse = errors.New("unrequested response")
2825

29-
_ common.AppHandler = (*Router)(nil)
30-
_ validators.Connector = (*Router)(nil)
26+
_ common.AppHandler = (*Router)(nil)
3127
)
3228

3329
// Router routes incoming application messages to the corresponding registered
@@ -42,7 +38,6 @@ type Router struct {
4238
pendingAppRequests map[uint32]AppResponseCallback
4339
pendingCrossChainAppRequests map[uint32]CrossChainAppResponseCallback
4440
requestID uint32
45-
peers set.SampleableSet[ids.NodeID]
4641
}
4742

4843
// NewRouter returns a new instance of Router
@@ -56,26 +51,10 @@ func NewRouter(log logging.Logger, sender common.AppSender) *Router {
5651
}
5752
}
5853

59-
func (r *Router) Connected(_ context.Context, nodeID ids.NodeID, _ *version.Application) error {
60-
r.lock.Lock()
61-
defer r.lock.Unlock()
62-
63-
r.peers.Add(nodeID)
64-
return nil
65-
}
66-
67-
func (r *Router) Disconnected(_ context.Context, nodeID ids.NodeID) error {
68-
r.lock.Lock()
69-
defer r.lock.Unlock()
70-
71-
r.peers.Remove(nodeID)
72-
return nil
73-
}
74-
7554
// RegisterAppProtocol reserves an identifier for an application protocol and
7655
// returns a Client that can be used to send messages for the corresponding
7756
// protocol.
78-
func (r *Router) RegisterAppProtocol(handlerID uint64, handler Handler) (*Client, error) {
57+
func (r *Router) RegisterAppProtocol(handlerID uint64, handler Handler, nodeSampler NodeSampler) (*Client, error) {
7958
r.lock.Lock()
8059
defer r.lock.Unlock()
8160

@@ -94,6 +73,7 @@ func (r *Router) RegisterAppProtocol(handlerID uint64, handler Handler) (*Client
9473
handlerPrefix: binary.AppendUvarint(nil, handlerID),
9574
sender: r.sender,
9675
router: r,
76+
nodeSampler: nodeSampler,
9777
}, nil
9878
}
9979

0 commit comments

Comments
 (0)