Skip to content

Commit 1147e6f

Browse files
authored
Merge branch 'dev' into e2e-staking-rewards
2 parents b5222cc + 6fe9dd0 commit 1147e6f

File tree

8 files changed

+180
-67
lines changed

8 files changed

+180
-67
lines changed

.github/workflows/build-and-test.yml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,16 @@ jobs:
2222
with:
2323
go-version: '~1.20.8'
2424
check-latest: true
25+
- name: Set timeout on Windows # Windows UT run slower and need a longer timeout
26+
shell: bash
27+
if: matrix.os == 'windows-2022'
28+
run: |
29+
echo "TIMEOUT=240s" >> $GITHUB_ENV
2530
- name: build_test
2631
shell: bash
2732
run: .github/workflows/build_and_test.sh
33+
env:
34+
TIMEOUT: ${{ env.TIMEOUT }}
2835
- name: fuzz_test
2936
shell: bash
3037
if: matrix.os == 'ubuntu-22.04' # Only run on Ubuntu 22.04

database/database.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ type KeyValueReader interface {
2121
Has(key []byte) (bool, error)
2222

2323
// Get retrieves the given key if it's present in the key-value data store.
24+
// Returns ErrNotFound if the key is not present in the key-value data store.
2425
//
2526
// Note: [key] is safe to modify and read after calling Get.
2627
// The returned byte slice is safe to read, but cannot be modified.

network/p2p/gossip/gossip.go

Lines changed: 65 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -20,27 +20,53 @@ import (
2020
"github.com/ava-labs/avalanchego/utils/wrappers"
2121
)
2222

23+
var (
24+
_ Gossiper = (*ValidatorGossiper)(nil)
25+
_ Gossiper = (*PullGossiper[testTx, *testTx])(nil)
26+
)
27+
28+
// Gossiper gossips Gossipables to other nodes
29+
type Gossiper interface {
30+
// Gossip runs a cycle of gossip. Returns an error if we failed to gossip.
31+
Gossip(ctx context.Context) error
32+
}
33+
2334
// GossipableAny exists to help create non-nil pointers to a concrete Gossipable
2435
// ref: https://stackoverflow.com/questions/69573113/how-can-i-instantiate-a-non-nil-pointer-of-type-argument-with-generic-go
2536
type GossipableAny[T any] interface {
2637
*T
2738
Gossipable
2839
}
2940

41+
// ValidatorGossiper only calls [Gossip] if the given node is a validator
42+
type ValidatorGossiper struct {
43+
Gossiper
44+
45+
NodeID ids.NodeID
46+
Validators p2p.ValidatorSet
47+
}
48+
49+
func (v ValidatorGossiper) Gossip(ctx context.Context) error {
50+
if !v.Validators.Has(ctx, v.NodeID) {
51+
return nil
52+
}
53+
54+
return v.Gossiper.Gossip(ctx)
55+
}
56+
3057
type Config struct {
3158
Namespace string
32-
Frequency time.Duration
3359
PollSize int
3460
}
3561

36-
func NewGossiper[T any, U GossipableAny[T]](
62+
func NewPullGossiper[T any, U GossipableAny[T]](
3763
config Config,
3864
log logging.Logger,
3965
set Set[U],
4066
client *p2p.Client,
4167
metrics prometheus.Registerer,
42-
) (*Gossiper[T, U], error) {
43-
g := &Gossiper[T, U]{
68+
) (*PullGossiper[T, U], error) {
69+
p := &PullGossiper[T, U]{
4470
config: config,
4571
log: log,
4672
set: set,
@@ -59,14 +85,14 @@ func NewGossiper[T any, U GossipableAny[T]](
5985

6086
errs := wrappers.Errs{}
6187
errs.Add(
62-
metrics.Register(g.receivedN),
63-
metrics.Register(g.receivedBytes),
88+
metrics.Register(p.receivedN),
89+
metrics.Register(p.receivedBytes),
6490
)
6591

66-
return g, errs.Err
92+
return p, errs.Err
6793
}
6894

69-
type Gossiper[T any, U GossipableAny[T]] struct {
95+
type PullGossiper[T any, U GossipableAny[T]] struct {
7096
config Config
7197
log logging.Logger
7298
set Set[U]
@@ -75,25 +101,8 @@ type Gossiper[T any, U GossipableAny[T]] struct {
75101
receivedBytes prometheus.Counter
76102
}
77103

78-
func (g *Gossiper[_, _]) Gossip(ctx context.Context) {
79-
gossipTicker := time.NewTicker(g.config.Frequency)
80-
defer gossipTicker.Stop()
81-
82-
for {
83-
select {
84-
case <-gossipTicker.C:
85-
if err := g.gossip(ctx); err != nil {
86-
g.log.Warn("failed to gossip", zap.Error(err))
87-
}
88-
case <-ctx.Done():
89-
g.log.Debug("shutting down gossip")
90-
return
91-
}
92-
}
93-
}
94-
95-
func (g *Gossiper[_, _]) gossip(ctx context.Context) error {
96-
bloom, salt, err := g.set.GetFilter()
104+
func (p *PullGossiper[_, _]) Gossip(ctx context.Context) error {
105+
bloom, salt, err := p.set.GetFilter()
97106
if err != nil {
98107
return err
99108
}
@@ -107,23 +116,23 @@ func (g *Gossiper[_, _]) gossip(ctx context.Context) error {
107116
return err
108117
}
109118

110-
for i := 0; i < g.config.PollSize; i++ {
111-
if err := g.client.AppRequestAny(ctx, msgBytes, g.handleResponse); err != nil {
119+
for i := 0; i < p.config.PollSize; i++ {
120+
if err := p.client.AppRequestAny(ctx, msgBytes, p.handleResponse); err != nil {
112121
return err
113122
}
114123
}
115124

116125
return nil
117126
}
118127

119-
func (g *Gossiper[T, U]) handleResponse(
128+
func (p *PullGossiper[T, U]) handleResponse(
120129
_ context.Context,
121130
nodeID ids.NodeID,
122131
responseBytes []byte,
123132
err error,
124133
) {
125134
if err != nil {
126-
g.log.Debug(
135+
p.log.Debug(
127136
"failed gossip request",
128137
zap.Stringer("nodeID", nodeID),
129138
zap.Error(err),
@@ -133,7 +142,7 @@ func (g *Gossiper[T, U]) handleResponse(
133142

134143
response := &sdk.PullGossipResponse{}
135144
if err := proto.Unmarshal(responseBytes, response); err != nil {
136-
g.log.Debug("failed to unmarshal gossip response", zap.Error(err))
145+
p.log.Debug("failed to unmarshal gossip response", zap.Error(err))
137146
return
138147
}
139148

@@ -143,7 +152,7 @@ func (g *Gossiper[T, U]) handleResponse(
143152

144153
gossipable := U(new(T))
145154
if err := gossipable.Unmarshal(bytes); err != nil {
146-
g.log.Debug(
155+
p.log.Debug(
147156
"failed to unmarshal gossip",
148157
zap.Stringer("nodeID", nodeID),
149158
zap.Error(err),
@@ -152,13 +161,13 @@ func (g *Gossiper[T, U]) handleResponse(
152161
}
153162

154163
hash := gossipable.GetID()
155-
g.log.Debug(
164+
p.log.Debug(
156165
"received gossip",
157166
zap.Stringer("nodeID", nodeID),
158167
zap.Stringer("id", hash),
159168
)
160-
if err := g.set.Add(gossipable); err != nil {
161-
g.log.Debug(
169+
if err := p.set.Add(gossipable); err != nil {
170+
p.log.Debug(
162171
"failed to add gossip to the known set",
163172
zap.Stringer("nodeID", nodeID),
164173
zap.Stringer("id", hash),
@@ -168,6 +177,24 @@ func (g *Gossiper[T, U]) handleResponse(
168177
}
169178
}
170179

171-
g.receivedN.Add(float64(len(response.Gossip)))
172-
g.receivedBytes.Add(float64(receivedBytes))
180+
p.receivedN.Add(float64(len(response.Gossip)))
181+
p.receivedBytes.Add(float64(receivedBytes))
182+
}
183+
184+
// Every calls [Gossip] every [frequency] amount of time.
185+
func Every(ctx context.Context, log logging.Logger, gossiper Gossiper, frequency time.Duration) {
186+
ticker := time.NewTicker(frequency)
187+
defer ticker.Stop()
188+
189+
for {
190+
select {
191+
case <-ticker.C:
192+
if err := gossiper.Gossip(ctx); err != nil {
193+
log.Warn("failed to gossip", zap.Error(err))
194+
}
195+
case <-ctx.Done():
196+
log.Debug("shutting down gossip")
197+
return
198+
}
199+
}
173200
}

network/p2p/gossip/gossip_test.go

Lines changed: 77 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,17 @@ import (
2222
"github.com/ava-labs/avalanchego/utils/set"
2323
)
2424

25+
var (
26+
_ p2p.ValidatorSet = (*testValidatorSet)(nil)
27+
_ Gossiper = (*testGossiper)(nil)
28+
)
29+
2530
func TestGossiperShutdown(t *testing.T) {
2631
require := require.New(t)
2732

28-
config := Config{Frequency: time.Second}
2933
metrics := prometheus.NewRegistry()
30-
gossiper, err := NewGossiper[testTx](
31-
config,
34+
gossiper, err := NewPullGossiper[testTx](
35+
Config{},
3236
logging.NoLog{},
3337
nil,
3438
nil,
@@ -41,7 +45,7 @@ func TestGossiperShutdown(t *testing.T) {
4145
wg.Add(1)
4246

4347
go func() {
44-
gossiper.Gossip(ctx)
48+
Every(ctx, logging.NoLog{}, gossiper, time.Second)
4549
wg.Done()
4650
}()
4751

@@ -166,10 +170,9 @@ func TestGossiperGossip(t *testing.T) {
166170
require.NoError(err)
167171

168172
config := Config{
169-
Frequency: 500 * time.Millisecond,
170-
PollSize: 1,
173+
PollSize: 1,
171174
}
172-
gossiper, err := NewGossiper[testTx, *testTx](
175+
gossiper, err := NewPullGossiper[testTx, *testTx](
173176
config,
174177
logging.NoLog{},
175178
requestSet,
@@ -182,7 +185,7 @@ func TestGossiperGossip(t *testing.T) {
182185
received.Add(tx)
183186
}
184187

185-
require.NoError(gossiper.gossip(context.Background()))
188+
require.NoError(gossiper.Gossip(context.Background()))
186189
<-gossiped
187190

188191
require.Len(requestSet.set, tt.expectedLen)
@@ -196,3 +199,69 @@ func TestGossiperGossip(t *testing.T) {
196199
})
197200
}
198201
}
202+
203+
func TestEvery(*testing.T) {
204+
ctx, cancel := context.WithCancel(context.Background())
205+
calls := 0
206+
gossiper := &testGossiper{
207+
gossipF: func(context.Context) error {
208+
if calls >= 10 {
209+
cancel()
210+
return nil
211+
}
212+
213+
calls++
214+
return nil
215+
},
216+
}
217+
218+
go Every(ctx, logging.NoLog{}, gossiper, time.Millisecond)
219+
<-ctx.Done()
220+
}
221+
222+
func TestValidatorGossiper(t *testing.T) {
223+
require := require.New(t)
224+
225+
nodeID := ids.GenerateTestNodeID()
226+
227+
validators := testValidatorSet{
228+
validators: set.Of(nodeID),
229+
}
230+
231+
calls := 0
232+
gossiper := ValidatorGossiper{
233+
Gossiper: &testGossiper{
234+
gossipF: func(context.Context) error {
235+
calls++
236+
return nil
237+
},
238+
},
239+
NodeID: nodeID,
240+
Validators: validators,
241+
}
242+
243+
// we are a validator, so we should request gossip
244+
require.NoError(gossiper.Gossip(context.Background()))
245+
require.Equal(1, calls)
246+
247+
// we are not a validator, so we should not request gossip
248+
validators.validators = set.Set[ids.NodeID]{}
249+
require.NoError(gossiper.Gossip(context.Background()))
250+
require.Equal(2, calls)
251+
}
252+
253+
type testGossiper struct {
254+
gossipF func(ctx context.Context) error
255+
}
256+
257+
func (t *testGossiper) Gossip(ctx context.Context) error {
258+
return t.gossipF(ctx)
259+
}
260+
261+
type testValidatorSet struct {
262+
validators set.Set[ids.NodeID]
263+
}
264+
265+
func (t testValidatorSet) Has(_ context.Context, nodeID ids.NodeID) bool {
266+
return t.validators.Contains(nodeID)
267+
}

scripts/build_test.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,4 @@ AVALANCHE_PATH=$( cd "$( dirname "${BASH_SOURCE[0]}" )"; cd .. && pwd )
88
source "$AVALANCHE_PATH"/scripts/constants.sh
99

1010
# Ensure execution of fixture unit tests under tests/ but exclude ginkgo tests in tests/e2e and tests/upgrade
11-
go test -shuffle=on -race -timeout="120s" -coverprofile="coverage.out" -covermode="atomic" $(go list ./... | grep -v /mocks | grep -v proto | grep -v tests/e2e | grep -v tests/upgrade)
11+
go test -shuffle=on -race -timeout=${TIMEOUT:-"120s"} -coverprofile="coverage.out" -covermode="atomic" $(go list ./... | grep -v /mocks | grep -v proto | grep -v tests/e2e | grep -v tests/upgrade)

vms/platformvm/client.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -78,8 +78,8 @@ type Client interface {
7878
GetCurrentValidators(ctx context.Context, subnetID ids.ID, nodeIDs []ids.NodeID, options ...rpc.Option) ([]ClientPermissionlessValidator, error)
7979
// GetPendingValidators returns the list of pending validators for subnet with ID [subnetID]
8080
GetPendingValidators(ctx context.Context, subnetID ids.ID, nodeIDs []ids.NodeID, options ...rpc.Option) ([]interface{}, []interface{}, error)
81-
// GetCurrentSupply returns an upper bound on the supply of AVAX in the system
82-
GetCurrentSupply(ctx context.Context, subnetID ids.ID, options ...rpc.Option) (uint64, error)
81+
// GetCurrentSupply returns an upper bound on the supply of AVAX in the system along with the P-chain height
82+
GetCurrentSupply(ctx context.Context, subnetID ids.ID, options ...rpc.Option) (uint64, uint64, error)
8383
// SampleValidators returns the nodeIDs of a sample of [sampleSize] validators from the current validator set for subnet with ID [subnetID]
8484
SampleValidators(ctx context.Context, subnetID ids.ID, sampleSize uint16, options ...rpc.Option) ([]ids.NodeID, error)
8585
// AddValidator issues a transaction to add a validator to the primary network
@@ -455,12 +455,12 @@ func (c *client) GetPendingValidators(
455455
return res.Validators, res.Delegators, err
456456
}
457457

458-
func (c *client) GetCurrentSupply(ctx context.Context, subnetID ids.ID, options ...rpc.Option) (uint64, error) {
458+
func (c *client) GetCurrentSupply(ctx context.Context, subnetID ids.ID, options ...rpc.Option) (uint64, uint64, error) {
459459
res := &GetCurrentSupplyReply{}
460460
err := c.requester.SendRequest(ctx, "platform.getCurrentSupply", &GetCurrentSupplyArgs{
461461
SubnetID: subnetID,
462462
}, res, options...)
463-
return uint64(res.Supply), err
463+
return uint64(res.Supply), uint64(res.Height), err
464464
}
465465

466466
func (c *client) SampleValidators(ctx context.Context, subnetID ids.ID, sampleSize uint16, options ...rpc.Option) ([]ids.NodeID, error) {

0 commit comments

Comments
 (0)