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