github.com/klaytn/klaytn@v1.12.1/blockchain/block_validator_test.go (about)

     1  // Modifications Copyright 2018 The klaytn Authors
     2  // Copyright 2015 The go-ethereum Authors
     3  // This file is part of the go-ethereum library.
     4  //
     5  // The go-ethereum library is free software: you can redistribute it and/or modify
     6  // it under the terms of the GNU Lesser General Public License as published by
     7  // the Free Software Foundation, either version 3 of the License, or
     8  // (at your option) any later version.
     9  //
    10  // The go-ethereum library is distributed in the hope that it will be useful,
    11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    13  // GNU Lesser General Public License for more details.
    14  //
    15  // You should have received a copy of the GNU Lesser General Public License
    16  // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
    17  //
    18  // This file is derived from core/block_validator_test.go (2018/06/04).
    19  // Modified and improved for the klaytn development.
    20  
    21  package blockchain
    22  
    23  import (
    24  	"math/big"
    25  	"runtime"
    26  	"testing"
    27  	"time"
    28  
    29  	"github.com/klaytn/klaytn/blockchain/types"
    30  	"github.com/klaytn/klaytn/blockchain/vm"
    31  	"github.com/klaytn/klaytn/common"
    32  	"github.com/klaytn/klaytn/consensus/gxhash"
    33  	"github.com/klaytn/klaytn/crypto"
    34  	"github.com/klaytn/klaytn/params"
    35  	"github.com/klaytn/klaytn/storage/database"
    36  	"github.com/stretchr/testify/assert"
    37  )
    38  
    39  // Tests that simple header verification works, for both good and bad blocks.
    40  func TestHeaderVerification(t *testing.T) {
    41  	// Create a simple chain to verify
    42  	var (
    43  		testdb    = database.NewMemoryDBManager()
    44  		gspec     = &Genesis{Config: params.TestChainConfig}
    45  		genesis   = gspec.MustCommit(testdb)
    46  		blocks, _ = GenerateChain(params.TestChainConfig, genesis, gxhash.NewFaker(), testdb, 8, nil)
    47  	)
    48  	headers := make([]*types.Header, len(blocks))
    49  	for i, block := range blocks {
    50  		headers[i] = block.Header()
    51  	}
    52  	// Run the header checker for blocks one-by-one, checking for both valid and invalid nonces
    53  	chain, _ := NewBlockChain(testdb, nil, params.TestChainConfig, gxhash.NewFaker(), vm.Config{})
    54  	defer chain.Stop()
    55  
    56  	for i := 0; i < len(blocks); i++ {
    57  		for j, valid := range []bool{true, false} {
    58  			var results <-chan error
    59  
    60  			if valid {
    61  				engine := gxhash.NewFaker()
    62  				_, results = engine.VerifyHeaders(chain, []*types.Header{headers[i]}, []bool{true})
    63  			} else {
    64  				engine := gxhash.NewFakeFailer(headers[i].Number.Uint64())
    65  				_, results = engine.VerifyHeaders(chain, []*types.Header{headers[i]}, []bool{true})
    66  			}
    67  			// Wait for the verification result
    68  			select {
    69  			case result := <-results:
    70  				if (result == nil) != valid {
    71  					t.Errorf("test %d.%d: validity mismatch: have %v, want %v", i, j, result, valid)
    72  				}
    73  			case <-time.After(time.Second):
    74  				t.Fatalf("test %d.%d: verification timeout", i, j)
    75  			}
    76  			// Make sure no more data is returned
    77  			select {
    78  			case result := <-results:
    79  				t.Fatalf("test %d.%d: unexpected result returned: %v", i, j, result)
    80  			case <-time.After(25 * time.Millisecond):
    81  			}
    82  		}
    83  		chain.InsertChain(blocks[i : i+1])
    84  	}
    85  }
    86  
    87  func TestVerifyBlockBody(t *testing.T) {
    88  	testcases := []struct {
    89  		baseFee *big.Int
    90  		txData  types.TxInternalData
    91  		err     bool
    92  	}{
    93  		{
    94  			big.NewInt(100),
    95  			&types.TxInternalDataLegacy{
    96  				GasLimit: 1,
    97  				Price:    big.NewInt(200),
    98  				Payload:  []byte("abcdef"),
    99  			},
   100  			false,
   101  		},
   102  
   103  		{
   104  			big.NewInt(100),
   105  			&types.TxInternalDataEthereumDynamicFee{
   106  				ChainID:   big.NewInt(1),
   107  				GasLimit:  123457,
   108  				GasFeeCap: big.NewInt(10),
   109  				GasTipCap: big.NewInt(10),
   110  				Payload:   []byte("abcdef"),
   111  			},
   112  			true,
   113  		},
   114  		{
   115  			big.NewInt(100),
   116  			&types.TxInternalDataEthereumDynamicFee{
   117  				ChainID:   big.NewInt(1),
   118  				GasLimit:  123457,
   119  				GasFeeCap: big.NewInt(100),
   120  				GasTipCap: big.NewInt(10),
   121  				Payload:   []byte("abcdef"),
   122  			},
   123  			false,
   124  		},
   125  		{
   126  			big.NewInt(100),
   127  			&types.TxInternalDataEthereumDynamicFee{
   128  				ChainID:   big.NewInt(1),
   129  				GasLimit:  123457,
   130  				GasFeeCap: big.NewInt(200),
   131  				GasTipCap: big.NewInt(10),
   132  			},
   133  			false,
   134  		},
   135  	}
   136  
   137  	// Create a simple chain to verify
   138  	var (
   139  		testdb  = database.NewMemoryDBManager()
   140  		gspec   = &Genesis{Config: params.TestChainConfig}
   141  		genesis = gspec.MustCommit(testdb)
   142  	)
   143  
   144  	// We don't need istanbul instance here because validateBody is in BlockValidator instance
   145  	GenerateChain(params.TestChainConfig, genesis, gxhash.NewFaker(), testdb, 8, nil)
   146  	chain, _ := NewBlockChain(testdb, nil, params.TestChainConfig, gxhash.NewFaker(), vm.Config{})
   147  	defer chain.Stop()
   148  
   149  	// Generate a batch of accounts to start with
   150  	privKey, _ := crypto.GenerateKey()
   151  	signer := types.LatestSignerForChainID(big.NewInt(1))
   152  	var block *types.Block
   153  	for _, testcase := range testcases {
   154  		// Generate a block header
   155  		header := &types.Header{
   156  			ParentHash: chain.hc.currentHeaderHash,
   157  			Number:     common.Big1,
   158  			GasUsed:    0,
   159  			Extra:      []byte{},
   160  			BaseFee:    testcase.baseFee,
   161  		}
   162  
   163  		// Generate a block with tx
   164  		tx := types.NewTx(testcase.txData)
   165  		tx.Sign(signer, privKey)
   166  		block = types.NewBlock(header, append(types.Transactions{}, tx), nil)
   167  
   168  		err := chain.validator.ValidateBody(block)
   169  		if errExist := err != nil; errExist != testcase.err {
   170  			assert.Error(t, err)
   171  		}
   172  	}
   173  }
   174  
   175  // Tests that concurrent header verification works, for both good and bad blocks.
   176  func TestHeaderConcurrentVerification2(t *testing.T)  { testHeaderConcurrentVerification(t, 2) }
   177  func TestHeaderConcurrentVerification8(t *testing.T)  { testHeaderConcurrentVerification(t, 8) }
   178  func TestHeaderConcurrentVerification32(t *testing.T) { testHeaderConcurrentVerification(t, 32) }
   179  
   180  func testHeaderConcurrentVerification(t *testing.T, threads int) {
   181  	// Create a simple chain to verify
   182  	var (
   183  		testdb    = database.NewMemoryDBManager()
   184  		gspec     = &Genesis{Config: params.TestChainConfig}
   185  		genesis   = gspec.MustCommit(testdb)
   186  		blocks, _ = GenerateChain(params.TestChainConfig, genesis, gxhash.NewFaker(), testdb, 8, nil)
   187  	)
   188  	headers := make([]*types.Header, len(blocks))
   189  	seals := make([]bool, len(blocks))
   190  
   191  	for i, block := range blocks {
   192  		headers[i] = block.Header()
   193  		seals[i] = true
   194  	}
   195  	// Set the number of threads to verify on
   196  	old := runtime.GOMAXPROCS(threads)
   197  	defer runtime.GOMAXPROCS(old)
   198  
   199  	// Run the header checker for the entire block chain at once both for a valid and
   200  	// also an invalid chain (enough if one arbitrary block is invalid).
   201  	for i, valid := range []bool{true, false} {
   202  		var results <-chan error
   203  
   204  		if valid {
   205  			chain, _ := NewBlockChain(testdb, nil, params.TestChainConfig, gxhash.NewFaker(), vm.Config{})
   206  			_, results = chain.engine.VerifyHeaders(chain, headers, seals)
   207  			chain.Stop()
   208  		} else {
   209  			chain, _ := NewBlockChain(testdb, nil, params.TestChainConfig, gxhash.NewFakeFailer(uint64(len(headers)-1)), vm.Config{})
   210  			_, results = chain.engine.VerifyHeaders(chain, headers, seals)
   211  			chain.Stop()
   212  		}
   213  		// Wait for all the verification results
   214  		checks := make(map[int]error)
   215  		for j := 0; j < len(blocks); j++ {
   216  			select {
   217  			case result := <-results:
   218  				checks[j] = result
   219  
   220  			case <-time.After(time.Second):
   221  				t.Fatalf("test %d.%d: verification timeout", i, j)
   222  			}
   223  		}
   224  		// Check nonce check validity
   225  		for j := 0; j < len(blocks); j++ {
   226  			want := valid || (j < len(blocks)-2) // We chose the last-but-one nonce in the chain to fail
   227  			if (checks[j] == nil) != want {
   228  				t.Errorf("test %d.%d: validity mismatch: have %v, want %v", i, j, checks[j], want)
   229  			}
   230  			if !want {
   231  				// A few blocks after the first error may pass verification due to concurrent
   232  				// workers. We don't care about those in this test, just that the correct block
   233  				// errors out.
   234  				break
   235  			}
   236  		}
   237  		// Make sure no more data is returned
   238  		select {
   239  		case result := <-results:
   240  			t.Fatalf("test %d: unexpected result returned: %v", i, result)
   241  		case <-time.After(25 * time.Millisecond):
   242  		}
   243  	}
   244  }
   245  
   246  // Tests that aborting a header validation indeed prevents further checks from being
   247  // run, as well as checks that no left-over goroutines are leaked.
   248  func TestHeaderConcurrentAbortion2(t *testing.T)  { testHeaderConcurrentAbortion(t, 2) }
   249  func TestHeaderConcurrentAbortion8(t *testing.T)  { testHeaderConcurrentAbortion(t, 8) }
   250  func TestHeaderConcurrentAbortion32(t *testing.T) { testHeaderConcurrentAbortion(t, 32) }
   251  
   252  func testHeaderConcurrentAbortion(t *testing.T, threads int) {
   253  	// Create a simple chain to verify
   254  	var (
   255  		testdb    = database.NewMemoryDBManager()
   256  		gspec     = &Genesis{Config: params.TestChainConfig}
   257  		genesis   = gspec.MustCommit(testdb)
   258  		blocks, _ = GenerateChain(params.TestChainConfig, genesis, gxhash.NewFaker(), testdb, 1024, nil)
   259  	)
   260  	headers := make([]*types.Header, len(blocks))
   261  	seals := make([]bool, len(blocks))
   262  
   263  	for i, block := range blocks {
   264  		headers[i] = block.Header()
   265  		seals[i] = true
   266  	}
   267  	// Set the number of threads to verify on
   268  	old := runtime.GOMAXPROCS(threads)
   269  	defer runtime.GOMAXPROCS(old)
   270  
   271  	// Start the verifications and immediately abort
   272  	chain, _ := NewBlockChain(testdb, nil, params.TestChainConfig, gxhash.NewFakeDelayer(time.Millisecond), vm.Config{})
   273  	defer chain.Stop()
   274  
   275  	abort, results := chain.engine.VerifyHeaders(chain, headers, seals)
   276  	close(abort)
   277  
   278  	// Deplete the results channel
   279  	verified := 0
   280  	for depleted := false; !depleted; {
   281  		select {
   282  		case result := <-results:
   283  			if result != nil {
   284  				t.Errorf("header %d: validation failed: %v", verified, result)
   285  			}
   286  			verified++
   287  		case <-time.After(50 * time.Millisecond):
   288  			depleted = true
   289  		}
   290  	}
   291  	// Check that abortion was honored by not processing too many POWs
   292  	if verified > 2*threads {
   293  		t.Errorf("verification count too large: have %d, want below %d", verified, 2*threads)
   294  	}
   295  }