-
Notifications
You must be signed in to change notification settings - Fork 328
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
[staking] fix weighted votes of contract staking bucket #3936
Conversation
Codecov Report
@@ Coverage Diff @@
## master #3936 +/- ##
==========================================
+ Coverage 75.38% 76.09% +0.71%
==========================================
Files 303 328 +25
Lines 25923 27959 +2036
==========================================
+ Hits 19541 21275 +1734
- Misses 5360 5591 +231
- Partials 1022 1093 +71
... and 5 files with indirect coverage changes 📣 We’re building smart automated test selection to slash your CI/CD build times. Learn more |
remainingTime := v.StakedDuration.Seconds() | ||
if !v.isNative() { | ||
// according to produce one block per 5 seconds | ||
remainingTime = float64(v.StakedDurationBlockNumber) * 5 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
don't hard code constant
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes, by maintain the StakedDuration
field also for the nft bucket, we can remove the additional case here.
} | ||
) | ||
|
||
// NewContractStakingIndexer creates a new contract staking indexer | ||
func NewContractStakingIndexer(kvStore db.KVStore, contractAddr string, contractDeployHeight uint64) (*Indexer, error) { | ||
func NewContractStakingIndexer(kvStore db.KVStore, contractAddr string, contractDeployHeight uint64, voteConsts genesis.VoteWeightCalConsts) (*Indexer, error) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
pass in the calc function instead
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok
remainingTime := v.StakedDuration.Seconds() | ||
if !v.isNative() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
for NFT bucket, does it have v.StakedDuation
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This time, the StakedDuration
field is also set for the NFT bucket, based on the StakedDurationBlockNumber* blockInterval
.
blockindex/contractstaking/cache.go
Outdated
} | ||
|
||
calculateVoteWeightFunc func(v *Bucket, selfStake bool) *big.Int |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
since for NFT buckets, the func is always called withselfStake = false
, can remove it from the input param?
calculateVoteWeightFunc func(v *Bucket) *big.Int
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes, it's more concise
chainservice/builder.go
Outdated
indexer, err := contractstaking.NewContractStakingIndexer(db.NewBoltDB(dbConfig), builder.cfg.Genesis.SystemStakingContractAddress, builder.cfg.Genesis.SystemStakingContractHeight) | ||
voteCalcConsts := builder.cfg.Genesis.VoteWeightCalConsts | ||
indexer, err := contractstaking.NewContractStakingIndexer(db.NewBoltDB(dbConfig), builder.cfg.Genesis.SystemStakingContractAddress, builder.cfg.Genesis.SystemStakingContractHeight, func(v *staking.VoteBucket, selfStake bool) *big.Int { | ||
return staking.CalculateVoteWeight(voteCalcConsts, v, selfStake) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
and here
return staking.CalculateVoteWeight(voteCalcConsts, v, false)
@@ -223,7 +228,7 @@ func calculateVoteWeight(c genesis.VoteWeightCalConsts, v *VoteBucket, selfStake | |||
if remainingTime > 0 { | |||
weight += math.Log(math.Ceil(remainingTime/86400)*(1+m)) / math.Log(c.DurationLg) / 100 | |||
} | |||
if selfStake && v.AutoStake && v.StakedDuration >= time.Duration(91)*24*time.Hour { | |||
if v.isNative() && selfStake && v.AutoStake && v.StakedDuration >= time.Duration(91)*24*time.Hour { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
confused by the condition v.isNative() && selfStake
. Does it mean contract can do self staking (isnative: false & selfstake: true) ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here, we just want to ensure that the nft bucket does not calculate selfStake weighting. However, this can also be achieved by ensuring that selfStake=false
is passed in when bucket is nft. We can revert it.
@@ -137,7 +137,7 @@ func (vr *VoteReviser) calculateVoteWeight(csm CandidateStateManager) (Candidate | |||
} | |||
|
|||
if cand.SelfStakeBucketIdx == bucket.Index { | |||
if err = cand.AddVote(calculateVoteWeight(vr.c, bucket, true)); err != nil { | |||
if err = cand.AddVote(CalculateVoteWeight(vr.c, bucket, true)); err != nil { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will the returned result on L108 be impacted due to the behavior change of CalculateVoteWeight
@@ -74,7 +77,11 @@ func (s *contractStakingCache) CandidateVotes(candidate address.Address, height | |||
continue | |||
} | |||
bt := s.mustGetBucketType(bi.TypeIndex) | |||
votes.Add(votes, bt.Amount) | |||
if featureCtx.FixContractStakingWeightedVotes { | |||
votes.Add(votes, s.config.CalculateVoteWeight(assembleBucket(id, bi, bt, s.config.ContractAddress, s.config.BlockInterval))) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
throw error if CalculateVoteWeight
is nullptr when Config is empty
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok
"github.com/iotexproject/iotex-core/action/protocol/staking" | ||
) | ||
|
||
// Bucket defines the bucket struct for contract staking | ||
type Bucket = staking.VoteBucket | ||
|
||
func assembleBucket(token uint64, bi *bucketInfo, bt *BucketType, contractAddr string) *Bucket { | ||
func assembleBucket(token uint64, bi *bucketInfo, bt *BucketType, contractAddr string, blockInterval time.Duration) *Bucket { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what are the values of CreateTime
, StakeStartTime
, and UnstakeStartTime
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
they are only for native bucket and keep zero-value for nft bucket. The nft bucket use CreateBlockHeight
, StakeStartBlockHeight
, UnstakeStartBlockHeight
instead.
@@ -1868,6 +1876,18 @@ func TestContractStaking(t *testing.T) { | |||
}) | |||
}) | |||
|
|||
t.Run("afterRedsea", func(t *testing.T) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
add tests on following heights, like _testRedseaBlockHeight + 10/ + 100, to indicates the change of weighted votes
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the votes will not change over time unless unstake or restake.
ctx := protocol.WithFeatureCtx(protocol.WithBlockCtx(ctx, protocol.BlockCtx{BlockHeight: height})) | ||
votes, err := indexer.CandidateVotes(ctx, identityset.Address(7), height) | ||
r.NoError(err) | ||
r.EqualValues(12245, votes.Int64()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@dustinxie is the value 12245
expected?
blockindex/contractstaking/cache.go
Outdated
@@ -74,7 +81,11 @@ func (s *contractStakingCache) CandidateVotes(candidate address.Address, height | |||
continue | |||
} | |||
bt := s.mustGetBucketType(bi.TypeIndex) | |||
votes.Add(votes, bt.Amount) | |||
if featureCtx.FixContractStakingWeightedVotes { | |||
votes.Add(votes, s.calculateVoteWeight(assembleBucket(id, bi, bt, s.contractAddress), false)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
selfStake should check bucket ownerAddress == delegate,can not be use false here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
selfStake is only for candidate register, not enable to nft bucket
vb := Bucket{ | ||
Index: token, | ||
StakedAmount: bt.Amount, | ||
StakedDuration: time.Duration(bt.Duration) * blockInterval, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
pls make sure all NFT buckets are processed by this assembleBucket
before being passed to CalculateVoteWeight
otherwise v.StakedDuration
is not correct
blockindex/contractstaking/cache.go
Outdated
@@ -78,6 +78,9 @@ func (s *contractStakingCache) CandidateVotes(ctx context.Context, candidate add | |||
} | |||
bt := s.mustGetBucketType(bi.TypeIndex) | |||
if featureCtx.FixContractStakingWeightedVotes { | |||
if s.config.CalculateVoteWeight == nil { | |||
return nil, errors.New("calculate vote weight function is not set") | |||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if we add this check, it should be added at creation time, when the func is passed to Newxxx
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok
SonarCloud Quality Gate failed. 0 Bugs No Coverage information Catch issues before they fail your Quality Gate with our IDE extension SonarLint |
Description
Currently, the IIP-13
contract staking bucket
does not take into account the impact of thestaking duration
andstake-lock
when calculating the voting power. It only considers the amount of IOTX staked. However, it should actually use the similar voting logic as thenative staking bucket
.It's a hard-fork at
RedseaBlockHeight
.Type of change
Please delete options that are not relevant.
How Has This Been Tested?
Please describe the tests that you ran to verify your changes. Provide instructions so we can reproduce. Please also list any relevant details for your test configuration
Test Configuration:
Checklist: