github.com/klaytn/klaytn@v1.10.2/tests/hard_fork_test.go (about)

     1  // Copyright 2019 The klaytn Authors
     2  // This file is part of the klaytn library.
     3  //
     4  // The klaytn library is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU Lesser General Public License as published by
     6  // the Free Software Foundation, either version 3 of the License, or
     7  // (at your option) any later version.
     8  //
     9  // The klaytn library is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    12  // GNU Lesser General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU Lesser General Public License
    15  // along with the klaytn library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package tests
    18  
    19  import (
    20  	"crypto/ecdsa"
    21  	"encoding/json"
    22  	"fmt"
    23  	"io/ioutil"
    24  	"math/big"
    25  	"os"
    26  	"strings"
    27  	"testing"
    28  	"time"
    29  
    30  	"github.com/klaytn/klaytn/blockchain"
    31  	"github.com/klaytn/klaytn/blockchain/types"
    32  	"github.com/klaytn/klaytn/blockchain/vm"
    33  	"github.com/klaytn/klaytn/common"
    34  	"github.com/klaytn/klaytn/common/hexutil"
    35  	"github.com/klaytn/klaytn/common/profile"
    36  	"github.com/klaytn/klaytn/consensus/istanbul"
    37  	"github.com/klaytn/klaytn/crypto"
    38  	"github.com/klaytn/klaytn/governance"
    39  	"github.com/klaytn/klaytn/log"
    40  	"github.com/klaytn/klaytn/params"
    41  	"github.com/klaytn/klaytn/rlp"
    42  	"github.com/klaytn/klaytn/storage/database"
    43  	"github.com/stretchr/testify/assert"
    44  	"github.com/stretchr/testify/require"
    45  
    46  	istanbulBackend "github.com/klaytn/klaytn/consensus/istanbul/backend"
    47  )
    48  
    49  // TestHardForkBlock tests whether the change incurs a hard fork or not.
    50  // genesis.json, b1.rlp, and b2.rlp has raw data of genesis, and consecutive two blocks after the genesis block.
    51  // If anything is failed, it can be considered that a hard fork occurs.
    52  func TestHardForkBlock(t *testing.T) {
    53  	log.EnableLogForTest(log.LvlCrit, log.LvlTrace)
    54  	var genesis blockchain.Genesis
    55  
    56  	// If you uncomment the below, you can find this test failed with an error "!!!!!HARD FORK DETECTED!!!!!"
    57  	//fork.UpdateHardForkConfig(&fork.HardForkConfig{
    58  	//})
    59  
    60  	// If you print out b1.rlp and b2.rlp, uncomment below.
    61  	// `genBlocks` could be failed sometimes depends on the order of transaction in a block. Just try again.
    62  	// genBlocks(t)
    63  	// return
    64  
    65  	// load raw data from files.
    66  	genesisJson, err := ioutil.ReadFile("genesis.json")
    67  	require.Equal(t, nil, err)
    68  	rawb1, err := ioutil.ReadFile("b1.rlp")
    69  	require.Equal(t, nil, err)
    70  	rawb2, err := ioutil.ReadFile("b2.rlp")
    71  	require.Equal(t, nil, err)
    72  
    73  	err = json.Unmarshal([]byte(genesisJson), &genesis)
    74  	require.Equal(t, nil, err)
    75  
    76  	genesisKey, err := crypto.HexToECDSA("42eb1412d77987043716f425964b1c8d4c27ce9fb3e9a5b9ab243bc9882fe731")
    77  	require.Equal(t, nil, err)
    78  
    79  	var genesisAddr common.Address
    80  	for addr := range genesis.Alloc {
    81  		genesisAddr = addr
    82  		break
    83  	}
    84  
    85  	dir := "chaindata-hardfork"
    86  	os.RemoveAll(dir)
    87  
    88  	chainDb := NewDatabase(dir, database.LevelDB)
    89  	defer func() {
    90  		os.RemoveAll(dir)
    91  	}()
    92  
    93  	gov := generateGovernaceDataForTest()
    94  	chainConfig, _, err := blockchain.SetupGenesisBlock(chainDb, &genesis, params.UnusedNetworkId, false, false)
    95  	governance.AddGovernanceCacheForTest(gov, 0, genesis.Config)
    96  	engine := istanbulBackend.New(genesisAddr, istanbul.DefaultConfig, genesisKey, chainDb, gov, common.CONSENSUSNODE)
    97  	chain, err := blockchain.NewBlockChain(chainDb, nil, chainConfig, engine, vm.Config{})
    98  
    99  	r1, err := hexutil.Decode(string(rawb1))
   100  	require.Equal(t, nil, err)
   101  	r2, err := hexutil.Decode(string(rawb2))
   102  	require.Equal(t, nil, err)
   103  	rawBlocks := [...][]byte{r1, r2}
   104  
   105  	var blocks types.Blocks
   106  	for _, raw := range rawBlocks {
   107  		var blk types.Block
   108  
   109  		err := rlp.DecodeBytes(raw, &blk)
   110  		require.Equal(t, nil, err)
   111  
   112  		blocks = append(blocks, &blk)
   113  	}
   114  
   115  	idx, err := chain.InsertChain(blocks)
   116  	require.Equalf(t, nil, err, "!!!!!HARD FORK DETECTED!!!!!")
   117  	require.Equal(t, 0, idx)
   118  }
   119  
   120  // genBlock generates two blocks including transactions utilizing all transaction types and account types.
   121  func genBlocks(t *testing.T) {
   122  	testFunctions := []struct {
   123  		Name  string
   124  		genTx genTransaction
   125  	}{
   126  		{"LegacyTransaction", genLegacyTransaction},
   127  		{"ValueTransfer", genValueTransfer},
   128  		{"ValueTransferWithMemo", genValueTransferWithMemo},
   129  		{"AccountUpdate", genAccountUpdateIdem},
   130  		{"SmartContractExecution", genSmartContractExecution},
   131  		{"Cancel", genCancel},
   132  		{"ChainDataAnchoring", genChainDataAnchoring},
   133  		{"FeeDelegatedValueTransfer", genFeeDelegatedValueTransfer},
   134  		{"FeeDelegatedValueTransferWithMemo", genFeeDelegatedValueTransferWithMemo},
   135  		{"FeeDelegatedAccountUpdate", genFeeDelegatedAccountUpdateIdem},
   136  		{"FeeDelegatedSmartContractExecution", genFeeDelegatedSmartContractExecution},
   137  		{"FeeDelegatedCancel", genFeeDelegatedCancel},
   138  		{"FeeDelegatedWithRatioValueTransfer", genFeeDelegatedWithRatioValueTransfer},
   139  		{"FeeDelegatedWithRatioValueTransferWithMemo", genFeeDelegatedWithRatioValueTransferWithMemo},
   140  		{"FeeDelegatedWithRatioAccountUpdate", genFeeDelegatedWithRatioAccountUpdateIdem},
   141  		{"FeeDelegatedWithRatioSmartContractExecution", genFeeDelegatedWithRatioSmartContractExecution},
   142  		{"FeeDelegatedWithRatioCancel", genFeeDelegatedWithRatioCancel},
   143  	}
   144  
   145  	accountTypes := []struct {
   146  		Type    string
   147  		account TestAccount
   148  	}{
   149  		{"KlaytnLegacy", genKlaytnLegacyAccount(t)},
   150  		{"Public", genPublicAccount(t)},
   151  		{"MultiSig", genMultiSigAccount(t)},
   152  		{"RoleBasedWithPublic", genRoleBasedWithPublicAccount(t)},
   153  		{"RoleBasedWithMultiSig", genRoleBasedWithMultiSigAccount(t)},
   154  	}
   155  
   156  	log.EnableLogForTest(log.LvlCrit, log.LvlTrace)
   157  	prof := profile.NewProfiler()
   158  
   159  	// Initialize blockchain
   160  	start := time.Now()
   161  	bcdata, err := NewBCData(6, 4)
   162  	assert.Equal(t, nil, err)
   163  	prof.Profile("main_init_blockchain", time.Now().Sub(start))
   164  
   165  	b, err := json.Marshal(bcdata.genesis)
   166  	ioutil.WriteFile("genesis.json", b, 0o755)
   167  
   168  	defer bcdata.Shutdown()
   169  
   170  	// Initialize address-balance map for verification
   171  	start = time.Now()
   172  	accountMap := NewAccountMap()
   173  	if err := accountMap.Initialize(bcdata); err != nil {
   174  		t.Fatal(err)
   175  	}
   176  	prof.Profile("main_init_accountMap", time.Now().Sub(start))
   177  
   178  	// reservoir account
   179  	var reservoir TestAccount
   180  	reservoir = &TestAccountType{
   181  		Addr:  *bcdata.addrs[0],
   182  		Keys:  []*ecdsa.PrivateKey{bcdata.privKeys[0]},
   183  		Nonce: uint64(0),
   184  	}
   185  
   186  	signer := types.LatestSignerForChainID(bcdata.bc.Config().ChainID)
   187  	gasPrice := new(big.Int).SetUint64(bcdata.bc.Config().UnitPrice)
   188  
   189  	// For smart contract
   190  	contract, err := createAnonymousAccount("ed34b0cf47a0021e9897760f0a904a69260c2f638e0bcc805facb745ec3ff9ab")
   191  	assert.Equal(t, nil, err)
   192  
   193  	// Preparing step
   194  	{
   195  		var txs types.Transactions
   196  		// Preparing step. Send KLAY to LegacyAccount.
   197  		{
   198  			amount := new(big.Int).Mul(big.NewInt(3000), new(big.Int).SetUint64(params.KLAY))
   199  			tx := types.NewTransaction(reservoir.GetNonce(),
   200  				accountTypes[0].account.GetAddr(), amount, gasLimit, gasPrice, []byte{})
   201  
   202  			err := tx.SignWithKeys(signer, reservoir.GetTxKeys())
   203  			assert.Equal(t, nil, err)
   204  
   205  			txs = append(txs, tx)
   206  
   207  			reservoir.AddNonce()
   208  		}
   209  
   210  		// Preparing step. Send KLAY to KlaytnAcounts.
   211  		for i := 1; i < len(accountTypes); i++ {
   212  			// create an account which account key will be replaced to one of account key types.
   213  			anon, err := createAnonymousAccount(getRandomPrivateKeyString(t))
   214  			assert.Equal(t, nil, err)
   215  
   216  			accountTypes[i].account.SetAddr(anon.Addr)
   217  
   218  			{
   219  				amount := new(big.Int).Mul(big.NewInt(3000), new(big.Int).SetUint64(params.KLAY))
   220  				tx := types.NewTransaction(reservoir.GetNonce(),
   221  					accountTypes[i].account.GetAddr(), amount, gasLimit, gasPrice, []byte{})
   222  
   223  				err := tx.SignWithKeys(signer, reservoir.GetTxKeys())
   224  				assert.Equal(t, nil, err)
   225  				txs = append(txs, tx)
   226  
   227  				reservoir.AddNonce()
   228  			}
   229  
   230  			// update the account's key
   231  			{
   232  				values := map[types.TxValueKeyType]interface{}{
   233  					types.TxValueKeyNonce:      accountTypes[i].account.GetNonce(),
   234  					types.TxValueKeyFrom:       accountTypes[i].account.GetAddr(),
   235  					types.TxValueKeyGasLimit:   gasLimit,
   236  					types.TxValueKeyGasPrice:   gasPrice,
   237  					types.TxValueKeyAccountKey: accountTypes[i].account.GetAccKey(),
   238  				}
   239  				tx, err := types.NewTransactionWithMap(types.TxTypeAccountUpdate, values)
   240  				assert.Equal(t, nil, err)
   241  
   242  				err = tx.SignWithKeys(signer, anon.Keys)
   243  				assert.Equal(t, nil, err)
   244  
   245  				txs = append(txs, tx)
   246  
   247  				accountTypes[i].account.AddNonce()
   248  			}
   249  		}
   250  
   251  		{
   252  			amount := new(big.Int).SetUint64(0)
   253  
   254  			values := map[types.TxValueKeyType]interface{}{
   255  				types.TxValueKeyNonce:         reservoir.GetNonce(),
   256  				types.TxValueKeyFrom:          reservoir.GetAddr(),
   257  				types.TxValueKeyTo:            (*common.Address)(nil),
   258  				types.TxValueKeyAmount:        amount,
   259  				types.TxValueKeyGasLimit:      gasLimit,
   260  				types.TxValueKeyGasPrice:      gasPrice,
   261  				types.TxValueKeyHumanReadable: false,
   262  				types.TxValueKeyData:          common.FromHex(code),
   263  				types.TxValueKeyCodeFormat:    params.CodeFormatEVM,
   264  			}
   265  			tx, err := types.NewTransactionWithMap(types.TxTypeSmartContractDeploy, values)
   266  			assert.Equal(t, nil, err)
   267  
   268  			err = tx.SignWithKeys(signer, reservoir.GetTxKeys())
   269  			assert.Equal(t, nil, err)
   270  
   271  			txs = append(txs, tx)
   272  
   273  			contract.Addr = crypto.CreateAddress(reservoir.GetAddr(), reservoir.GetNonce())
   274  
   275  			reservoir.AddNonce()
   276  		}
   277  		{
   278  			amount := new(big.Int).SetUint64(0)
   279  
   280  			values := map[types.TxValueKeyType]interface{}{
   281  				types.TxValueKeyNonce:         reservoir.GetNonce(),
   282  				types.TxValueKeyFrom:          reservoir.GetAddr(),
   283  				types.TxValueKeyTo:            (*common.Address)(nil),
   284  				types.TxValueKeyAmount:        amount,
   285  				types.TxValueKeyGasLimit:      gasLimit,
   286  				types.TxValueKeyGasPrice:      gasPrice,
   287  				types.TxValueKeyHumanReadable: false,
   288  				types.TxValueKeyData:          common.FromHex(code),
   289  				types.TxValueKeyFeePayer:      reservoir.GetAddr(),
   290  				types.TxValueKeyCodeFormat:    params.CodeFormatEVM,
   291  			}
   292  			tx, err := types.NewTransactionWithMap(types.TxTypeFeeDelegatedSmartContractDeploy, values)
   293  			assert.Equal(t, nil, err)
   294  
   295  			err = tx.SignWithKeys(signer, reservoir.GetTxKeys())
   296  			assert.Equal(t, nil, err)
   297  
   298  			err = tx.SignFeePayerWithKeys(signer, reservoir.GetFeeKeys())
   299  			assert.Equal(t, nil, err)
   300  
   301  			txs = append(txs, tx)
   302  
   303  			reservoir.AddNonce()
   304  		}
   305  		{
   306  			amount := new(big.Int).SetUint64(0)
   307  
   308  			values := map[types.TxValueKeyType]interface{}{
   309  				types.TxValueKeyNonce:              reservoir.GetNonce(),
   310  				types.TxValueKeyFrom:               reservoir.GetAddr(),
   311  				types.TxValueKeyTo:                 (*common.Address)(nil),
   312  				types.TxValueKeyAmount:             amount,
   313  				types.TxValueKeyGasLimit:           gasLimit,
   314  				types.TxValueKeyGasPrice:           gasPrice,
   315  				types.TxValueKeyHumanReadable:      false,
   316  				types.TxValueKeyData:               common.FromHex(code),
   317  				types.TxValueKeyFeePayer:           reservoir.GetAddr(),
   318  				types.TxValueKeyFeeRatioOfFeePayer: types.FeeRatio(20),
   319  				types.TxValueKeyCodeFormat:         params.CodeFormatEVM,
   320  			}
   321  			tx, err := types.NewTransactionWithMap(types.TxTypeFeeDelegatedSmartContractDeployWithRatio, values)
   322  			assert.Equal(t, nil, err)
   323  
   324  			err = tx.SignWithKeys(signer, reservoir.GetTxKeys())
   325  			assert.Equal(t, nil, err)
   326  
   327  			err = tx.SignFeePayerWithKeys(signer, reservoir.GetFeeKeys())
   328  			assert.Equal(t, nil, err)
   329  
   330  			txs = append(txs, tx)
   331  
   332  			reservoir.AddNonce()
   333  		}
   334  
   335  		// SmartContractDeploy with Nil Recipient.
   336  		{
   337  			amount := new(big.Int).SetUint64(0)
   338  
   339  			values := map[types.TxValueKeyType]interface{}{
   340  				types.TxValueKeyNonce:         reservoir.GetNonce(),
   341  				types.TxValueKeyFrom:          reservoir.GetAddr(),
   342  				types.TxValueKeyTo:            (*common.Address)(nil),
   343  				types.TxValueKeyAmount:        amount,
   344  				types.TxValueKeyGasLimit:      gasLimit,
   345  				types.TxValueKeyGasPrice:      gasPrice,
   346  				types.TxValueKeyHumanReadable: false,
   347  				types.TxValueKeyData:          common.FromHex(code),
   348  				types.TxValueKeyCodeFormat:    params.CodeFormatEVM,
   349  			}
   350  			tx, err := types.NewTransactionWithMap(types.TxTypeSmartContractDeploy, values)
   351  			assert.Equal(t, nil, err)
   352  
   353  			err = tx.SignWithKeys(signer, reservoir.GetTxKeys())
   354  			assert.Equal(t, nil, err)
   355  
   356  			txs = append(txs, tx)
   357  
   358  			reservoir.AddNonce()
   359  		}
   360  		{
   361  			amount := new(big.Int).SetUint64(0)
   362  
   363  			values := map[types.TxValueKeyType]interface{}{
   364  				types.TxValueKeyNonce:         reservoir.GetNonce(),
   365  				types.TxValueKeyFrom:          reservoir.GetAddr(),
   366  				types.TxValueKeyTo:            (*common.Address)(nil),
   367  				types.TxValueKeyAmount:        amount,
   368  				types.TxValueKeyGasLimit:      gasLimit,
   369  				types.TxValueKeyGasPrice:      gasPrice,
   370  				types.TxValueKeyHumanReadable: false,
   371  				types.TxValueKeyData:          common.FromHex(code),
   372  				types.TxValueKeyFeePayer:      reservoir.GetAddr(),
   373  				types.TxValueKeyCodeFormat:    params.CodeFormatEVM,
   374  			}
   375  			tx, err := types.NewTransactionWithMap(types.TxTypeFeeDelegatedSmartContractDeploy, values)
   376  			assert.Equal(t, nil, err)
   377  
   378  			err = tx.SignWithKeys(signer, reservoir.GetTxKeys())
   379  			assert.Equal(t, nil, err)
   380  
   381  			err = tx.SignFeePayerWithKeys(signer, reservoir.GetFeeKeys())
   382  			assert.Equal(t, nil, err)
   383  
   384  			txs = append(txs, tx)
   385  
   386  			reservoir.AddNonce()
   387  		}
   388  		{
   389  			amount := new(big.Int).SetUint64(0)
   390  
   391  			values := map[types.TxValueKeyType]interface{}{
   392  				types.TxValueKeyNonce:              reservoir.GetNonce(),
   393  				types.TxValueKeyFrom:               reservoir.GetAddr(),
   394  				types.TxValueKeyTo:                 (*common.Address)(nil),
   395  				types.TxValueKeyAmount:             amount,
   396  				types.TxValueKeyGasLimit:           gasLimit,
   397  				types.TxValueKeyGasPrice:           gasPrice,
   398  				types.TxValueKeyHumanReadable:      false,
   399  				types.TxValueKeyData:               common.FromHex(code),
   400  				types.TxValueKeyFeePayer:           reservoir.GetAddr(),
   401  				types.TxValueKeyFeeRatioOfFeePayer: types.FeeRatio(20),
   402  				types.TxValueKeyCodeFormat:         params.CodeFormatEVM,
   403  			}
   404  			tx, err := types.NewTransactionWithMap(types.TxTypeFeeDelegatedSmartContractDeployWithRatio, values)
   405  			assert.Equal(t, nil, err)
   406  
   407  			err = tx.SignWithKeys(signer, reservoir.GetTxKeys())
   408  			assert.Equal(t, nil, err)
   409  
   410  			err = tx.SignFeePayerWithKeys(signer, reservoir.GetFeeKeys())
   411  			assert.Equal(t, nil, err)
   412  
   413  			txs = append(txs, tx)
   414  
   415  			reservoir.AddNonce()
   416  		}
   417  
   418  		if err := bcdata.GenABlockWithTransactions(accountMap, txs, prof); err != nil {
   419  			t.Fatal(err)
   420  		}
   421  	}
   422  
   423  	var txs types.Transactions
   424  
   425  	for _, f := range testFunctions {
   426  		for _, sender := range accountTypes {
   427  			toAccount := reservoir
   428  
   429  			// LegacyTransaction can be used only with LegacyAccount and KlaytnAccount with AccountKeyLegacy.
   430  			if !strings.Contains(sender.Type, "Legacy") && strings.Contains(f.Name, "Legacy") {
   431  				continue
   432  			}
   433  
   434  			// Sender can't be a LegacyAccount with AccountUpdate
   435  			if sender.Type == "Legacy" && strings.Contains(f.Name, "AccountUpdate") {
   436  				continue
   437  			}
   438  
   439  			gasPriceLocal := gasPrice
   440  			// Set contract's address with SmartContractExecution
   441  			if strings.Contains(f.Name, "SmartContractExecution") {
   442  				toAccount = contract
   443  				gasPriceLocal = big.NewInt(0)
   444  			}
   445  
   446  			if !strings.Contains(f.Name, "FeeDelegated") {
   447  				// For NonFeeDelegated Transactions
   448  				tx, _ := f.genTx(t, signer, sender.account, toAccount, nil, gasPriceLocal)
   449  				txs = append(txs, tx)
   450  				sender.account.AddNonce()
   451  			} else {
   452  				// For FeeDelegated(WithRatio) Transactions
   453  				for _, payer := range accountTypes {
   454  					tx, _ := f.genTx(t, signer, sender.account, toAccount, payer.account, gasPriceLocal)
   455  					txs = append(txs, tx)
   456  					sender.account.AddNonce()
   457  				}
   458  			}
   459  		}
   460  	}
   461  
   462  	if err := bcdata.GenABlockWithTransactions(accountMap, txs, prof); err != nil {
   463  		t.Fatal(err)
   464  	}
   465  
   466  	lastBlock := bcdata.bc.CurrentBlock().NumberU64()
   467  	for i := uint64(0); i <= lastBlock; i++ {
   468  		blk := bcdata.bc.GetBlockByNumber(i)
   469  		b, err := rlp.EncodeToBytes(blk)
   470  		require.Equal(t, nil, err)
   471  
   472  		// fmt.Println(blk.String())
   473  		// fmt.Println("encoded===")
   474  		// fmt.Println((hexutil.Bytes)(b))
   475  
   476  		filename := fmt.Sprintf("b%d.rlp", i)
   477  		f, err := os.Create(filename)
   478  		require.Equal(t, nil, err)
   479  
   480  		_, err = f.WriteString(hexutil.Encode(b))
   481  		require.Equal(t, nil, err)
   482  
   483  		err = f.Close()
   484  		require.Equal(t, nil, err)
   485  	}
   486  }
   487  
   488  func genAccountUpdateIdem(t *testing.T, signer types.Signer, from TestAccount, to TestAccount, payer TestAccount, gasPrice *big.Int) (*types.Transaction, uint64) {
   489  	values, intrinsic := genMapForUpdate(from, to, gasPrice, from.GetAccKey(), types.TxTypeAccountUpdate)
   490  
   491  	tx, err := types.NewTransactionWithMap(types.TxTypeAccountUpdate, values)
   492  	assert.Equal(t, nil, err)
   493  
   494  	err = tx.SignWithKeys(signer, from.GetUpdateKeys())
   495  	assert.Equal(t, nil, err)
   496  
   497  	return tx, intrinsic
   498  }
   499  
   500  func genFeeDelegatedAccountUpdateIdem(t *testing.T, signer types.Signer, from TestAccount, to TestAccount, payer TestAccount, gasPrice *big.Int) (*types.Transaction, uint64) {
   501  	values, intrinsic := genMapForUpdate(from, to, gasPrice, from.GetAccKey(), types.TxTypeFeeDelegatedAccountUpdate)
   502  	values[types.TxValueKeyFeePayer] = payer.GetAddr()
   503  
   504  	tx, err := types.NewTransactionWithMap(types.TxTypeFeeDelegatedAccountUpdate, values)
   505  	assert.Equal(t, nil, err)
   506  
   507  	err = tx.SignWithKeys(signer, from.GetUpdateKeys())
   508  	assert.Equal(t, nil, err)
   509  
   510  	err = tx.SignFeePayerWithKeys(signer, payer.GetFeeKeys())
   511  	assert.Equal(t, nil, err)
   512  
   513  	return tx, intrinsic
   514  }
   515  
   516  func genFeeDelegatedWithRatioAccountUpdateIdem(t *testing.T, signer types.Signer, from TestAccount, to TestAccount, payer TestAccount, gasPrice *big.Int) (*types.Transaction, uint64) {
   517  	values, intrinsic := genMapForUpdate(from, to, gasPrice, from.GetAccKey(), types.TxTypeFeeDelegatedAccountUpdateWithRatio)
   518  	values[types.TxValueKeyFeePayer] = payer.GetAddr()
   519  	values[types.TxValueKeyFeeRatioOfFeePayer] = types.FeeRatio(30)
   520  
   521  	tx, err := types.NewTransactionWithMap(types.TxTypeFeeDelegatedAccountUpdateWithRatio, values)
   522  	assert.Equal(t, nil, err)
   523  
   524  	err = tx.SignWithKeys(signer, from.GetUpdateKeys())
   525  	assert.Equal(t, nil, err)
   526  
   527  	err = tx.SignFeePayerWithKeys(signer, payer.GetFeeKeys())
   528  	assert.Equal(t, nil, err)
   529  
   530  	return tx, intrinsic
   531  }