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

     1  // Copyright (c) 2019 IoTeX Foundation
     2  // This source code is provided 'as is' and no warranties are given as to title or non-infringement, merchantability
     3  // or fitness for purpose and, to the extent permitted by law, all liability for your use of the code is disclaimed.
     4  // This source code is governed by Apache License 2.0 that can be found in the LICENSE file.
     5  
     6  package e2etest
     7  
     8  import (
     9  	"context"
    10  	"fmt"
    11  	"math/big"
    12  	"math/rand"
    13  	"testing"
    14  	"time"
    15  
    16  	"github.com/cenkalti/backoff"
    17  	"github.com/pkg/errors"
    18  	"github.com/stretchr/testify/require"
    19  	"google.golang.org/genproto/googleapis/rpc/errdetails"
    20  	"google.golang.org/grpc"
    21  	"google.golang.org/grpc/codes"
    22  	"google.golang.org/grpc/status"
    23  
    24  	"github.com/iotexproject/go-pkgs/crypto"
    25  	"github.com/iotexproject/iotex-address/address"
    26  	"github.com/iotexproject/iotex-proto/golang/iotexapi"
    27  
    28  	"github.com/iotexproject/iotex-core/action"
    29  	"github.com/iotexproject/iotex-core/action/protocol"
    30  	"github.com/iotexproject/iotex-core/action/protocol/account"
    31  	accountutil "github.com/iotexproject/iotex-core/action/protocol/account/util"
    32  	"github.com/iotexproject/iotex-core/action/protocol/rewarding"
    33  	"github.com/iotexproject/iotex-core/actpool"
    34  	"github.com/iotexproject/iotex-core/blockchain"
    35  	"github.com/iotexproject/iotex-core/blockchain/blockdao"
    36  	"github.com/iotexproject/iotex-core/blockchain/filedao"
    37  	"github.com/iotexproject/iotex-core/blockchain/genesis"
    38  	"github.com/iotexproject/iotex-core/config"
    39  	"github.com/iotexproject/iotex-core/db"
    40  	"github.com/iotexproject/iotex-core/pkg/probe"
    41  	"github.com/iotexproject/iotex-core/pkg/unit"
    42  	"github.com/iotexproject/iotex-core/server/itx"
    43  	"github.com/iotexproject/iotex-core/state/factory"
    44  	"github.com/iotexproject/iotex-core/test/identityset"
    45  	"github.com/iotexproject/iotex-core/testutil"
    46  	"github.com/iotexproject/iotex-core/tools/util"
    47  )
    48  
    49  type TransferState int
    50  
    51  const (
    52  	//This transfer should fail to be accepted into action pool
    53  	TsfFail TransferState = iota
    54  	//This transfer should be accepted into action pool,
    55  	//and later on be minted into block chain after block creating interval
    56  	TsfSuccess
    57  	//This transfer should be accepted into action pool,
    58  	//but will stay in action pool (not minted yet)
    59  	//until all the blocks with preceding nonce arrive
    60  	TsfPending
    61  	//This transfer should enable all the pending transfer in action pool be accepted
    62  	//into block chain. This happens when a transfer with the missing nonce arrives,
    63  	//filling the gap between minted blocks and pending blocks.
    64  	TsfFinal
    65  )
    66  
    67  type AccountState int
    68  
    69  const (
    70  	//This account should be created on blockchain in run time with the given balance
    71  	AcntCreate AccountState = iota
    72  	//This account already exist, need to load the the key, address, balance to this test case
    73  	AcntExist
    74  	//This account doesnt exist on blockchain, but have a valid key and address
    75  	AcntNotRegistered
    76  	//This account doesnt exist, the address is not valid (a random byte string)
    77  	AcntBadAddr
    78  )
    79  
    80  type simpleTransferTestCfg struct {
    81  	senderAcntState AccountState
    82  	senderPriKey    crypto.PrivateKey
    83  	senderBalance   *big.Int
    84  	recvAcntState   AccountState
    85  	recvPriKey      crypto.PrivateKey
    86  	recvBalance     *big.Int
    87  	nonce           uint64
    88  	amount          *big.Int
    89  	payload         []byte
    90  	gasLimit        uint64
    91  	gasPrice        *big.Int
    92  	expectedResult  TransferState
    93  	expectedDesc    string
    94  	message         string
    95  }
    96  
    97  var (
    98  	localKeys = []string{
    99  		"fd26207d4657c422da8242686ba4f5066be11ffe9d342d37967f9538c44cebbf",
   100  		"012d7c684388ca7508fb3483f58e29a8de327b28097dd1d207116225307c98bf",
   101  		"0a653365c521592062fbbd3b8e1fc64a80b6199bce2b1dbac091955b5fe14125",
   102  		"0b3eb204a1641ea072505eec5161043e8c19bd039fad7f61e2180d4d396af45b",
   103  		"affad54ae2fd6f139c235439bebb9810ccdd016911113b220af6fd87c952b5bd",
   104  		"d260035a571390213c8521b73fff47b6fd8ce2474e37a2421bf1d4657e06e3ea",
   105  		"dee8d3dab8fbf36990608936241d1cc6f7d51663285919806eb05b1365dd62a3",
   106  		"d08769fb91911eed6156b1ea7dbb8adf3a68b1ed3b4b173074e7a67996d76c5d",
   107  		"29945a86884def518347585caaddcc9ac08c5d6ca614b8547625541b43adffe7",
   108  		"c8018d8a2ed602831c3435b03e33669d0f59e29c939764f1b11591175f2fe615",
   109  	}
   110  	// In the test case:
   111  	//  - an account with "nil" private key will be created with
   112  	//    keys, address, and initialized with the given balance.
   113  	// - an account with exiting private key will load exiting
   114  	//   balance into test case.
   115  	getSimpleTransferTests = []simpleTransferTestCfg{
   116  		{
   117  			AcntCreate, nil, big.NewInt(1000000),
   118  			AcntCreate, nil, big.NewInt(1000000),
   119  			1, big.NewInt(100), // nonce, amount
   120  			make([]byte, 100),             //payload
   121  			uint64(200000), big.NewInt(1), // gasLimit, gasPrice
   122  			TsfSuccess, "",
   123  			"Normal transfer from an account with enough balance and gas",
   124  		},
   125  		{
   126  			AcntCreate, nil, big.NewInt(232222),
   127  			AcntCreate, nil, big.NewInt(100000),
   128  			1, big.NewInt(222222),
   129  			make([]byte, 0),
   130  			uint64(200000), big.NewInt(1),
   131  			TsfSuccess, "",
   132  			"Transfer with just enough balance",
   133  		},
   134  		{
   135  			AcntCreate, nil, big.NewInt(1000000),
   136  			AcntNotRegistered, nil, big.NewInt(1000000),
   137  			1, big.NewInt(100), // nonce, amount
   138  			make([]byte, 100),             //payload
   139  			uint64(200000), big.NewInt(1), // gasLimit, gasPrice
   140  			TsfSuccess, "",
   141  			"Normal transfer to an address not created on block chain",
   142  		},
   143  		{
   144  			AcntCreate, nil, big.NewInt(100000),
   145  			AcntCreate, nil, big.NewInt(100000),
   146  			1, big.NewInt(0),
   147  			make([]byte, 4),
   148  			uint64(200000), big.NewInt(1),
   149  			TsfSuccess, "",
   150  			"Transfer with 0 amount",
   151  		},
   152  		{
   153  			AcntExist, identityset.PrivateKey(0), big.NewInt(100000),
   154  			AcntCreate, nil, big.NewInt(100000),
   155  			1, big.NewInt(100),
   156  			make([]byte, 4),
   157  			uint64(200000), big.NewInt(1),
   158  			TsfSuccess, "",
   159  			"Transfer with same nonce from a single sender 1",
   160  		},
   161  		{
   162  			AcntExist, identityset.PrivateKey(1), big.NewInt(100000),
   163  			AcntCreate, nil, big.NewInt(100000),
   164  			2, big.NewInt(100),
   165  			make([]byte, 4),
   166  			uint64(200000), big.NewInt(1),
   167  			TsfPending, "",
   168  			"Transfer with a sequence of nonce from a single sender 1",
   169  		},
   170  		{
   171  			AcntExist, identityset.PrivateKey(1), big.NewInt(100000),
   172  			AcntCreate, nil, big.NewInt(100000),
   173  			3, big.NewInt(100),
   174  			make([]byte, 4),
   175  			uint64(200000), big.NewInt(1),
   176  			TsfPending, "",
   177  			"Transfer with a sequence of nonce from a single sender 2",
   178  		},
   179  		{
   180  			AcntExist, getLocalKey(0), big.NewInt(30000),
   181  			AcntCreate, nil, big.NewInt(100000),
   182  			2, big.NewInt(20000),
   183  			make([]byte, 0),
   184  			uint64(200000), big.NewInt(0),
   185  			TsfPending, "",
   186  			"Transfer to multiple accounts with not enough total balance 1",
   187  		},
   188  		{
   189  			AcntExist, getLocalKey(0), big.NewInt(30000),
   190  			AcntCreate, nil, big.NewInt(100000),
   191  			3, big.NewInt(20000),
   192  			make([]byte, 4),
   193  			uint64(200000), big.NewInt(0),
   194  			TsfPending, "",
   195  			"Transfer to multiple accounts with not enough total balance 2",
   196  		},
   197  		{
   198  			AcntCreate, nil, big.NewInt(1000000),
   199  			AcntBadAddr, nil, big.NewInt(1000000),
   200  			1, big.NewInt(100), // nonce, amount
   201  			make([]byte, 100),             //payload
   202  			uint64(200000), big.NewInt(1), // gasLimit, gasPrice
   203  			TsfFail, "Unknown",
   204  			"Normal transfer to a bad address",
   205  		},
   206  		{
   207  			AcntNotRegistered, nil, big.NewInt(1000000),
   208  			AcntCreate, nil, big.NewInt(1000000),
   209  			1, big.NewInt(100), // nonce, amount
   210  			make([]byte, 100),             //payload
   211  			uint64(200000), big.NewInt(1), // gasLimit, gasPrice
   212  			TsfFail, action.ErrInsufficientFunds.Error(),
   213  			"Normal transfer from an address not created on block chain",
   214  		},
   215  		{
   216  			AcntCreate, nil, big.NewInt(232221),
   217  			AcntCreate, nil, big.NewInt(100000),
   218  			1, big.NewInt(222222),
   219  			make([]byte, 0),
   220  			uint64(200000), big.NewInt(1),
   221  			TsfFail, action.ErrInsufficientFunds.Error(),
   222  			"Transfer with not enough balance",
   223  		},
   224  		{
   225  			AcntCreate, nil, big.NewInt(232222),
   226  			AcntCreate, nil, big.NewInt(100000),
   227  			1, big.NewInt(222222),
   228  			make([]byte, 4),
   229  			uint64(200000), big.NewInt(1),
   230  			TsfFail, action.ErrInsufficientFunds.Error(),
   231  			"Transfer with not enough balance with payload",
   232  		},
   233  		{
   234  			AcntCreate, nil, big.NewInt(100000),
   235  			AcntCreate, nil, big.NewInt(100000),
   236  			1, big.NewInt(-100),
   237  			make([]byte, 4),
   238  			uint64(200000), big.NewInt(1),
   239  			TsfFail, "negative value",
   240  			"Transfer with negative amount",
   241  		},
   242  		{
   243  			AcntCreate, nil, big.NewInt(1000000),
   244  			AcntCreate, nil, big.NewInt(1000000),
   245  			1, big.NewInt(100),
   246  			make([]byte, 0),
   247  			uint64(1000), big.NewInt(1),
   248  			TsfFail, action.ErrIntrinsicGas.Error(),
   249  			"Transfer with not enough gas limit",
   250  		},
   251  		{
   252  			AcntCreate, nil, big.NewInt(100000),
   253  			AcntCreate, nil, big.NewInt(100000),
   254  			0, big.NewInt(0),
   255  			make([]byte, 4),
   256  			uint64(200000), big.NewInt(1),
   257  			TsfFail, "nonce too low",
   258  			"Transfer with nonce 0",
   259  		},
   260  		{
   261  			AcntExist, identityset.PrivateKey(0), big.NewInt(100000),
   262  			AcntCreate, nil, big.NewInt(100000),
   263  			1, big.NewInt(100),
   264  			make([]byte, 4),
   265  			uint64(200000), big.NewInt(1),
   266  			TsfFail, "nonce too low",
   267  			"Transfer with same nonce from a single sender 2",
   268  		},
   269  		{
   270  			AcntExist, identityset.PrivateKey(1), big.NewInt(100000),
   271  			AcntCreate, nil, big.NewInt(100000),
   272  			1, big.NewInt(100),
   273  			make([]byte, 4),
   274  			uint64(200000), big.NewInt(1),
   275  			TsfFinal, "",
   276  			"Transfer with a sequence of nonce from a single sender 3",
   277  		},
   278  		{
   279  			AcntExist, getLocalKey(0), big.NewInt(30000),
   280  			AcntCreate, nil, big.NewInt(100000),
   281  			1, big.NewInt(20000),
   282  			make([]byte, 4),
   283  			uint64(200000), big.NewInt(0),
   284  			TsfFinal, "",
   285  			"Transfer to multiple accounts with not enough total balance 3",
   286  		},
   287  	}
   288  )
   289  
   290  func TestLocalTransfer(t *testing.T) {
   291  	require := require.New(t)
   292  
   293  	testTriePath, err := testutil.PathOfTempFile("trie")
   294  	require.NoError(err)
   295  	testDBPath, err := testutil.PathOfTempFile("db")
   296  	require.NoError(err)
   297  	testIndexPath, err := testutil.PathOfTempFile("index")
   298  	require.NoError(err)
   299  	testBloomfilterIndexPath, err := testutil.PathOfTempFile("bloomfilterIndex")
   300  	require.NoError(err)
   301  	testSystemLogPath, err := testutil.PathOfTempFile("systemlog")
   302  	require.NoError(err)
   303  	testCandidateIndexPath, err := testutil.PathOfTempFile("candidateIndex")
   304  	require.NoError(err)
   305  	testContractStakeIndexPath, err := testutil.PathOfTempFile("contractStakeIndex")
   306  	require.NoError(err)
   307  	sgdIndexDBPath, err := testutil.PathOfTempFile("sgdIndex")
   308  	require.NoError(err)
   309  
   310  	defer func() {
   311  		testutil.CleanupPath(testTriePath)
   312  		testutil.CleanupPath(testDBPath)
   313  		testutil.CleanupPath(testIndexPath)
   314  		testutil.CleanupPath(testSystemLogPath)
   315  		testutil.CleanupPath(testBloomfilterIndexPath)
   316  		testutil.CleanupPath(testCandidateIndexPath)
   317  		testutil.CleanupPath(testContractStakeIndexPath)
   318  		testutil.CleanupPath(sgdIndexDBPath)
   319  	}()
   320  
   321  	networkPort := 4689
   322  	apiPort := testutil.RandomPort()
   323  	cfg, err := newTransferConfig(testDBPath, testTriePath, testIndexPath, sgdIndexDBPath, testBloomfilterIndexPath, testSystemLogPath, testCandidateIndexPath, testContractStakeIndexPath, networkPort, apiPort)
   324  	defer func() {
   325  		delete(cfg.Plugins, config.GatewayPlugin)
   326  	}()
   327  	require.NoError(err)
   328  
   329  	for i, tsfTest := range getSimpleTransferTests {
   330  		if tsfTest.senderAcntState == AcntCreate {
   331  			sk, err := crypto.GenerateKey()
   332  			require.NoError(err)
   333  			addr := sk.PublicKey().Address()
   334  			require.NotNil(addr)
   335  			cfg.Genesis.InitBalanceMap[addr.String()] = tsfTest.senderBalance.String()
   336  			getSimpleTransferTests[i].senderPriKey = sk
   337  		}
   338  		if tsfTest.recvAcntState == AcntCreate {
   339  			sk, err := crypto.GenerateKey()
   340  			require.NoError(err)
   341  			addr := sk.PublicKey().Address()
   342  			require.NotNil(addr)
   343  			cfg.Genesis.InitBalanceMap[addr.String()] = tsfTest.recvBalance.String()
   344  			getSimpleTransferTests[i].recvPriKey = sk
   345  		}
   346  	}
   347  	for i := 0; i < len(localKeys); i++ {
   348  		sk := getLocalKey(i)
   349  		addr := sk.PublicKey().Address()
   350  		require.NotNil(addr)
   351  		cfg.Genesis.InitBalanceMap[addr.String()] = "30000"
   352  	}
   353  
   354  	// create server
   355  	svr, err := itx.NewServer(cfg)
   356  	require.NoError(err)
   357  
   358  	// Create and start probe server
   359  	ctx := genesis.WithGenesisContext(context.Background(), cfg.Genesis)
   360  	probeSvr := probe.New(7788)
   361  	require.NoError(probeSvr.Start(ctx))
   362  
   363  	// Start server
   364  	ctx, stopServer := context.WithCancel(ctx)
   365  	defer func() {
   366  		require.NoError(probeSvr.Stop(ctx))
   367  		stopServer()
   368  	}()
   369  
   370  	go itx.StartServer(ctx, svr, probeSvr, cfg)
   371  
   372  	// target address for grpc connection. Default is "127.0.0.1:14014"
   373  	grpcAddr := fmt.Sprintf("127.0.0.1:%d", apiPort)
   374  	grpcctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
   375  	defer cancel()
   376  	conn, err := grpc.DialContext(grpcctx, grpcAddr, grpc.WithBlock(), grpc.WithInsecure())
   377  	require.NoError(err)
   378  	defer conn.Close()
   379  	client := iotexapi.NewAPIServiceClient(conn)
   380  
   381  	chainID := cfg.Chain.ID
   382  	bc := svr.ChainService(chainID).Blockchain()
   383  	sf := svr.ChainService(chainID).StateFactory()
   384  	ap := svr.ChainService(chainID).ActionPool()
   385  	as := svr.APIServer(chainID)
   386  
   387  	for _, tsfTest := range getSimpleTransferTests {
   388  		senderPriKey, senderAddr, err := initStateKeyAddr(tsfTest.senderAcntState, tsfTest.senderPriKey, tsfTest.senderBalance, bc, sf)
   389  		require.NoError(err, tsfTest.message)
   390  
   391  		_, recvAddr, err := initStateKeyAddr(tsfTest.recvAcntState, tsfTest.recvPriKey, tsfTest.recvBalance, bc, sf)
   392  		require.NoError(err, tsfTest.message)
   393  
   394  		tsf, err := action.SignedTransfer(recvAddr, senderPriKey, tsfTest.nonce, tsfTest.amount,
   395  			tsfTest.payload, tsfTest.gasLimit, tsfTest.gasPrice)
   396  		require.NoError(err, tsfTest.message)
   397  
   398  		// wait 2 block time, retry 5 times
   399  		retryInterval := cfg.Genesis.BlockInterval * 2 / 5
   400  		bo := backoff.WithMaxRetries(backoff.NewConstantBackOff(retryInterval), 5)
   401  		err = backoff.Retry(func() error {
   402  			_, err := client.SendAction(context.Background(), &iotexapi.SendActionRequest{Action: tsf.Proto()})
   403  			return err
   404  		}, bo)
   405  		switch tsfTest.expectedResult {
   406  		case TsfSuccess:
   407  			require.NoError(err, tsfTest.message)
   408  			// Wait long enough for a block to be minted, and check the balance of both
   409  			// sender and receiver.
   410  			var actInfo *iotexapi.ActionInfo
   411  			err := backoff.Retry(func() error {
   412  				var err error
   413  				tsfHash, err1 := tsf.Hash()
   414  				if err1 != nil {
   415  					return err1
   416  				}
   417  				actInfo, err = util.GetActionByActionHash(as.CoreService(), tsfHash)
   418  				if err != nil {
   419  					return err
   420  				}
   421  				return err
   422  			}, bo)
   423  			require.NoError(err, tsfTest.message)
   424  			require.Equal(tsfTest.nonce, actInfo.GetAction().GetCore().GetNonce(), tsfTest.message)
   425  			require.Equal(senderPriKey.PublicKey().Bytes(), actInfo.GetAction().SenderPubKey, tsfTest.message)
   426  
   427  			senderAddr1, err := address.FromString(senderAddr)
   428  			require.NoError(err)
   429  			newSenderState, _ := accountutil.AccountState(ctx, sf, senderAddr1)
   430  			minusAmount := big.NewInt(0).Sub(tsfTest.senderBalance, tsfTest.amount)
   431  			gasUnitPayloadConsumed := big.NewInt(0).Mul(big.NewInt(int64(action.TransferPayloadGas)),
   432  				big.NewInt(int64(len(tsfTest.payload))))
   433  			gasUnitTransferConsumed := big.NewInt(int64(action.TransferBaseIntrinsicGas))
   434  			gasUnitConsumed := big.NewInt(0).Add(gasUnitPayloadConsumed, gasUnitTransferConsumed)
   435  			gasConsumed := big.NewInt(0).Mul(gasUnitConsumed, tsfTest.gasPrice)
   436  			expectedSenderBalance := big.NewInt(0).Sub(minusAmount, gasConsumed)
   437  			require.Equal(expectedSenderBalance.String(), newSenderState.Balance.String(), tsfTest.message)
   438  
   439  			recvAddr1, err := address.FromString(recvAddr)
   440  			require.NoError(err)
   441  			newRecvState, err := accountutil.AccountState(ctx, sf, recvAddr1)
   442  			require.NoError(err)
   443  			expectedRecvrBalance := big.NewInt(0)
   444  			if tsfTest.recvAcntState == AcntNotRegistered {
   445  				expectedRecvrBalance.Set(tsfTest.amount)
   446  			} else {
   447  				expectedRecvrBalance.Add(tsfTest.recvBalance, tsfTest.amount)
   448  			}
   449  			require.Equal(expectedRecvrBalance.String(), newRecvState.Balance.String(), tsfTest.message)
   450  		case TsfFail:
   451  			// bad address will be in actpool, but won't pass validation in protocol execution
   452  			if tsfTest.recvAcntState != AcntBadAddr {
   453  				require.Error(err, tsfTest.message)
   454  
   455  				st, ok := status.FromError(err)
   456  				require.True(ok, tsfTest.message)
   457  				require.Equal(st.Code(), codes.Internal, tsfTest.message)
   458  
   459  				details := st.Details()
   460  				require.Equal(len(details), 1, tsfTest.message)
   461  
   462  				detail, ok := details[0].(*errdetails.BadRequest)
   463  				require.True(ok, tsfTest.message)
   464  				require.Equal(len(detail.FieldViolations), 1, tsfTest.message)
   465  
   466  				violation := detail.FieldViolations[0]
   467  				require.Equal(tsfTest.expectedDesc, violation.Description, tsfTest.message)
   468  				require.Equal(violation.Field, "Action rejected", tsfTest.message)
   469  
   470  				//The transfer should be rejected right after we inject it
   471  				//Wait long enough to make sure the failed transfer does not exit in either action pool or blockchain
   472  				err := backoff.Retry(func() error {
   473  					var err error
   474  					tsfHash, err1 := tsf.Hash()
   475  					if err1 != nil {
   476  						return err1
   477  					}
   478  					_, err = ap.GetActionByHash(tsfHash)
   479  					return err
   480  				}, bo)
   481  				require.Error(err, tsfTest.message)
   482  			}
   483  			tsfHash, err1 := tsf.Hash()
   484  			require.NoError(err1)
   485  			_, err = util.GetActionByActionHash(as.CoreService(), tsfHash)
   486  			require.Error(err, tsfTest.message)
   487  
   488  			if tsfTest.senderAcntState == AcntCreate || tsfTest.senderAcntState == AcntExist {
   489  				senderAddr1, err := address.FromString(senderAddr)
   490  				require.NoError(err)
   491  				newSenderState, _ := accountutil.AccountState(ctx, sf, senderAddr1)
   492  				require.Equal(tsfTest.senderBalance.String(), newSenderState.Balance.String())
   493  			}
   494  
   495  		case TsfPending:
   496  			require.NoError(err, tsfTest.message)
   497  			//Need to wait long enough to make sure the pending transfer is not minted, only stay in action pool
   498  			err := backoff.Retry(func() error {
   499  				var err error
   500  				tsfHash, err1 := tsf.Hash()
   501  				if err1 != nil {
   502  					return err1
   503  				}
   504  				_, err = ap.GetActionByHash(tsfHash)
   505  				return err
   506  			}, bo)
   507  			require.NoError(err, tsfTest.message)
   508  			tsfHash, err1 := tsf.Hash()
   509  			require.NoError(err1)
   510  			_, err = util.GetActionByActionHash(as.CoreService(), tsfHash)
   511  			require.Error(err, tsfTest.message)
   512  		case TsfFinal:
   513  			require.NoError(err, tsfTest.message)
   514  			//After a blocked is minted, check all the pending transfers in action pool are cleared
   515  			//This checking procedure is simplified for this test case, because of the complexity of
   516  			//handling pending transfers.
   517  			time.Sleep(cfg.Genesis.BlockInterval + time.Second)
   518  			require.Equal(0, lenPendingActionMap(ap.PendingActionMap()), tsfTest.message)
   519  
   520  		default:
   521  			require.True(false, tsfTest.message)
   522  
   523  		}
   524  	}
   525  }
   526  
   527  // initStateKeyAddr, if the given private key is nil,
   528  // creates key, address, and init the new account with given balance
   529  // otherwise, calculate the the address, and load test with existing
   530  // balance state.
   531  func initStateKeyAddr(
   532  	accountState AccountState,
   533  	privateKey crypto.PrivateKey,
   534  	initBalance *big.Int,
   535  	bc blockchain.Blockchain,
   536  	sf factory.Factory,
   537  ) (crypto.PrivateKey, string, error) {
   538  	retKey := privateKey
   539  	retAddr := ""
   540  	switch accountState {
   541  	case AcntCreate:
   542  		addr := retKey.PublicKey().Address()
   543  		if addr == nil {
   544  			return nil, "", errors.New("failed to get address")
   545  		}
   546  		retAddr = addr.String()
   547  
   548  	case AcntExist:
   549  		addr := retKey.PublicKey().Address()
   550  		if addr == nil {
   551  			return nil, "", errors.New("failed to get address")
   552  		}
   553  		retAddr = addr.String()
   554  		ctx := genesis.WithGenesisContext(context.Background(), bc.Genesis())
   555  		existState, err := accountutil.AccountState(ctx, sf, addr)
   556  		if err != nil {
   557  			return nil, "", err
   558  		}
   559  		initBalance.Set(existState.Balance)
   560  	case AcntNotRegistered:
   561  		sk, err := crypto.GenerateKey()
   562  		if err != nil {
   563  			return nil, "", err
   564  		}
   565  		addr := sk.PublicKey().Address()
   566  		if addr == nil {
   567  			return nil, "", errors.New("failed to get address")
   568  		}
   569  		retAddr = addr.String()
   570  		retKey = sk
   571  	case AcntBadAddr:
   572  		rand.Seed(time.Now().UnixNano())
   573  		b := make([]byte, 41)
   574  		for i := range b {
   575  			b[i] = byte(65 + rand.Intn(26))
   576  		}
   577  		retAddr = string(b)
   578  	}
   579  	return retKey, retAddr, nil
   580  }
   581  
   582  func getLocalKey(i int) crypto.PrivateKey {
   583  	sk, _ := crypto.HexStringToPrivateKey(localKeys[i])
   584  	return sk
   585  }
   586  
   587  func newTransferConfig(
   588  	chainDBPath,
   589  	trieDBPath,
   590  	indexDBPath string,
   591  	sgdIndexDBPath string,
   592  	bloomfilterIndex string,
   593  	systemLogDBPath string,
   594  	candidateIndexDBPath string,
   595  	contractstakeIndexDBPath string,
   596  	networkPort,
   597  	apiPort int,
   598  ) (config.Config, error) {
   599  
   600  	cfg := config.Default
   601  	cfg.Plugins[config.GatewayPlugin] = true
   602  	cfg.Network.Port = networkPort
   603  	cfg.Chain.ID = 1
   604  	cfg.Chain.ChainDBPath = chainDBPath
   605  	cfg.Chain.TrieDBPath = trieDBPath
   606  	cfg.Chain.TrieDBPatchFile = ""
   607  	cfg.Chain.IndexDBPath = indexDBPath
   608  	cfg.Chain.SGDIndexDBPath = sgdIndexDBPath
   609  	cfg.Chain.BloomfilterIndexDBPath = bloomfilterIndex
   610  	cfg.System.SystemLogDBPath = systemLogDBPath
   611  	cfg.Chain.CandidateIndexDBPath = candidateIndexDBPath
   612  	cfg.Chain.ContractStakingIndexDBPath = contractstakeIndexDBPath
   613  	cfg.Chain.EnableAsyncIndexWrite = true
   614  	cfg.ActPool.MinGasPriceStr = "0"
   615  	cfg.Consensus.Scheme = config.StandaloneScheme
   616  	cfg.API.GRPCPort = apiPort
   617  	cfg.Genesis.BlockInterval = 800 * time.Millisecond
   618  
   619  	return cfg, nil
   620  }
   621  
   622  func lenPendingActionMap(acts map[string][]*action.SealedEnvelope) int {
   623  	l := 0
   624  	for _, part := range acts {
   625  		l += len(part)
   626  	}
   627  	return l
   628  }
   629  
   630  func TestEnforceChainID(t *testing.T) {
   631  	require := require.New(t)
   632  
   633  	testCase := []struct {
   634  		chainID uint32
   635  		nonce   uint64
   636  		success bool
   637  	}{
   638  		{
   639  			1, 1, true, // tx chainID = node chainID, height < KamchatkaHeight
   640  		},
   641  		{
   642  			2, 2, true, // tx chainID != node chainID, height < KamchatkaHeight
   643  		},
   644  		{
   645  			1, 3, true, // tx chainID = node chainID, height = KamchatkaHeight
   646  		},
   647  		{
   648  			1, 4, true, // tx chainID = node chainID, height > KamchatkaHeight
   649  		},
   650  		{
   651  			2, 5, false, // tx chainID != node chainID, height > KamchatkaHeight
   652  		},
   653  		{
   654  			0, 5, true, // tx chainID = 0, height < QuebecHeight, OK
   655  		},
   656  		{
   657  			0, 6, false, // tx chainID = 0, height = QuebecHeight, reject
   658  		},
   659  	}
   660  
   661  	ctx := context.Background()
   662  	cfg := config.Default
   663  	cfg.Genesis.BlockGasLimit = uint64(100000)
   664  	cfg.Genesis.MidwayBlockHeight = 3
   665  	cfg.Genesis.QuebecBlockHeight = 7
   666  	registry := protocol.NewRegistry()
   667  	acc := account.NewProtocol(rewarding.DepositGas)
   668  	require.NoError(acc.Register(registry))
   669  	factoryCfg := factory.GenerateConfig(cfg.Chain, cfg.Genesis)
   670  	sf, err := factory.NewFactory(factoryCfg, db.NewMemKVStore(), factory.RegistryOption(registry))
   671  	require.NoError(err)
   672  	ap, err := actpool.NewActPool(cfg.Genesis, sf, cfg.ActPool)
   673  	require.NoError(err)
   674  	store, err := filedao.NewFileDAOInMemForTest()
   675  	require.NoError(err)
   676  	blkMemDao := blockdao.NewBlockDAOWithIndexersAndCache(store, []blockdao.BlockIndexer{sf}, cfg.DB.MaxCacheSize)
   677  	bc := blockchain.NewBlockchain(
   678  		cfg.Chain,
   679  		cfg.Genesis,
   680  		blkMemDao,
   681  		factory.NewMinter(sf, ap),
   682  	)
   683  	require.NoError(bc.Start(ctx))
   684  
   685  	defer func() {
   686  		require.NoError(bc.Stop(ctx))
   687  	}()
   688  	for i, c := range testCase {
   689  		tsf, err := action.NewTransfer(
   690  			uint64(i)+1,
   691  			big.NewInt(100),
   692  			identityset.Address(27).String(),
   693  			[]byte{}, uint64(100000),
   694  			big.NewInt(1).Mul(big.NewInt(int64(i)+10), big.NewInt(unit.Qev)),
   695  		)
   696  		require.NoError(err)
   697  
   698  		bd := &action.EnvelopeBuilder{}
   699  		elp1 := bd.SetAction(tsf).
   700  			SetChainID(c.chainID).
   701  			SetNonce(c.nonce).
   702  			SetGasLimit(100000).
   703  			SetGasPrice(big.NewInt(1).Mul(big.NewInt(int64(i)+10), big.NewInt(unit.Qev))).Build()
   704  		selp, err := action.Sign(elp1, identityset.PrivateKey(0))
   705  		require.NoError(err)
   706  
   707  		// simulate API receives tx
   708  		selp1, err := (&action.Deserializer{}).SetEvmNetworkID(cfg.Chain.EVMNetworkID).ActionToSealedEnvelope(selp.Proto())
   709  		require.NoError(err)
   710  
   711  		// mint block using received tx
   712  		require.NoError(ap.Add(context.Background(), selp1))
   713  		blk, err := bc.MintNewBlock(testutil.TimestampNow())
   714  		require.NoError(err)
   715  		require.NoError(bc.CommitBlock(blk))
   716  		require.Equal(c.success, len(blk.Actions) == 1)
   717  		require.Equal(c.success, len(blk.Receipts) == 1)
   718  
   719  		// verify action has valid chainID
   720  		if c.success {
   721  			act := blk.Actions[0]
   722  			tsf, ok := act.Action().(*action.Transfer)
   723  			require.True(ok)
   724  			require.Equal(c.chainID, act.ChainID())
   725  			require.Equal(c.chainID, tsf.ChainID())
   726  		}
   727  	}
   728  }