github.com/iotexproject/iotex-core@v1.14.1-rc1/e2etest/rewarding_test.go (about)

     1  package e2etest
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"math/big"
     7  	"math/rand"
     8  	"os"
     9  	"strconv"
    10  	"sync"
    11  	"testing"
    12  	"time"
    13  
    14  	"github.com/cenkalti/backoff"
    15  	"github.com/stretchr/testify/assert"
    16  	"github.com/stretchr/testify/require"
    17  	"go.uber.org/zap"
    18  	"google.golang.org/grpc"
    19  
    20  	"github.com/iotexproject/go-pkgs/crypto"
    21  	"github.com/iotexproject/go-pkgs/hash"
    22  	"github.com/iotexproject/iotex-proto/golang/iotexapi"
    23  	"github.com/iotexproject/iotex-proto/golang/iotextypes"
    24  
    25  	"github.com/iotexproject/iotex-core/action"
    26  	"github.com/iotexproject/iotex-core/action/protocol"
    27  	accountutil "github.com/iotexproject/iotex-core/action/protocol/account/util"
    28  	"github.com/iotexproject/iotex-core/action/protocol/rewarding"
    29  	"github.com/iotexproject/iotex-core/api"
    30  	"github.com/iotexproject/iotex-core/blockchain"
    31  	"github.com/iotexproject/iotex-core/blockchain/genesis"
    32  	"github.com/iotexproject/iotex-core/config"
    33  	"github.com/iotexproject/iotex-core/pkg/log"
    34  	"github.com/iotexproject/iotex-core/pkg/probe"
    35  	"github.com/iotexproject/iotex-core/pkg/util/fileutil"
    36  	"github.com/iotexproject/iotex-core/server/itx"
    37  	"github.com/iotexproject/iotex-core/state/factory"
    38  	"github.com/iotexproject/iotex-core/test/identityset"
    39  	"github.com/iotexproject/iotex-core/testutil"
    40  	"github.com/iotexproject/iotex-core/tools/util"
    41  )
    42  
    43  type claimTestCaseID int
    44  
    45  const (
    46  	//To claim amount 0
    47  	caseClaimZero claimTestCaseID = iota
    48  	//To claim all unclaimed balance
    49  	caseClaimAll
    50  	//To claim more than unclaimed balance
    51  	caseClaimMoreThanBalance
    52  	//To claim only part of available balance
    53  	caseClaimPartOfBalance
    54  	//To claim a negative amount
    55  	caseClaimNegative
    56  	//To claim with an operator address other than the rewarding address
    57  	caseClaimToNonRewardingAddr
    58  	//Total number of claim test cases, keep this at the bottom the enum
    59  	totalClaimCasesNum
    60  )
    61  
    62  func TestBlockReward(t *testing.T) {
    63  	r := require.New(t)
    64  	testTriePath, err := testutil.PathOfTempFile("trie")
    65  	r.NoError(err)
    66  	testDBPath, err := testutil.PathOfTempFile("db")
    67  	r.NoError(err)
    68  	testIndexPath, err := testutil.PathOfTempFile("index")
    69  	r.NoError(err)
    70  	testConsensusPath, err := testutil.PathOfTempFile("cons")
    71  	r.NoError(err)
    72  	testContractIndexPath, err := testutil.PathOfTempFile("contractindex")
    73  	r.NoError(err)
    74  	testSGDIndexPath, err := testutil.PathOfTempFile("sgdindex")
    75  	r.NoError(err)
    76  	cfg := config.Default
    77  	cfg.Consensus.Scheme = config.RollDPoSScheme
    78  	cfg.Genesis.NumDelegates = 1
    79  	cfg.Genesis.NumSubEpochs = 10
    80  	cfg.Genesis.Delegates = []genesis.Delegate{
    81  		{
    82  			OperatorAddrStr: identityset.Address(0).String(),
    83  			RewardAddrStr:   identityset.Address(0).String(),
    84  			VotesStr:        "10",
    85  		},
    86  	}
    87  	cfg.Genesis.BlockInterval = time.Second
    88  	cfg.Consensus.RollDPoS.FSM.AcceptBlockTTL = 300 * time.Millisecond
    89  	cfg.Consensus.RollDPoS.FSM.AcceptProposalEndorsementTTL = 300 * time.Millisecond
    90  	cfg.Consensus.RollDPoS.FSM.AcceptLockEndorsementTTL = 300 * time.Millisecond
    91  	cfg.Consensus.RollDPoS.FSM.CommitTTL = 100 * time.Millisecond
    92  	cfg.Consensus.RollDPoS.ConsensusDBPath = testConsensusPath
    93  	cfg.Genesis.EnableGravityChainVoting = false
    94  	cfg.Chain.ProducerPrivKey = identityset.PrivateKey(0).HexString()
    95  	cfg.Chain.TrieDBPatchFile = ""
    96  	cfg.Chain.TrieDBPath = testTriePath
    97  	cfg.Chain.ChainDBPath = testDBPath
    98  	cfg.Chain.IndexDBPath = testIndexPath
    99  	cfg.Chain.ContractStakingIndexDBPath = testContractIndexPath
   100  	cfg.Chain.SGDIndexDBPath = testSGDIndexPath
   101  	cfg.Network.Port = testutil.RandomPort()
   102  	cfg.Genesis.PollMode = "lifeLong"
   103  
   104  	svr, err := itx.NewServer(cfg)
   105  	require.NoError(t, err)
   106  	require.NoError(t, svr.Start(context.Background()))
   107  	defer func() {
   108  		require.NoError(t, svr.Stop(context.Background()))
   109  	}()
   110  
   111  	require.NoError(t, testutil.WaitUntil(100*time.Millisecond, 20*time.Second, func() (b bool, e error) {
   112  		return svr.ChainService(1).Blockchain().TipHeight() >= 5, nil
   113  	}))
   114  
   115  	ctx := protocol.WithBlockCtx(
   116  		context.Background(),
   117  		protocol.BlockCtx{
   118  			BlockHeight: 0,
   119  		},
   120  	)
   121  	ctx = genesis.WithGenesisContext(ctx, cfg.Genesis)
   122  	ctx = protocol.WithFeatureCtx(ctx)
   123  
   124  	rp := rewarding.FindProtocol(svr.ChainService(1).Registry())
   125  	require.NotNil(t, rp)
   126  	sf := svr.ChainService(1).StateFactory()
   127  
   128  	sk, err := crypto.HexStringToPrivateKey(cfg.Chain.ProducerPrivKey)
   129  	require.NoError(t, err)
   130  	addr := sk.PublicKey().Address()
   131  	require.NotNil(t, addr)
   132  
   133  	blockReward, err := rp.BlockReward(ctx, sf)
   134  	require.NoError(t, err)
   135  	balance, _, err := rp.UnclaimedBalance(ctx, sf, addr)
   136  	require.NoError(t, err)
   137  	assert.True(t, balance.Cmp(big.NewInt(0).Mul(blockReward, big.NewInt(5))) <= 0)
   138  
   139  	for i := 1; i <= 5; i++ {
   140  		blk, err := svr.ChainService(1).BlockDAO().GetBlockByHeight(uint64(i))
   141  		require.NoError(t, err)
   142  		ok := false
   143  		var gr *action.GrantReward
   144  		for _, act := range blk.Body.Actions {
   145  			gr, ok = act.Action().(*action.GrantReward)
   146  			if ok {
   147  				assert.Equal(t, uint64(i), gr.Height())
   148  				break
   149  			}
   150  		}
   151  		assert.True(t, ok)
   152  	}
   153  }
   154  
   155  func TestBlockEpochReward(t *testing.T) {
   156  	// TODO: fix the test
   157  	t.Skip()
   158  
   159  	dbFilePaths := make([]string, 0)
   160  
   161  	//Test will stop after reaching this height
   162  	runToHeight := uint64(60)
   163  
   164  	//Number of nodes
   165  	numNodes := 4
   166  
   167  	// Set mini-cluster configurations
   168  	rand.Seed(time.Now().UnixNano())
   169  	configs := make([]config.Config, numNodes)
   170  	for i := 0; i < numNodes; i++ {
   171  		chainDBPath := fmt.Sprintf("./chain%d.db", i+1)
   172  		dbFilePaths = append(dbFilePaths, chainDBPath)
   173  		trieDBPath := fmt.Sprintf("./trie%d.db", i+1)
   174  		dbFilePaths = append(dbFilePaths, trieDBPath)
   175  		indexDBPath := fmt.Sprintf("./index%d.db", i+1)
   176  		dbFilePaths = append(dbFilePaths, indexDBPath)
   177  		contractIndexDBPath := fmt.Sprintf("./contractindex%d.db", i+1)
   178  		dbFilePaths = append(dbFilePaths, contractIndexDBPath)
   179  		consensusDBPath := fmt.Sprintf("./consensus%d.db", i+1)
   180  		dbFilePaths = append(dbFilePaths, consensusDBPath)
   181  		networkPort := 4689 + i
   182  		apiPort := 14014 + i
   183  		HTTPStatsPort := 8080 + i
   184  		HTTPAdminPort := 9009 + i
   185  		cfg := newConfig(chainDBPath, trieDBPath, indexDBPath, contractIndexDBPath, identityset.PrivateKey(i),
   186  			networkPort, apiPort, uint64(numNodes))
   187  		cfg.Consensus.RollDPoS.ConsensusDBPath = consensusDBPath
   188  		if i == 0 {
   189  			cfg.Network.BootstrapNodes = []string{}
   190  			cfg.Network.MasterKey = "bootnode"
   191  		}
   192  
   193  		//Set Operator and Reward address
   194  		cfg.Genesis.Delegates[i].RewardAddrStr = identityset.Address(i + numNodes).String()
   195  		cfg.Genesis.Delegates[i].OperatorAddrStr = identityset.Address(i).String()
   196  		//Generate random votes  from [1000,2000]
   197  		cfg.Genesis.Delegates[i].VotesStr = strconv.Itoa(1000 + rand.Intn(1000))
   198  		cfg.System.HTTPStatsPort = HTTPStatsPort
   199  		cfg.System.HTTPAdminPort = HTTPAdminPort
   200  		configs[i] = cfg
   201  	}
   202  
   203  	for _, dbFilePath := range dbFilePaths {
   204  		if fileutil.FileExists(dbFilePath) && os.RemoveAll(dbFilePath) != nil {
   205  			log.L().Error("Failed to delete db file")
   206  		}
   207  	}
   208  
   209  	defer func() {
   210  		for _, dbFilePath := range dbFilePaths {
   211  			if fileutil.FileExists(dbFilePath) && os.RemoveAll(dbFilePath) != nil {
   212  				log.L().Error("Failed to delete db file")
   213  			}
   214  		}
   215  
   216  	}()
   217  	// Create mini-cluster
   218  	svrs := make([]*itx.Server, numNodes)
   219  	for i := 0; i < numNodes; i++ {
   220  		svr, err := itx.NewServer(configs[i])
   221  		if err != nil {
   222  			log.L().Fatal("Failed to create server.", zap.Error(err))
   223  		}
   224  		svrs[i] = svr
   225  	}
   226  
   227  	// Start mini-cluster
   228  	for i := 0; i < numNodes; i++ {
   229  		go itx.StartServer(context.Background(), svrs[i], probe.New(7788+i), configs[i])
   230  	}
   231  
   232  	// target address for grpc connection. Default is "127.0.0.1:14014"
   233  	grpcAddr := "127.0.0.1:14014"
   234  
   235  	grpcctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
   236  	defer cancel()
   237  	conn, err := grpc.DialContext(grpcctx, grpcAddr, grpc.WithBlock(), grpc.WithInsecure())
   238  	if err != nil {
   239  		log.L().Error("Failed to connect to API server.")
   240  	}
   241  	defer conn.Close()
   242  
   243  	client := iotexapi.NewAPIServiceClient(conn)
   244  
   245  	// Get each server's parameters: rewarding protocol, working set, block chain etc.
   246  	rps := make([]*rewarding.Protocol, numNodes)
   247  	sfs := make([]factory.Factory, numNodes)
   248  	chains := make([]blockchain.Blockchain, numNodes)
   249  	apis := make([]*api.ServerV2, numNodes)
   250  	//Map of expected unclaimed balance for each reward address
   251  	exptUnclaimed := make(map[string]*big.Int, numNodes)
   252  	//Map of real unclaimed balance for each reward address
   253  	unClaimedBalances := make(map[string]*big.Int, numNodes)
   254  	//Map of initial balance of both reward and operator address
   255  	initBalances := make(map[string]*big.Int, numNodes)
   256  	//Map of claimed amount for each reward address
   257  	claimedAmount := make(map[string]*big.Int, numNodes)
   258  	//Map to translate from operator address to reward address
   259  	getRewardAddStr := make(map[string]string)
   260  
   261  	for i := 0; i < numNodes; i++ {
   262  		rp := rewarding.FindProtocol(svrs[i].ChainService(configs[i].Chain.ID).Registry())
   263  		require.NotNil(t, rp)
   264  		rps[i] = rp
   265  
   266  		sfs[i] = svrs[i].ChainService(configs[i].Chain.ID).StateFactory()
   267  
   268  		chains[i] = svrs[i].ChainService(configs[i].Chain.ID).Blockchain()
   269  		apis[i] = svrs[i].APIServer(configs[i].Chain.ID)
   270  
   271  		rewardAddr := identityset.Address(i + numNodes)
   272  		rewardAddrStr := identityset.Address(i + numNodes).String()
   273  		exptUnclaimed[rewardAddrStr] = big.NewInt(0)
   274  		ctx := genesis.WithGenesisContext(context.Background(), configs[i].Genesis)
   275  		initState, err := accountutil.AccountState(ctx, sfs[i], rewardAddr)
   276  		require.NoError(t, err)
   277  		initBalances[rewardAddrStr] = initState.Balance
   278  
   279  		operatorAddr := identityset.Address(i)
   280  		operatorAddrStr := identityset.Address(i).String()
   281  		initState, err = accountutil.AccountState(ctx, sfs[i], operatorAddr)
   282  		require.NoError(t, err)
   283  		initBalances[operatorAddrStr] = initState.Balance
   284  
   285  		claimedAmount[rewardAddrStr] = big.NewInt(0)
   286  
   287  		getRewardAddStr[identityset.Address(i).String()] = rewardAddrStr
   288  
   289  	}
   290  
   291  	blocksPerEpoch := configs[0].Genesis.Blockchain.NumDelegates * configs[0].Genesis.Blockchain.NumSubEpochs
   292  
   293  	blockReward, err := rps[0].BlockReward(context.Background(), sfs[0])
   294  	require.NoError(t, err)
   295  
   296  	//Calculate epoch reward shares for each delegate based on their weight (votes number)
   297  	epochReward, err := rps[0].EpochReward(context.Background(), sfs[0])
   298  	require.NoError(t, err)
   299  	epRwdShares := make(map[string]*big.Int, numNodes)
   300  
   301  	totalVotes := big.NewInt(0)
   302  	for i := 0; i < numNodes; i++ {
   303  		totalVotes = totalVotes.Add(configs[0].Genesis.Delegates[i].Votes(), totalVotes)
   304  	}
   305  
   306  	for i := 0; i < numNodes; i++ {
   307  		tempShare := big.NewInt(0).Mul(epochReward, configs[0].Genesis.Delegates[i].Votes())
   308  		rewardAddrStr := identityset.Address(i + numNodes).String()
   309  		epRwdShares[rewardAddrStr] = big.NewInt(0).Div(tempShare, totalVotes)
   310  	}
   311  
   312  	//Map from action hash to expected result(Fail-false or Success-true), storing pending injected claim actions,
   313  	pendingClaimActions := make(map[hash.Hash256]bool)
   314  	//Start testing
   315  	preHeight := uint64(0)
   316  	preEpochNum := uint64(0)
   317  	preExpectHigh := uint64(0)
   318  
   319  	fmt.Println("Starting test")
   320  
   321  	if err := testutil.WaitUntil(100*time.Millisecond, 120*time.Second, func() (bool, error) {
   322  		height := chains[0].TipHeight()
   323  
   324  		//New height is reached, need to update block reward
   325  		if height > preHeight {
   326  
   327  			err = testutil.WaitUntil(100*time.Millisecond, 15*time.Second, func() (bool, error) {
   328  				//This Waituntil block guarantees that we can get a consistent snapshot of the followings at some height:
   329  				// 1) all unclaimed balance live
   330  				// 2) expected unclaimed balance
   331  				// The test keeps comparing these values (after Waituntil block) to make sure everything is correct
   332  				curHigh := chains[0].TipHeight()
   333  
   334  				//check pending Claim actions, if a claim is executed, then adjust the expectation accordingly
   335  				//Wait until all the pending actions are settled
   336  				updateExpectationWithPendingClaimList(t, apis[0].CoreService(), exptUnclaimed, claimedAmount, pendingClaimActions)
   337  				if len(pendingClaimActions) > 0 {
   338  					// if there is pending action, retry
   339  					return false, nil
   340  				}
   341  
   342  				for i := 0; i < numNodes; i++ {
   343  					rewardAddr := identityset.Address(i + numNodes)
   344  					unClaimedBalances[rewardAddr.String()], _, err =
   345  						rps[0].UnclaimedBalance(context.Background(), sfs[0], rewardAddr)
   346  				}
   347  
   348  				if curHigh != chains[0].TipHeight() {
   349  					return false, nil
   350  				}
   351  
   352  				//add expected block/epoch reward
   353  				for h := preExpectHigh + 1; h <= curHigh; h++ {
   354  					//Add block reward to current block producer
   355  					header, err := chains[0].BlockHeaderByHeight(h)
   356  					require.NoError(t, err)
   357  					exptUnclaimed[getRewardAddStr[header.ProducerAddress()]] =
   358  						big.NewInt(0).Add(exptUnclaimed[getRewardAddStr[header.ProducerAddress()]], blockReward)
   359  
   360  					//update Epoch rewards
   361  					epochNum := h / blocksPerEpoch
   362  					if epochNum > preEpochNum {
   363  						require.Equal(t, epochNum, preEpochNum+1)
   364  						preEpochNum = epochNum
   365  
   366  						//Add normal epoch reward
   367  						for i := 0; i < numNodes; i++ {
   368  							rewardAddrStr := identityset.Address(i + numNodes).String()
   369  							expectAfterEpoch := big.NewInt(0).Add(exptUnclaimed[rewardAddrStr], epRwdShares[rewardAddrStr])
   370  							exptUnclaimed[rewardAddrStr] = expectAfterEpoch
   371  						}
   372  						//Add foundation bonus
   373  						foundationBonusLastEpoch, err := rps[0].FoundationBonusLastEpoch(context.Background(), sfs[0])
   374  						require.NoError(t, err)
   375  						foundationBonus, err := rps[0].FoundationBonus(context.Background(), sfs[0])
   376  						require.NoError(t, err)
   377  						if epochNum <= foundationBonusLastEpoch {
   378  							for i := 0; i < numNodes; i++ {
   379  								rewardAddrStr := identityset.Address(i + numNodes).String()
   380  								expectAfterEpochBonus := big.NewInt(0).Add(exptUnclaimed[rewardAddrStr], foundationBonus)
   381  								exptUnclaimed[rewardAddrStr] = expectAfterEpochBonus
   382  							}
   383  						}
   384  
   385  					}
   386  					preExpectHigh = h
   387  				}
   388  
   389  				//check pending Claim actions, if a claim is executed, then adjust the expectation accordingly
   390  				updateExpectationWithPendingClaimList(t, apis[0].CoreService(), exptUnclaimed, claimedAmount, pendingClaimActions)
   391  
   392  				curHighCheck := chains[0].TipHeight()
   393  				preHeight = curHighCheck
   394  				//If chain height changes, we need to take snapshot again.
   395  				return curHigh == curHighCheck, nil
   396  
   397  			})
   398  			require.NoError(t, err)
   399  
   400  			//Comparing the expected and real unclaimed balance
   401  			for i := 0; i < numNodes; i++ {
   402  				rewardAddrStr := identityset.Address(i + numNodes).String()
   403  
   404  				fmt.Println("Server ", i, " ", rewardAddrStr,
   405  					" unclaimed ", unClaimedBalances[rewardAddrStr].String(), " height ", preHeight)
   406  				fmt.Println("Server ", i, " ", rewardAddrStr,
   407  					"  expected ", exptUnclaimed[rewardAddrStr].String())
   408  
   409  				require.Equal(t, exptUnclaimed[rewardAddrStr].String(), unClaimedBalances[rewardAddrStr].String())
   410  			}
   411  
   412  			// perform a random claim and record the amount
   413  			// chose a random node to claim
   414  			d := rand.Intn(numNodes)
   415  			var amount *big.Int
   416  			rewardAddrStr := identityset.Address(d + numNodes).String()
   417  			rewardPriKey := identityset.PrivateKey(d + numNodes)
   418  			expectedSuccess := true
   419  
   420  			rand.Seed(time.Now().UnixNano())
   421  			switch r := rand.Intn(int(totalClaimCasesNum)); claimTestCaseID(r) {
   422  			case caseClaimZero:
   423  				//Claim 0
   424  				amount = big.NewInt(0)
   425  			case caseClaimAll:
   426  				//Claim all
   427  				amount = exptUnclaimed[rewardAddrStr]
   428  			case caseClaimMoreThanBalance:
   429  				//Claim more than available unclaimed balance
   430  				amount = big.NewInt(0).Mul(exptUnclaimed[rewardAddrStr], big.NewInt(2))
   431  			case caseClaimPartOfBalance:
   432  				//Claim random part of available
   433  				amount = big.NewInt(0).Div(exptUnclaimed[rewardAddrStr], big.NewInt(int64(rand.Intn(100000))))
   434  			case caseClaimNegative:
   435  				//Claim negative
   436  				amount = big.NewInt(-100000)
   437  				expectedSuccess = false
   438  			case caseClaimToNonRewardingAddr:
   439  				//Claim to operator address instead of reward address
   440  				rewardPriKey = identityset.PrivateKey(d)
   441  				amount = big.NewInt(12345)
   442  				expectedSuccess = false
   443  			}
   444  
   445  			injectClaim(t, nil, client, rewardPriKey, amount,
   446  				expectedSuccess, 3, 1, pendingClaimActions)
   447  
   448  		}
   449  
   450  		return height > runToHeight, nil
   451  	}); err != nil {
   452  
   453  		log.L().Error(err.Error())
   454  	}
   455  
   456  	//Wait until all the pending actions are settled
   457  	err = testutil.WaitUntil(100*time.Millisecond, 40*time.Second, func() (bool, error) {
   458  		updateExpectationWithPendingClaimList(t, apis[0].CoreService(), exptUnclaimed, claimedAmount, pendingClaimActions)
   459  		return len(pendingClaimActions) == 0, nil
   460  	})
   461  	require.NoError(t, err)
   462  
   463  	for i := 0; i < numNodes; i++ {
   464  		//Check Reward address balance
   465  		rewardAddr := identityset.Address(i + numNodes)
   466  		rewardAddrStr := rewardAddr.String()
   467  		endState, err := accountutil.AccountState(
   468  			genesis.WithGenesisContext(context.Background(), configs[0].Genesis),
   469  			sfs[0],
   470  			rewardAddr,
   471  		)
   472  		require.NoError(t, err)
   473  		fmt.Println("Server ", i, " ", rewardAddrStr, " Closing Balance ", endState.Balance.String())
   474  		expectBalance := big.NewInt(0).Add(initBalances[rewardAddrStr], claimedAmount[rewardAddrStr])
   475  		fmt.Println("Server ", i, " ", rewardAddrStr, "Expected Balance ", expectBalance.String())
   476  		require.Equal(t, expectBalance.String(), endState.Balance.String())
   477  
   478  		//Make sure the non-reward addresses have not received money
   479  		operatorAddr := identityset.Address(i)
   480  		operatorAddrStr := identityset.Address(i).String()
   481  		operatorState, err := accountutil.AccountState(
   482  			genesis.WithGenesisContext(context.Background(), configs[i].Genesis),
   483  			sfs[i],
   484  			operatorAddr,
   485  		)
   486  		require.NoError(t, err)
   487  		require.Equal(t, initBalances[operatorAddrStr], operatorState.Balance)
   488  	}
   489  }
   490  
   491  func injectClaim(
   492  	t *testing.T,
   493  	wg *sync.WaitGroup,
   494  	c iotexapi.APIServiceClient,
   495  	beneficiaryPri crypto.PrivateKey,
   496  	amount *big.Int,
   497  	expectedSuccess bool,
   498  	retryNum int,
   499  	retryInterval int,
   500  	pendingClaimActions map[hash.Hash256]bool,
   501  ) {
   502  	if wg != nil {
   503  		wg.Add(1)
   504  	}
   505  	payload := []byte{}
   506  	beneficiaryAddr := beneficiaryPri.PublicKey().Address()
   507  	require.NotNil(t, beneficiaryAddr)
   508  	ctx := context.Background()
   509  	request := iotexapi.GetAccountRequest{Address: beneficiaryAddr.String()}
   510  	response, err := c.GetAccount(ctx, &request)
   511  	require.NoError(t, err)
   512  	nonce := response.AccountMeta.PendingNonce
   513  
   514  	b := &action.ClaimFromRewardingFundBuilder{}
   515  	act := b.SetAmount(amount).SetData(payload).Build()
   516  	bd := &action.EnvelopeBuilder{}
   517  	elp := bd.SetNonce(nonce).
   518  		SetGasPrice(big.NewInt(0)).
   519  		SetGasLimit(100000).
   520  		SetAction(&act).Build()
   521  
   522  	selp, err := action.Sign(elp, beneficiaryPri)
   523  	require.NoError(t, err)
   524  
   525  	bo := backoff.WithMaxRetries(backoff.NewConstantBackOff(time.Duration(retryInterval)*time.Second), uint64(retryNum))
   526  	if err := backoff.Retry(func() error {
   527  		_, err := c.SendAction(context.Background(), &iotexapi.SendActionRequest{Action: selp.Proto()})
   528  		return err
   529  	}, bo); err != nil {
   530  		log.L().Error("Failed to inject claim", zap.Error(err))
   531  	}
   532  
   533  	if err == nil {
   534  		selpHash, err1 := selp.Hash()
   535  		if err1 != nil {
   536  			log.L().Error("Failed to get hash", zap.Error(err1))
   537  		}
   538  		pendingClaimActions[selpHash] = expectedSuccess
   539  	}
   540  
   541  	if wg != nil {
   542  		wg.Done()
   543  	}
   544  }
   545  
   546  func updateExpectationWithPendingClaimList(
   547  	t *testing.T,
   548  	api api.CoreService,
   549  	exptUnclaimed map[string]*big.Int,
   550  	claimedAmount map[string]*big.Int,
   551  	pendingClaimActions map[hash.Hash256]bool,
   552  ) bool {
   553  	updated := false
   554  	for selpHash, expectedSuccess := range pendingClaimActions {
   555  		receipt, err := util.GetReceiptByAction(api, selpHash)
   556  		if err == nil {
   557  			actInfo, err := util.GetActionByActionHash(api, selpHash)
   558  			require.NoError(t, err)
   559  			addr := actInfo.GetSender()
   560  			require.NotNil(t, addr)
   561  
   562  			act := &action.ClaimFromRewardingFund{}
   563  			err = act.LoadProto(actInfo.GetAction().Core.GetClaimFromRewardingFund())
   564  			require.NoError(t, err)
   565  			amount := act.Amount()
   566  
   567  			if receipt.Status == uint64(iotextypes.ReceiptStatus_Success) {
   568  				newExpectUnclaimed := big.NewInt(0).Sub(exptUnclaimed[addr], amount)
   569  				exptUnclaimed[addr] = newExpectUnclaimed
   570  
   571  				newClaimedAmount := big.NewInt(0).Add(claimedAmount[addr], amount)
   572  				claimedAmount[addr] = newClaimedAmount
   573  				updated = true
   574  
   575  				//An test case expected to fail should never success
   576  				require.NotEqual(t, expectedSuccess, false)
   577  			}
   578  
   579  			delete(pendingClaimActions, selpHash)
   580  		}
   581  	}
   582  
   583  	return updated
   584  }
   585  
   586  func newConfig(
   587  	chainDBPath,
   588  	trieDBPath,
   589  	indexDBPath,
   590  	contractIndexDBPath string,
   591  	producerPriKey crypto.PrivateKey,
   592  	networkPort,
   593  	apiPort int,
   594  	numNodes uint64,
   595  ) config.Config {
   596  	cfg := config.Default
   597  
   598  	cfg.Network.Port = networkPort
   599  	cfg.Network.BootstrapNodes = []string{"/ip4/127.0.0.1/tcp/4689/ipfs/12D3KooWJwW6pUpTkxPTMv84RPLPMQVEAjZ6fvJuX4oZrvW5DAGQ"}
   600  
   601  	cfg.Chain.ID = 1
   602  	cfg.Chain.ChainDBPath = chainDBPath
   603  	cfg.Chain.TrieDBPath = trieDBPath
   604  	cfg.Chain.IndexDBPath = indexDBPath
   605  	cfg.Chain.ContractStakingIndexDBPath = contractIndexDBPath
   606  	cfg.Chain.ProducerPrivKey = producerPriKey.HexString()
   607  	cfg.Chain.EnableAsyncIndexWrite = false
   608  
   609  	cfg.ActPool.MinGasPriceStr = big.NewInt(0).String()
   610  
   611  	cfg.Consensus.Scheme = config.RollDPoSScheme
   612  	cfg.Consensus.RollDPoS.FSM.UnmatchedEventInterval = 120 * time.Millisecond
   613  	cfg.Consensus.RollDPoS.FSM.AcceptBlockTTL = 200 * time.Millisecond
   614  	cfg.Consensus.RollDPoS.FSM.AcceptProposalEndorsementTTL = 100 * time.Millisecond
   615  	cfg.Consensus.RollDPoS.FSM.AcceptLockEndorsementTTL = 100 * time.Millisecond
   616  	cfg.Consensus.RollDPoS.FSM.CommitTTL = 100 * time.Millisecond
   617  	cfg.Consensus.RollDPoS.FSM.EventChanSize = 100000
   618  	cfg.Consensus.RollDPoS.ToleratedOvertime = 1200 * time.Millisecond
   619  	cfg.Consensus.RollDPoS.Delay = 6 * time.Second
   620  
   621  	cfg.API.GRPCPort = apiPort
   622  
   623  	cfg.Genesis.Blockchain.NumSubEpochs = 4
   624  	cfg.Genesis.Blockchain.NumDelegates = numNodes
   625  	cfg.Genesis.Blockchain.TimeBasedRotation = true
   626  	cfg.Genesis.Delegates = cfg.Genesis.Delegates[0:numNodes]
   627  	cfg.Genesis.BlockInterval = 500 * time.Millisecond
   628  	cfg.Genesis.EnableGravityChainVoting = true
   629  	cfg.Genesis.Rewarding.FoundationBonusLastEpoch = 2
   630  
   631  	return cfg
   632  }