Skip to content

Commit abf4fbc

Browse files
abi87StephenButtolphdhrubabasu
authored
Drop Pending Stakers 3 - persist stakers' StartTime (#2306)
Co-authored-by: Stephen Buttolph <stephen@avalabs.org> Co-authored-by: dhrubabasu <7675102+dhrubabasu@users.noreply.github.com>
1 parent 0f4cff1 commit abf4fbc

File tree

13 files changed

+262
-55
lines changed

13 files changed

+262
-55
lines changed

vms/platformvm/block/builder/helpers_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ func newEnvironment(t *testing.T) *environment {
135135
res.fx = defaultFx(t, res.clk, res.ctx.Log, res.isBootstrapped.Get())
136136

137137
rewardsCalc := reward.NewCalculator(res.config.RewardConfig)
138-
res.state = defaultState(t, res.config.Validators, res.ctx, res.baseDB, rewardsCalc)
138+
res.state = defaultState(t, res.config, res.ctx, res.baseDB, rewardsCalc)
139139

140140
res.atomicUTXOs = avax.NewAtomicUTXOManager(res.ctx.SharedMemory, txs.Codec)
141141
res.uptimes = uptime.NewManager(res.state, res.clk)
@@ -237,7 +237,7 @@ func addSubnet(t *testing.T, env *environment) {
237237

238238
func defaultState(
239239
t *testing.T,
240-
validators validators.Manager,
240+
cfg *config.Config,
241241
ctx *snow.Context,
242242
db database.Database,
243243
rewards reward.Calculator,
@@ -250,7 +250,7 @@ func defaultState(
250250
db,
251251
genesisBytes,
252252
prometheus.NewRegistry(),
253-
validators,
253+
cfg,
254254
execCfg,
255255
ctx,
256256
metrics.Noop,

vms/platformvm/block/executor/helpers_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ func newEnvironment(t *testing.T, ctrl *gomock.Controller) *environment {
145145
res.atomicUTXOs = avax.NewAtomicUTXOManager(res.ctx.SharedMemory, txs.Codec)
146146

147147
if ctrl == nil {
148-
res.state = defaultState(res.config.Validators, res.ctx, res.baseDB, rewardsCalc)
148+
res.state = defaultState(res.config, res.ctx, res.baseDB, rewardsCalc)
149149
res.uptimes = uptime.NewManager(res.state, res.clk)
150150
res.utxosHandler = utxo.NewHandler(res.ctx, res.clk, res.fx)
151151
res.txBuilder = p_tx_builder.New(
@@ -263,7 +263,7 @@ func addSubnet(env *environment) {
263263
}
264264

265265
func defaultState(
266-
validators validators.Manager,
266+
cfg *config.Config,
267267
ctx *snow.Context,
268268
db database.Database,
269269
rewards reward.Calculator,
@@ -274,7 +274,7 @@ func defaultState(
274274
db,
275275
genesisBytes,
276276
prometheus.NewRegistry(),
277-
validators,
277+
cfg,
278278
execCfg,
279279
ctx,
280280
metrics.Noop,

vms/platformvm/state/metadata_codec.go

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,20 +8,28 @@ import (
88

99
"github.com/ava-labs/avalanchego/codec"
1010
"github.com/ava-labs/avalanchego/codec/linearcodec"
11+
"github.com/ava-labs/avalanchego/utils"
1112
)
1213

1314
const (
1415
v0tag = "v0"
1516
v0 = uint16(0)
17+
18+
v1tag = "v1"
19+
v1 = uint16(1)
1620
)
1721

1822
var metadataCodec codec.Manager
1923

2024
func init() {
21-
c := linearcodec.New([]string{v0tag}, math.MaxInt32)
25+
c0 := linearcodec.New([]string{v0tag}, math.MaxInt32)
26+
c1 := linearcodec.New([]string{v0tag, v1tag}, math.MaxInt32)
2227
metadataCodec = codec.NewManager(math.MaxInt32)
2328

24-
err := metadataCodec.RegisterCodec(v0, c)
29+
err := utils.Err(
30+
metadataCodec.RegisterCodec(v0, c0),
31+
metadataCodec.RegisterCodec(v1, c1),
32+
)
2533
if err != nil {
2634
panic(err)
2735
}

vms/platformvm/state/metadata_delegator.go

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,17 +9,36 @@ import (
99
)
1010

1111
type delegatorMetadata struct {
12-
PotentialReward uint64
12+
PotentialReward uint64 `v1:"true"`
13+
StakerStartTime uint64 `v1:"true"`
1314

1415
txID ids.ID
1516
}
1617

1718
func parseDelegatorMetadata(bytes []byte, metadata *delegatorMetadata) error {
1819
var err error
19-
metadata.PotentialReward, err = database.ParseUInt64(bytes)
20+
switch len(bytes) {
21+
case database.Uint64Size:
22+
// only potential reward was stored
23+
metadata.PotentialReward, err = database.ParseUInt64(bytes)
24+
default:
25+
_, err = metadataCodec.Unmarshal(bytes, metadata)
26+
}
2027
return err
2128
}
2229

23-
func writeDelegatorMetadata(db database.KeyValueWriter, metadata *delegatorMetadata) error {
24-
return database.PutUInt64(db, metadata.txID[:], metadata.PotentialReward)
30+
func writeDelegatorMetadata(db database.KeyValueWriter, metadata *delegatorMetadata, codecVersion uint16) error {
31+
// The "0" codec is skipped for [delegatorMetadata]. This is to ensure the
32+
// [validatorMetadata] codec version is the same as the [delegatorMetadata]
33+
// codec version.
34+
//
35+
// TODO: Cleanup post-Durango activation.
36+
if codecVersion == 0 {
37+
return database.PutUInt64(db, metadata.txID[:], metadata.PotentialReward)
38+
}
39+
metadataBytes, err := metadataCodec.Marshal(codecVersion, metadata)
40+
if err != nil {
41+
return err
42+
}
43+
return db.Put(metadata.txID[:], metadataBytes)
2544
}
Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
// Copyright (C) 2019-2023, Ava Labs, Inc. All rights reserved.
2+
// See the file LICENSE for licensing terms.
3+
4+
package state
5+
6+
import (
7+
"testing"
8+
9+
"github.com/stretchr/testify/require"
10+
11+
"github.com/ava-labs/avalanchego/codec"
12+
"github.com/ava-labs/avalanchego/database/memdb"
13+
"github.com/ava-labs/avalanchego/ids"
14+
"github.com/ava-labs/avalanchego/utils/wrappers"
15+
)
16+
17+
func TestParseDelegatorMetadata(t *testing.T) {
18+
type test struct {
19+
name string
20+
bytes []byte
21+
expected *delegatorMetadata
22+
expectedErr error
23+
}
24+
tests := []test{
25+
{
26+
name: "potential reward only no codec",
27+
bytes: []byte{
28+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7b,
29+
},
30+
expected: &delegatorMetadata{
31+
PotentialReward: 123,
32+
StakerStartTime: 0,
33+
},
34+
expectedErr: nil,
35+
},
36+
{
37+
name: "potential reward + staker start time with codec v1",
38+
bytes: []byte{
39+
// codec version
40+
0x00, 0x01,
41+
// potential reward
42+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7b,
43+
// staker start time
44+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xc8,
45+
},
46+
expected: &delegatorMetadata{
47+
PotentialReward: 123,
48+
StakerStartTime: 456,
49+
},
50+
expectedErr: nil,
51+
},
52+
{
53+
name: "invalid codec version",
54+
bytes: []byte{
55+
// codec version
56+
0x00, 0x02,
57+
// potential reward
58+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7b,
59+
// staker start time
60+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xc8,
61+
},
62+
expected: nil,
63+
expectedErr: codec.ErrUnknownVersion,
64+
},
65+
{
66+
name: "short byte len",
67+
bytes: []byte{
68+
// codec version
69+
0x00, 0x01,
70+
// potential reward
71+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7b,
72+
// staker start time
73+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
74+
},
75+
expected: nil,
76+
expectedErr: wrappers.ErrInsufficientLength,
77+
},
78+
}
79+
for _, tt := range tests {
80+
t.Run(tt.name, func(t *testing.T) {
81+
require := require.New(t)
82+
var metadata delegatorMetadata
83+
err := parseDelegatorMetadata(tt.bytes, &metadata)
84+
require.ErrorIs(err, tt.expectedErr)
85+
if tt.expectedErr != nil {
86+
return
87+
}
88+
require.Equal(tt.expected, &metadata)
89+
})
90+
}
91+
}
92+
93+
func TestWriteDelegatorMetadata(t *testing.T) {
94+
type test struct {
95+
name string
96+
version uint16
97+
metadata *delegatorMetadata
98+
expected []byte
99+
}
100+
tests := []test{
101+
{
102+
name: "v0",
103+
version: v0,
104+
metadata: &delegatorMetadata{
105+
PotentialReward: 123,
106+
StakerStartTime: 456,
107+
},
108+
expected: []byte{
109+
// potential reward
110+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7b,
111+
},
112+
},
113+
{
114+
name: "v1",
115+
version: v1,
116+
metadata: &delegatorMetadata{
117+
PotentialReward: 123,
118+
StakerStartTime: 456,
119+
},
120+
expected: []byte{
121+
// codec version
122+
0x00, 0x01,
123+
// potential reward
124+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7b,
125+
// staker start time
126+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xc8,
127+
},
128+
},
129+
}
130+
for _, tt := range tests {
131+
t.Run(tt.name, func(t *testing.T) {
132+
require := require.New(t)
133+
db := memdb.New()
134+
tt.metadata.txID = ids.GenerateTestID()
135+
require.NoError(writeDelegatorMetadata(db, tt.metadata, tt.version))
136+
bytes, err := db.Get(tt.metadata.txID[:])
137+
require.NoError(err)
138+
require.Equal(tt.expected, bytes)
139+
})
140+
}
141+
}

vms/platformvm/state/metadata_validator.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ type validatorMetadata struct {
3232
LastUpdated uint64 `v0:"true"` // Unix time in seconds
3333
PotentialReward uint64 `v0:"true"`
3434
PotentialDelegateeReward uint64 `v0:"true"`
35+
StakerStartTime uint64 ` v1:"true"`
3536

3637
txID ids.ID
3738
lastUpdated time.Time
@@ -130,6 +131,7 @@ type validatorState interface {
130131
WriteValidatorMetadata(
131132
dbPrimary database.KeyValueWriter,
132133
dbSubnet database.KeyValueWriter,
134+
codecVersion uint16,
133135
) error
134136
}
135137

@@ -230,13 +232,14 @@ func (m *metadata) DeleteValidatorMetadata(vdrID ids.NodeID, subnetID ids.ID) {
230232
func (m *metadata) WriteValidatorMetadata(
231233
dbPrimary database.KeyValueWriter,
232234
dbSubnet database.KeyValueWriter,
235+
codecVersion uint16,
233236
) error {
234237
for vdrID, updatedSubnets := range m.updatedMetadata {
235238
for subnetID := range updatedSubnets {
236239
metadata := m.metadata[vdrID][subnetID]
237240
metadata.LastUpdated = uint64(metadata.lastUpdated.Unix())
238241

239-
metadataBytes, err := metadataCodec.Marshal(v0, metadata)
242+
metadataBytes, err := metadataCodec.Marshal(codecVersion, metadata)
240243
if err != nil {
241244
return err
242245
}

vms/platformvm/state/metadata_validator_test.go

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -81,8 +81,10 @@ func TestWriteValidatorMetadata(t *testing.T) {
8181

8282
primaryDB := memdb.New()
8383
subnetDB := memdb.New()
84+
85+
codecVersion := v1
8486
// write empty uptimes
85-
require.NoError(state.WriteValidatorMetadata(primaryDB, subnetDB))
87+
require.NoError(state.WriteValidatorMetadata(primaryDB, subnetDB, codecVersion))
8688

8789
// load uptime
8890
nodeID := ids.GenerateTestNodeID()
@@ -96,7 +98,7 @@ func TestWriteValidatorMetadata(t *testing.T) {
9698
state.LoadValidatorMetadata(nodeID, subnetID, testUptimeReward)
9799

98100
// write state, should not reflect to DB yet
99-
require.NoError(state.WriteValidatorMetadata(primaryDB, subnetDB))
101+
require.NoError(state.WriteValidatorMetadata(primaryDB, subnetDB, codecVersion))
100102
require.False(primaryDB.Has(testUptimeReward.txID[:]))
101103
require.False(subnetDB.Has(testUptimeReward.txID[:]))
102104

@@ -112,7 +114,7 @@ func TestWriteValidatorMetadata(t *testing.T) {
112114
require.NoError(state.SetUptime(nodeID, subnetID, newUpDuration, newLastUpdated))
113115

114116
// write uptimes, should reflect to subnet DB
115-
require.NoError(state.WriteValidatorMetadata(primaryDB, subnetDB))
117+
require.NoError(state.WriteValidatorMetadata(primaryDB, subnetDB, codecVersion))
116118
require.False(primaryDB.Has(testUptimeReward.txID[:]))
117119
require.True(subnetDB.Has(testUptimeReward.txID[:]))
118120
}
@@ -252,7 +254,7 @@ func TestParseValidatorMetadata(t *testing.T) {
252254
name: "invalid codec version",
253255
bytes: []byte{
254256
// codec version
255-
0x00, 0x01,
257+
0x00, 0x02,
256258
// up duration
257259
0x00, 0x00, 0x00, 0x00, 0x00, 0x5B, 0x8D, 0x80,
258260
// last updated

0 commit comments

Comments
 (0)