diff --git a/common/config/config.go b/common/config/config.go index a3f545051..a34b9d481 100644 --- a/common/config/config.go +++ b/common/config/config.go @@ -194,6 +194,7 @@ func GetDefaultParams() *Configuration { CandidatesCount: 72, DPoSV2RewardAccumulateProgramHash: StakeRewardProgramHash, NFTStartHeight: 1405000, + DPoSV2SortVotesHeight: 1415500 + 720*15, OriginArbiters: []string{ "0248df6705a909432be041e0baa25b8f648741018f70d1911f2ed28778db4b8fe4", "02771faf0f4d4235744b30972d5f2c470993920846c761e4d08889ecfdc061cddf", @@ -720,6 +721,8 @@ type DPoSConfiguration struct { CRDPoSNodeHotFixHeight uint32 `screw:"--crdposnodehotfixheight" usage:"CRDPoSNodeHotFixHeight indicates the hot fix start height of CR DPoS node"` // NFTStartHeight defines the height of NFT started. NFTStartHeight uint32 `screw:"--nftstartheight" usage:"the start height of NFT transaction"` + // DPoSV2SortVotesHeight + DPoSV2SortVotesHeight uint32 `screw:"--dposv2sortvotesheight" usage:"defines the start height to sort DPoSV2 votes"` } type CRConfiguration struct { diff --git a/common/fixed64.go b/common/fixed64.go index 32dac7864..35076ec46 100644 --- a/common/fixed64.go +++ b/common/fixed64.go @@ -16,6 +16,8 @@ import ( //the 64 bit fixed-point number, precise 10^-8 type Fixed64 int64 +const ZeroFixed64 = Fixed64(0) + func (f *Fixed64) Serialize(w io.Writer) error { return WriteElement(w, f) } diff --git a/core/transaction/dposv2claimrewardtransaction.go b/core/transaction/dposv2claimrewardtransaction.go index dde5c0547..cc546b97d 100644 --- a/core/transaction/dposv2claimrewardtransaction.go +++ b/core/transaction/dposv2claimrewardtransaction.go @@ -20,6 +20,8 @@ import ( "github.com/elastos/Elastos.ELA/vm" ) +const RewardTolerance = 10 + type DPoSV2ClaimRewardTransaction struct { BaseTransaction } @@ -103,8 +105,16 @@ func (t *DPoSV2ClaimRewardTransaction) SpecialContextCheck() (elaerr.ELAError, b return elaerr.Simple(elaerr.ErrTxPayload, errors.New("no reward to claim for such address")), true } - if claimAmount < claimReward.Value { - return elaerr.Simple(elaerr.ErrTxPayload, errors.New("claim reward exceeded , max claim reward "+claimAmount.String())), true + if t.parameters.BlockHeight < t.parameters.Config.DPoSConfiguration.DPoSV2SortVotesHeight { + if claimAmount+RewardTolerance*t.parameters.Config.MinCrossChainTxFee < claimReward.Value { + return elaerr.Simple(elaerr.ErrTxPayload, errors.New("claim reward exceeded , max claim reward "+ + claimAmount.String()+" current:"+claimReward.Value.String())), true + } + } else { + if claimAmount < claimReward.Value { + return elaerr.Simple(elaerr.ErrTxPayload, errors.New("claim reward exceeded , max claim reward "+ + claimAmount.String()+" current:"+claimReward.Value.String())), true + } } if claimReward.Value <= t.parameters.Config.CRConfiguration.RealWithdrawSingleFee { diff --git a/dpos/state/arbitrators.go b/dpos/state/arbitrators.go index cae18b880..06a8f7821 100644 --- a/dpos/state/arbitrators.go +++ b/dpos/state/arbitrators.go @@ -734,10 +734,22 @@ func (a *Arbiters) getDPoSV2RewardsV2(dposReward common.Fixed64, sponsor []byte, if len(sVoteDetail) == 0 { continue } + type sVoteDetailData struct { + Addr common.Uint256 + VoteInfo payload.DetailedVoteInfo + } + svdd := make([]sVoteDetailData, 0) + for referKey, votes := range sVoteDetail { + svdd = append(svdd, sVoteDetailData{referKey, votes}) + sort.Slice(svdd, func(i, j int) bool { + return svdd[i].Addr.Compare(svdd[j].Addr) < 0 + }) + } + var totalN float64 - for _, votes := range sVoteDetail { - weightF := math.Log10(float64(votes.Info[0].LockTime-votes.BlockHeight) / 7200 * 10) - N := common.Fixed64(float64(votes.Info[0].Votes) * weightF) + for _, votes := range svdd { + weightF := math.Log10(float64(votes.VoteInfo.Info[0].LockTime-votes.VoteInfo.BlockHeight) / 7200 * 10) + N := common.Fixed64(float64(votes.VoteInfo.Info[0].Votes) * weightF) totalN += float64(N) } if totalN == 0 { @@ -747,9 +759,21 @@ func (a *Arbiters) getDPoSV2RewardsV2(dposReward common.Fixed64, sponsor []byte, totalNI += totalN } - for sVoteAddr, N := range producersN { - addr, _ := sVoteAddr.ToAddress() - p := N / totalNI * float64(votesReward) + type kvData struct { + Addr common.Uint168 + N float64 + } + sortedProducersN := make([]kvData, 0) + for k, v := range producersN { + sortedProducersN = append(sortedProducersN, kvData{k, v}) + } + sort.Slice(sortedProducersN, func(i, j int) bool { + return sortedProducersN[i].Addr.Compare(sortedProducersN[j].Addr) < 0 + }) + + for _, producerN := range sortedProducersN { + addr, _ := producerN.Addr.ToAddress() + p := producerN.N / totalNI * float64(votesReward) rewards[addr] += common.Fixed64(p) log.Debugf("getDPoSV2Rewards addr:%s, p:%s, reward:%s \n", addr, common.Fixed64(p), rewards[addr]) } diff --git a/dpos/state/keyframe.go b/dpos/state/keyframe.go index eda9cdd89..6c46404eb 100644 --- a/dpos/state/keyframe.go +++ b/dpos/state/keyframe.go @@ -133,6 +133,8 @@ func (s *StateKeyFrame) snapshot() *StateKeyFrame { SpecialTxHashes: make(map[common.Uint256]struct{}), PreBlockArbiters: make(map[string]struct{}), ProducerDepositMap: make(map[common.Uint168]struct{}), + + EmergencyInactiveArbiters: make(map[string]struct{}), } state.NodeOwnerKeys = copyStringMap(s.NodeOwnerKeys) state.CurrentCRNodeOwnerKeys = copyStringMap(s.CurrentCRNodeOwnerKeys) @@ -166,7 +168,21 @@ func (s *StateKeyFrame) snapshot() *StateKeyFrame { state.PreBlockArbiters = copyStringSet(s.PreBlockArbiters) state.ProducerDepositMap = copyDIDSet(s.ProducerDepositMap) - //todo add DPOSStartHeight and so on + state.EmergencyInactiveArbiters = copyStringSet(s.EmergencyInactiveArbiters) + state.LastRandomCandidateOwner = s.LastRandomCandidateOwner + state.VersionStartHeight = s.VersionStartHeight + state.VersionEndHeight = s.VersionEndHeight + state.LastRandomCandidateHeight = s.LastRandomCandidateHeight + state.DPOSWorkHeight = s.DPOSWorkHeight + state.ConsensusAlgorithm = s.ConsensusAlgorithm + state.LastBlockTimestamp = s.LastBlockTimestamp + state.NeedRevertToDPOSTX = s.NeedRevertToDPOSTX + state.NeedNextTurnDPOSInfo = s.NeedNextTurnDPOSInfo + state.NoProducers = s.NoProducers + state.NoClaimDPOSNode = s.NoClaimDPOSNode + state.RevertToPOWBlockHeight = s.RevertToPOWBlockHeight + state.LastIrreversibleHeight = s.LastIrreversibleHeight + state.DPOSStartHeight = s.DPOSStartHeight state.DPoSV2ActiveHeight = s.DPoSV2ActiveHeight return &state diff --git a/dpos/state/state.go b/dpos/state/state.go index fe777d349..b1d3c30f7 100644 --- a/dpos/state/state.go +++ b/dpos/state/state.go @@ -234,11 +234,24 @@ func (p *Producer) GetExpiredNFTVotes() map[common.Uint168]payload.DetailedVoteI return p.expiredNFTVotes } +type sVoteDetailData struct { + Addr common.Uint256 + VoteInfo payload.DetailedVoteInfo +} + func (p *Producer) GetTotalDPoSV2VoteRights() float64 { var result float64 for _, sVoteDetail := range p.detailedDPoSV2Votes { + svdd := make([]sVoteDetailData, 0) + for referKey, votes := range sVoteDetail { + svdd = append(svdd, sVoteDetailData{referKey, votes}) + sort.Slice(svdd, func(i, j int) bool { + return svdd[i].Addr.Compare(svdd[j].Addr) < 0 + }) + } var totalN float64 - for _, votes := range sVoteDetail { + for _, v := range svdd { + votes := v.VoteInfo weightF := math.Log10(float64(votes.Info[0].LockTime-votes.BlockHeight) / 7200 * 10) N := common.Fixed64(float64(votes.Info[0].Votes) * weightF) totalN += float64(N) @@ -251,7 +264,20 @@ func (p *Producer) GetTotalDPoSV2VoteRights() float64 { func (p *Producer) GetNFTVotesRight(targetReferKey common.Uint256) float64 { for _, sVoteDetail := range p.detailedDPoSV2Votes { + type sVoteDetailData struct { + Addr common.Uint256 + VoteInfo payload.DetailedVoteInfo + } + svdd := make([]sVoteDetailData, 0) for referKey, votes := range sVoteDetail { + svdd = append(svdd, sVoteDetailData{referKey, votes}) + sort.Slice(svdd, func(i, j int) bool { + return svdd[i].Addr.Compare(svdd[j].Addr) < 0 + }) + } + for _, v := range svdd { + referKey := v.Addr + votes := v.VoteInfo if referKey.IsEqual(targetReferKey) { weightF := math.Log10(float64(votes.Info[0].LockTime-votes.BlockHeight) / 7200 * 10) N := common.Fixed64(float64(votes.Info[0].Votes) * weightF) diff --git a/servers/interfaces.go b/servers/interfaces.go index 4f3354f3d..450f0b408 100644 --- a/servers/interfaces.go +++ b/servers/interfaces.go @@ -2341,6 +2341,9 @@ func DposV2RewardInfo(param Params) map[string]interface{} { claimable := Chain.GetState().DPoSV2RewardInfo[stakeAddress] claiming := Chain.GetState().DposV2RewardClaimingInfo[stakeAddress] claimed := Chain.GetState().DposV2RewardClaimedInfo[stakeAddress] + if claimable < common.ZeroFixed64 { + claimable = common.ZeroFixed64 + } result := RPCDposV2RewardInfo{ Address: addr, Claimable: claimable.String(),