Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

revise self stake buckets #4312

Merged
merged 2 commits into from
Jun 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions action/protocol/staking/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,6 @@ type (
Staking genesis.Staking
PersistStakingPatchBlock uint64
StakingPatchDir string
Revise ReviseConfig
}
)
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,10 @@ func initTestStateWithHeight(t *testing.T, ctrl *gomock.Controller, bucketCfgs [
}, &BuilderConfig{
Staking: genesis.Default.Staking,
PersistStakingPatchBlock: math.MaxUint64,
}, nil, nil, nil, genesis.Default.GreenlandBlockHeight)
Revise: ReviseConfig{
VoteWeight: genesis.Default.Staking.VoteWeightCalConsts,
},
}, nil, nil, nil)
require.NoError(err)

// set up bucket
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,10 @@ func TestProtocol_HandleCandidateTransferOwnership(t *testing.T) {
}, &BuilderConfig{
Staking: genesis.Default.Staking,
PersistStakingPatchBlock: math.MaxUint64,
}, nil, nil, nil, genesis.Default.GreenlandBlockHeight)
Revise: ReviseConfig{
VoteWeight: genesis.Default.Staking.VoteWeightCalConsts,
},
}, nil, nil, nil)
require.NoError(err)
initCandidateCfgs := []struct {
Owner address.Address
Expand Down
5 changes: 4 additions & 1 deletion action/protocol/staking/handler_stake_migrate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,11 @@ func TestHandleStakeMigrate(t *testing.T) {
&BuilderConfig{
Staking: genesis.Default.Staking,
PersistStakingPatchBlock: math.MaxUint64,
Revise: ReviseConfig{
VoteWeight: genesis.Default.Staking.VoteWeightCalConsts,
},
},
nil, nil, nil, 0)
nil, nil, nil)
r.NoError(err)
cfg := deepcopy.Copy(genesis.Default).(genesis.Genesis)
initCfg := func(cfg *genesis.Genesis) {
Expand Down
10 changes: 8 additions & 2 deletions action/protocol/staking/handlers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,10 @@ func TestProtocol_HandleCreateStake(t *testing.T) {
}, &BuilderConfig{
Staking: genesis.Default.Staking,
PersistStakingPatchBlock: math.MaxUint64,
}, nil, nil, nil, genesis.Default.GreenlandBlockHeight)
Revise: ReviseConfig{
VoteWeight: genesis.Default.Staking.VoteWeightCalConsts,
},
}, nil, nil, nil)
require.NoError(err)

// set up candidate
Expand Down Expand Up @@ -3221,7 +3224,10 @@ func initAll(t *testing.T, ctrl *gomock.Controller) (protocol.StateManager, *Pro
}, &BuilderConfig{
Staking: genesis.Default.Staking,
PersistStakingPatchBlock: math.MaxUint64,
}, nil, nil, nil, genesis.Default.GreenlandBlockHeight)
Revise: ReviseConfig{
VoteWeight: genesis.Default.Staking.VoteWeightCalConsts,
},
}, nil, nil, nil)
require.NoError(err)

// set up candidate
Expand Down
8 changes: 3 additions & 5 deletions action/protocol/staking/protocol.go
Original file line number Diff line number Diff line change
Expand Up @@ -130,8 +130,6 @@ func NewProtocol(
candBucketsIndexer *CandidatesBucketsIndexer,
contractStakingIndexer ContractStakingIndexerWithBucketType,
contractStakingIndexerV2 ContractStakingIndexer,
correctCandsHeight uint64,
reviseHeights ...uint64,
) (*Protocol, error) {
h := hash.Hash160b([]byte(_protocolID))
addr, err := address.FromBytes(h[:])
Expand All @@ -154,8 +152,8 @@ func NewProtocol(
return nil, action.ErrInvalidAmount
}

// new vote reviser, revise ate greenland
voteReviser := NewVoteReviser(cfg.Staking.VoteWeightCalConsts, correctCandsHeight, reviseHeights...)
// new vote reviser, revise at greenland
voteReviser := NewVoteReviser(cfg.Revise)
migrateContractAddress := ""
if contractStakingIndexerV2 != nil {
migrateContractAddress = contractStakingIndexerV2.ContractAddress()
Expand Down Expand Up @@ -301,7 +299,7 @@ func (p *Protocol) CreatePreStates(ctx context.Context, sm protocol.StateManager
if err != nil {
return err
}
if err := p.voteReviser.Revise(csm, blkCtx.BlockHeight); err != nil {
if err := p.voteReviser.Revise(featureCtx, csm, blkCtx.BlockHeight); err != nil {
return err
}
}
Expand Down
26 changes: 21 additions & 5 deletions action/protocol/staking/protocol_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,10 @@ func TestProtocol(t *testing.T) {
}, &BuilderConfig{
Staking: genesis.Default.Staking,
PersistStakingPatchBlock: math.MaxUint64,
}, nil, nil, nil, genesis.Default.GreenlandBlockHeight)
Revise: ReviseConfig{
VoteWeight: genesis.Default.Staking.VoteWeightCalConsts,
},
}, nil, nil, nil)
r.NotNil(stk)
r.NoError(err)
buckets, _, err := csr.getAllBuckets()
Expand Down Expand Up @@ -208,7 +211,10 @@ func TestCreatePreStates(t *testing.T) {
}, &BuilderConfig{
Staking: genesis.Default.Staking,
PersistStakingPatchBlock: math.MaxUint64,
}, nil, nil, nil, genesis.Default.GreenlandBlockHeight, genesis.Default.GreenlandBlockHeight)
Revise: ReviseConfig{
VoteWeight: genesis.Default.Staking.VoteWeightCalConsts,
ReviseHeights: []uint64{genesis.Default.GreenlandBlockHeight}},
}, nil, nil, nil)
require.NoError(err)
ctx := protocol.WithBlockCtx(
genesis.WithGenesisContext(context.Background(), genesis.Default),
Expand Down Expand Up @@ -274,7 +280,11 @@ func Test_CreatePreStatesWithRegisterProtocol(t *testing.T) {
}, &BuilderConfig{
Staking: genesis.Default.Staking,
PersistStakingPatchBlock: math.MaxUint64,
}, cbi, nil, nil, genesis.Default.GreenlandBlockHeight, genesis.Default.GreenlandBlockHeight)
Revise: ReviseConfig{
VoteWeight: genesis.Default.Staking.VoteWeightCalConsts,
ReviseHeights: []uint64{genesis.Default.GreenlandBlockHeight},
},
}, cbi, nil, nil)
require.NoError(err)

