github.com/klaytn/klaytn@v1.12.1/tests/race_test.go (about)

     1  // Copyright 2021 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  //go:build race
    18  // +build race
    19  
    20  package tests
    21  
    22  import (
    23  	"fmt"
    24  	"math/big"
    25  	"os"
    26  	"testing"
    27  	"time"
    28  
    29  	"github.com/klaytn/klaytn/blockchain"
    30  	"github.com/klaytn/klaytn/blockchain/state"
    31  	"github.com/klaytn/klaytn/blockchain/types"
    32  	"github.com/klaytn/klaytn/common"
    33  	"github.com/klaytn/klaytn/log"
    34  	"github.com/klaytn/klaytn/params"
    35  	"github.com/klaytn/klaytn/storage/database"
    36  )
    37  
    38  // TestRaceBetweenTxpoolAddAndCommitNewWork tests race conditions between `Txpool.add` and `commitNewWork`.
    39  // Since both access to txpool pending concurrently, critical sections should be protected by mutex lock.
    40  // This race test may need multiple trials and additional flags to avoid false alarms from sha3 package.
    41  // For example, `go test -gcflags=all=-d=checkptr=0 -race -run TestRaceBetweenTxpoolAddAndCommitNewWork`.
    42  func TestRaceBetweenTxpoolAddAndCommitNewWork(t *testing.T) {
    43  	log.EnableLogForTest(log.LvlCrit, log.LvlTrace)
    44  
    45  	numAccounts := 2
    46  	fullNode, node, validator, chainId, workspace := newBlockchain(t)
    47  	defer os.RemoveAll(workspace)
    48  
    49  	// create account
    50  	richAccount, accounts, _ := createAccount(t, numAccounts, validator)
    51  
    52  	quitCh := make(chan struct{})
    53  	iterNum := 1000
    54  
    55  	go func() {
    56  		var txList []*types.Transaction
    57  		for i := 0; i < iterNum; i++ {
    58  			{
    59  				tx, _, err := generateDefaultTx(richAccount, accounts[1], types.TxTypeValueTransfer, common.Address{})
    60  				if err != nil {
    61  					t.Fatal(err)
    62  				}
    63  				signer := types.LatestSignerForChainID(chainId)
    64  				if err := tx.Sign(signer, richAccount.Keys[0]); err != nil {
    65  					t.Fatal(err)
    66  				}
    67  				txList = append(txList, tx)
    68  			}
    69  			{
    70  				tx, _, err := generateDefaultTx(richAccount, accounts[1], types.TxTypeCancel, common.Address{})
    71  				if err != nil {
    72  					t.Fatal(err)
    73  				}
    74  				signer := types.LatestSignerForChainID(chainId)
    75  				if err := tx.Sign(signer, richAccount.Keys[0]); err != nil {
    76  					t.Fatal(err)
    77  				}
    78  				txList = append(txList, tx)
    79  			}
    80  			richAccount.AddNonce()
    81  		}
    82  
    83  		for _, tx := range txList {
    84  			if err := node.TxPool().AddLocal(tx); err != nil {
    85  				t.Fatal(err)
    86  			}
    87  		}
    88  		quitCh <- struct{}{}
    89  	}()
    90  
    91  	<-quitCh
    92  	time.Sleep(time.Second)
    93  
    94  	// stop node before ending the test code
    95  	if err := fullNode.Stop(); err != nil {
    96  		t.Fatal(err)
    97  	}
    98  }
    99  
   100  // TestRaceAsMessageWithAccountPickerForFeePayer tests calling AsMessageWithAccountPicker of a fee delegated transaction
   101  // where a fee payer may be inserted wrongly due to concurrent issue.
   102  func TestRaceAsMessageWithAccountPickerForFeePayer(t *testing.T) {
   103  	log.EnableLogForTest(log.LvlCrit, log.LvlTrace)
   104  
   105  	// Configure and generate a sample block chain
   106  	var (
   107  		gendb = database.NewMemoryDBManager()
   108  
   109  		// create a sender and a feepayer
   110  		from, _     = createAnonymousAccount("a5c9a50938a089618167c9d67dbebc0deaffc3c76ddc6b40c2777ae594389999")
   111  		feePayer, _ = createAnonymousAccount("ed580f5bd71a2ee4dae5cb43e331b7d0318596e561e6add7844271ed94156b20")
   112  
   113  		funds = new(big.Int).Mul(big.NewInt(1e16), big.NewInt(params.KLAY))
   114  		gspec = &blockchain.Genesis{
   115  			Config: params.TestChainConfig,
   116  			Alloc: blockchain.GenesisAlloc{
   117  				from.GetAddr():     {Balance: funds},
   118  				feePayer.GetAddr(): {Balance: funds},
   119  			},
   120  		}
   121  		genesis = gspec.MustCommit(gendb)
   122  		signer  = types.LatestSignerForChainID(gspec.Config.ChainID)
   123  	)
   124  
   125  	iterNum := 10000
   126  	errCh := make(chan error, 2*iterNum)
   127  
   128  	for i := 0; i < iterNum; i++ {
   129  		tx, _ := genFeeDelegatedChainDataAnchoring(t, signer, from, nil, feePayer, big.NewInt(1234))
   130  		for i := 0; i < 2; i++ {
   131  			go func() {
   132  				stateDB, err := state.New(genesis.Root(), state.NewDatabase(gendb))
   133  				if err != nil {
   134  					panic(err)
   135  				}
   136  
   137  				msg, err := tx.AsMessageWithAccountKeyPicker(signer, stateDB, 0)
   138  				if err != nil {
   139  					panic(err)
   140  				}
   141  
   142  				if msg.ValidatedFeePayer() != feePayer.GetAddr() {
   143  					errCh <- fmt.Errorf("expected: %v, actual: %v", feePayer.GetAddr().String(), msg.ValidatedFeePayer().String())
   144  				} else {
   145  					errCh <- nil
   146  				}
   147  			}()
   148  		}
   149  	}
   150  
   151  	for i := 0; i < 2*iterNum; i++ {
   152  		if err := <-errCh; err != nil {
   153  			t.Fatal(err)
   154  		}
   155  	}
   156  }