From 702e2b7cb4db42f7dbb004a6cb4b224450b3dcd6 Mon Sep 17 00:00:00 2001 From: envestcc Date: Thu, 25 Jul 2024 16:46:34 +0800 Subject: [PATCH] use id instead of owner when vote counting --- .../protocol/staking/staking_statereader.go | 2 +- e2etest/e2etest.go | 40 ++++++++ e2etest/native_staking_test.go | 94 +++++++++++++++++++ 3 files changed, 135 insertions(+), 1 deletion(-) diff --git a/action/protocol/staking/staking_statereader.go b/action/protocol/staking/staking_statereader.go index 418c5e902c..4f5f86aacf 100644 --- a/action/protocol/staking/staking_statereader.go +++ b/action/protocol/staking/staking_statereader.go @@ -359,7 +359,7 @@ func (c *compositeStakingStateReader) addContractStakingVotes(ctx context.Contex if !ok { return errors.Errorf("invalid total weighted votes %s", candidate.TotalWeightedVotes) } - addr, err := address.FromString(candidate.OwnerAddress) + addr, err := address.FromString(candidate.GetId()) if err != nil { return err } diff --git a/e2etest/e2etest.go b/e2etest/e2etest.go index 7bbc8df4dd..db6fc68f68 100644 --- a/e2etest/e2etest.go +++ b/e2etest/e2etest.go @@ -3,6 +3,7 @@ package e2etest import ( "context" "fmt" + "slices" "testing" "time" @@ -158,6 +159,45 @@ func (e *e2etest) getCandidateByName(name string) (*iotextypes.CandidateV2, erro return candidate, nil } +func (e *e2etest) getBucket(index uint64, contractAddr string) (*iotextypes.VoteBucket, error) { + methodName, err := proto.Marshal(&iotexapi.ReadStakingDataMethod{ + Method: iotexapi.ReadStakingDataMethod_COMPOSITE_BUCKETS_BY_INDEXES, + }) + if err != nil { + return nil, err + } + arg, err := proto.Marshal(&iotexapi.ReadStakingDataRequest{ + Request: &iotexapi.ReadStakingDataRequest_BucketsByIndexes{ + BucketsByIndexes: &iotexapi.ReadStakingDataRequest_VoteBucketsByIndexes{ + Index: []uint64{index}, + }, + }, + }) + if err != nil { + return nil, err + } + resp, err := e.api.ReadState(context.Background(), &iotexapi.ReadStateRequest{ + ProtocolID: []byte("staking"), + MethodName: methodName, + Arguments: [][]byte{arg}, + }) + if err != nil { + return nil, err + } + bucketList := &iotextypes.VoteBucketList{} + if err = proto.Unmarshal(resp.GetData(), bucketList); err != nil { + return nil, err + } + idx := slices.IndexFunc(bucketList.Buckets, func(e *iotextypes.VoteBucket) bool { + return e.Index == index && e.ContractAddress == contractAddr + }) + if idx < 0 { + return nil, nil + } + return bucketList.Buckets[idx], nil + +} + func addOneTx(ctx context.Context, ap actpool.ActPool, bc blockchain.Blockchain, tx *actionWithTime) (*action.SealedEnvelope, *action.Receipt, error) { if err := ap.Add(ctx, tx.act); err != nil { return tx.act, nil, err diff --git a/e2etest/native_staking_test.go b/e2etest/native_staking_test.go index 350e4044de..a095be789d 100644 --- a/e2etest/native_staking_test.go +++ b/e2etest/native_staking_test.go @@ -1239,6 +1239,100 @@ func TestCandidateTransferOwnership(t *testing.T) { }, }) }) + t.Run("votesCounting", func(t *testing.T) { + contractAddr := "io16gnlvx6zk3tev9g6vaupngkpcrwe8hdsknxerw" + cfg := initCfg(require) + cfg.Genesis.UpernavikBlockHeight = 1 + cfg.Genesis.EndorsementWithdrawWaitingBlocks = 5 + cfg.DardanellesUpgrade.BlockInterval = time.Second * 8640 + cfg.Genesis.SystemStakingContractV2Address = contractAddr + cfg.Genesis.SystemStakingContractV2Height = 0 + test := newE2ETest(t, cfg) + defer test.teardown() + + var ( + oldOwnerID = 1 + stakerID = 2 + contractCreator = 3 + newOwnerID = 4 + beneficiaryID = 5 + chainID = test.cfg.Chain.ID + stakeTime = time.Now() + minAmount = unit.ConvertIotxToRau(1000) + stakeAmount = unit.ConvertIotxToRau(10000) + blocksPerDay = 24 * time.Hour / cfg.DardanellesUpgrade.BlockInterval + stakeDurationBlocks = big.NewInt(int64(blocksPerDay)) + candidate *iotextypes.CandidateV2 + ) + bytecode, err := hex.DecodeString(stakingContractV2Bytecode) + require.NoError(err) + mustCallData := func(m string, args ...any) []byte { + data, err := abiCall(staking.StakingContractABI, m, args...) + require.NoError(err) + return data + } + test.run([]*testcase{ + { + name: "prepare", + preActs: []*actionWithTime{ + {mustNoErr(action.SignedCandidateRegister(test.nonceMgr.pop(identityset.Address(oldOwnerID).String()), "cand1", identityset.Address(1).String(), identityset.Address(1).String(), identityset.Address(oldOwnerID).String(), registerAmount.String(), 1, true, nil, gasLimit, gasPrice, identityset.PrivateKey(oldOwnerID), action.WithChainID(chainID))), time.Now()}, + }, + act: &actionWithTime{mustNoErr(action.SignedExecution("", identityset.PrivateKey(contractCreator), test.nonceMgr.pop(identityset.Address(contractCreator).String()), big.NewInt(0), gasLimit, gasPrice, append(bytecode, mustCallData("", minAmount)...), action.WithChainID(chainID))), time.Now()}, + expect: []actionExpect{successExpect, &executionExpect{contractAddr}}, + }, + { + name: "stakeBuckets", + preActs: []*actionWithTime{ + {mustNoErr(action.SignedExecution(contractAddr, identityset.PrivateKey(contractCreator), test.nonceMgr.pop(identityset.Address(contractCreator).String()), big.NewInt(0), gasLimit, gasPrice, mustCallData("setBeneficiary(address)", common.BytesToAddress(identityset.Address(beneficiaryID).Bytes())), action.WithChainID(chainID))), stakeTime}, + {mustNoErr(action.SignedCreateStake(test.nonceMgr.pop(identityset.Address(stakerID).String()), "cand1", stakeAmount.String(), 91, true, nil, gasLimit, gasPrice, identityset.PrivateKey(stakerID), action.WithChainID(test.cfg.Chain.ID))), stakeTime}, + }, + act: &actionWithTime{mustNoErr(action.SignedExecution(contractAddr, identityset.PrivateKey(stakerID), test.nonceMgr.pop(identityset.Address(stakerID).String()), stakeAmount, gasLimit, gasPrice, mustCallData("stake(uint256,address)", stakeDurationBlocks, common.BytesToAddress(identityset.Address(oldOwnerID).Bytes())), action.WithChainID(chainID))), stakeTime}, + expect: []actionExpect{successExpect, &functionExpect{func(test *e2etest, act *action.SealedEnvelope, receipt *action.Receipt, err error) { + cand, err := test.getCandidateByName("cand1") + require.NoError(err) + candidate = cand + selfStake, err := test.getBucket(cand.SelfStakeBucketIdx, "") + require.NoError(err) + require.NotNil(selfStake) + nb, err := test.getBucket(1, "") + require.NoError(err) + require.NotNil(nb) + nft, err := test.getBucket(1, contractAddr) + require.NoError(err) + require.NotNil(nft) + amtSS, ok := big.NewInt(0).SetString(selfStake.StakedAmount, 10) + require.True(ok) + amtNB, ok := big.NewInt(0).SetString(nb.StakedAmount, 10) + require.True(ok) + amtNFT, ok := big.NewInt(0).SetString(nft.StakedAmount, 10) + require.True(ok) + voteSS := staking.CalculateVoteWeight(test.cfg.Genesis.VoteWeightCalConsts, &staking.VoteBucket{StakedAmount: amtSS, StakedDuration: time.Duration(selfStake.StakedDuration*24) * time.Hour, AutoStake: selfStake.AutoStake}, true) + voteNB := staking.CalculateVoteWeight(test.cfg.Genesis.VoteWeightCalConsts, &staking.VoteBucket{StakedAmount: amtNB, StakedDuration: time.Duration(nb.StakedDuration*24) * time.Hour, AutoStake: nb.AutoStake}, false) + voteNFT := staking.CalculateVoteWeight(test.cfg.Genesis.VoteWeightCalConsts, &staking.VoteBucket{StakedAmount: amtNFT, StakedDuration: time.Duration(nft.StakedDuration*24) * time.Hour, AutoStake: nft.AutoStake}, false) + require.Equal(voteSS.Add(voteSS, voteNB.Add(voteNB, voteNFT)).String(), cand.TotalWeightedVotes) + }}}, + }, + { + name: "transferOwnership", + act: &actionWithTime{mustNoErr(action.SignedCandidateTransferOwnership(test.nonceMgr.pop(identityset.Address(oldOwnerID).String()), identityset.Address(newOwnerID).String(), nil, gasLimit, gasPrice, identityset.PrivateKey(oldOwnerID), action.WithChainID(chainID))), time.Now()}, + expect: []actionExpect{successExpect, &functionExpect{func(test *e2etest, act *action.SealedEnvelope, receipt *action.Receipt, err error) { + cand, err := test.getCandidateByName(candidate.Name) + require.NoError(err) + selfStakeBucket, err := test.getBucket(candidate.SelfStakeBucketIdx, "") + require.NoError(err) + require.NotNil(selfStakeBucket) + amtSS, ok := big.NewInt(0).SetString(selfStakeBucket.StakedAmount, 10) + require.True(ok) + selfStakeVotes := staking.CalculateVoteWeight(test.cfg.Genesis.VoteWeightCalConsts, &staking.VoteBucket{StakedAmount: amtSS, StakedDuration: time.Duration(selfStakeBucket.StakedDuration*24) * time.Hour, AutoStake: selfStakeBucket.AutoStake}, true) + nonSelfStakeVotes := staking.CalculateVoteWeight(test.cfg.Genesis.VoteWeightCalConsts, &staking.VoteBucket{StakedAmount: amtSS, StakedDuration: time.Duration(selfStakeBucket.StakedDuration*24) * time.Hour, AutoStake: selfStakeBucket.AutoStake}, false) + deltaVotes := big.NewInt(0).Sub(selfStakeVotes, nonSelfStakeVotes) + votes, ok := big.NewInt(0).SetString(cand.TotalWeightedVotes, 10) + require.True(ok) + require.Equal(votes.Sub(votes, deltaVotes).String(), candidate.TotalWeightedVotes) + }}}, + }, + }) + }) } func initCfg(r *require.Assertions) config.Config {