rol := rolldpos.NewProtocol(23, 4, 3)
Expand Down Expand Up @@ -393,7 +403,10 @@ func Test_CreateGenesisStates(t *testing.T) {
}, &BuilderConfig{
Staking: cfg,
PersistStakingPatchBlock: math.MaxUint64,
}, nil, nil, nil, genesis.Default.GreenlandBlockHeight)
Revise: ReviseConfig{
VoteWeight: genesis.Default.Staking.VoteWeightCalConsts,
},
}, nil, nil, nil)
require.NoError(err)

v, err := p.Start(ctx, sm)
Expand Down Expand Up @@ -430,7 +443,10 @@ func TestProtocol_ActiveCandidates(t *testing.T) {
}, &BuilderConfig{
Staking: cfg,
PersistStakingPatchBlock: math.MaxUint64,
}, nil, csIndexer, nil, genesis.Default.GreenlandBlockHeight)
Revise: ReviseConfig{
VoteWeight: genesis.Default.Staking.VoteWeightCalConsts,
},
}, nil, csIndexer, nil)
require.NoError(err)

blkHeight := genesis.Default.QuebecBlockHeight + 1
Expand Down
5 changes: 4 additions & 1 deletion action/protocol/staking/validations_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,10 @@ func initTestProtocol(t *testing.T) (*Protocol, []*Candidate) {
}, &BuilderConfig{
Staking: genesis.Default.Staking,
PersistStakingPatchBlock: math.MaxUint64,
}, nil, nil, nil, genesis.Default.GreenlandBlockHeight)
Revise: ReviseConfig{
VoteWeight: genesis.Default.Staking.VoteWeightCalConsts,
},
}, nil, nil, nil)
require.NoError(err)

var cans []*Candidate
Expand Down
100 changes: 68 additions & 32 deletions action/protocol/staking/vote_reviser.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,43 +2,63 @@ package staking

import (
"math/big"
"slices"
"sort"

"github.com/pkg/errors"
"go.uber.org/zap"

"github.com/iotexproject/iotex-core/action/protocol"
"github.com/iotexproject/iotex-core/blockchain/genesis"
"github.com/iotexproject/iotex-core/pkg/log"
"github.com/iotexproject/iotex-core/state"
)

// VoteReviser is used to recalculate candidate votes.
type VoteReviser struct {
reviseHeights []uint64
cache map[uint64]CandidateList
c genesis.VoteWeightCalConsts
correctCandsHeight uint64
}
type (
VoteReviser struct {
cache map[uint64]CandidateList
cfg ReviseConfig
}

ReviseConfig struct {
VoteWeight genesis.VoteWeightCalConsts
ReviseHeights []uint64
CorrectCandsHeight uint64
SelfStakeBucketReviseHeight uint64
}
)

// NewVoteReviser creates a VoteReviser.
func NewVoteReviser(c genesis.VoteWeightCalConsts, correctCandsHeight uint64, reviseHeights ...uint64) *VoteReviser {
func NewVoteReviser(cfg ReviseConfig) *VoteReviser {
// TODO: return error if cfg.CorrectSelfStakeBucketHeights is before hardfork height
return &VoteReviser{
reviseHeights: reviseHeights,
cache: make(map[uint64]CandidateList),
c: c,
correctCandsHeight: correctCandsHeight,
cfg: cfg,
cache: make(map[uint64]CandidateList),
}
}

// Revise recalculate candidate votes on preset revising height.
func (vr *VoteReviser) Revise(csm CandidateStateManager, height uint64) error {
func (vr *VoteReviser) Revise(ctx protocol.FeatureCtx, csm CandidateStateManager, height uint64) error {
if !vr.isCacheExist(height) {
cands, err := vr.calculateVoteWeight(csm)
cands, _, err := newCandidateStateReader(csm.SM()).getAllCandidates()
switch {
case errors.Cause(err) == state.ErrStateNotExist:
case err != nil:
return err
}
if vr.shouldReviseSelfStakeBuckets(height) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it is actually revise for endorsement status, i think name it reviseEndorsement or at least put endorsement into the func name

cands, err = vr.reviseSelfStakeBuckets(ctx, csm, height, cands)
if err != nil {
return err
}
}
cands, err = vr.calculateVoteWeight(csm, height, cands)
if err != nil {
return err
}
sort.Sort(cands)
if vr.needCorrectCands(height) {
if vr.shouldReviseAlias(height) {
cands, err = vr.correctAliasCands(csm, cands)
if err != nil {
return err
Expand Down Expand Up @@ -73,6 +93,28 @@ func (vr *VoteReviser) correctAliasCands(csm CandidateStateManager, cands Candid
return retval, nil
}

func (vr *VoteReviser) reviseSelfStakeBuckets(ctx protocol.FeatureCtx, csm CandidateStateManager, height uint64, cands CandidateList) (CandidateList, error) {
// revise endorsements
esm := NewEndorsementStateManager(csm.SM())
for _, cand := range cands {
endorsement, err := esm.Get(cand.SelfStakeBucketIdx)
switch errors.Cause(err) {
case state.ErrStateNotExist:
continue
case nil:
if endorsement.LegacyStatus(height) == EndorseExpired {
if err := esm.Delete(cand.SelfStakeBucketIdx); err != nil {
return nil, errors.Wrapf(err, "failed to delete endorsement with bucket index %d", cand.SelfStakeBucketIdx)
}
cand.SelfStakeBucketIdx = candidateNoSelfStakeBucketIndex
}
envestcc marked this conversation as resolved.
Show resolved Hide resolved
default:
return nil, errors.Wrapf(err, "failed to get endorsement with bucket index %d", cand.SelfStakeBucketIdx)
}
}
return cands, nil
}

func (vr *VoteReviser) result(height uint64) (CandidateList, bool) {
cands, ok := vr.cache[height]
if !ok {
Expand All @@ -92,27 +134,22 @@ func (vr *VoteReviser) isCacheExist(height uint64) bool {

// NeedRevise returns true if height needs revise
func (vr *VoteReviser) NeedRevise(height uint64) bool {
for _, h := range vr.reviseHeights {
if height == h {
return true
}
}
return vr.needCorrectCands(height)
return slices.Contains(vr.cfg.ReviseHeights, height) ||
vr.shouldReviseSelfStakeBuckets(height) ||
vr.shouldReviseAlias(height)
}

// NeedCorrectCands returns true if height needs to correct candidates
func (vr *VoteReviser) needCorrectCands(height uint64) bool {
return height == vr.correctCandsHeight
func (vr *VoteReviser) shouldReviseAlias(height uint64) bool {
return height == vr.cfg.CorrectCandsHeight
}

func (vr *VoteReviser) shouldReviseSelfStakeBuckets(height uint64) bool {
return vr.cfg.SelfStakeBucketReviseHeight == height
}

func (vr *VoteReviser) calculateVoteWeight(csm CandidateStateManager) (CandidateList, error) {
func (vr *VoteReviser) calculateVoteWeight(csm CandidateStateManager, height uint64, cands CandidateList) (CandidateList, error) {
csr := newCandidateStateReader(csm.SM())
cands, _, err := csr.getAllCandidates()
switch {
case errors.Cause(err) == state.ErrStateNotExist:
case err != nil:
return nil, err
}
candm := make(map[string]*Candidate)
for _, cand := range cands {
candm[cand.GetIdentifier().String()] = cand.Clone()
Expand All @@ -135,9 +172,8 @@ func (vr *VoteReviser) calculateVoteWeight(csm CandidateStateManager) (Candidate
log.L().Error("invalid bucket candidate", zap.Uint64("bucket index", bucket.Index), zap.String("candidate", bucket.Candidate.String()))
continue
}

if cand.SelfStakeBucketIdx == bucket.Index {
if err = cand.AddVote(CalculateVoteWeight(vr.c, bucket, true)); err != nil {
if err = cand.AddVote(CalculateVoteWeight(vr.cfg.VoteWeight, bucket, true)); err != nil {
log.L().Error("failed to add vote for candidate",
zap.Uint64("bucket index", bucket.Index),
zap.String("candidate", bucket.Candidate.String()),
Expand All @@ -146,7 +182,7 @@ func (vr *VoteReviser) calculateVoteWeight(csm CandidateStateManager) (Candidate
}
cand.SelfStake = bucket.StakedAmount
} else {
_ = cand.AddVote(CalculateVoteWeight(vr.c, bucket, false))
_ = cand.AddVote(CalculateVoteWeight(vr.cfg.VoteWeight, bucket, false))
}
}

Expand Down
Loading
Loading