github.com/kisexp/xdchain@v0.0.0-20211206025815-490d6b732aa7/consensus/istanbul/backend/engine_test.go (about)

     1  // Copyright 2017 The go-ethereum Authors
     2  // This file is part of the go-ethereum library.
     3  //
     4  // The go-ethereum 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 go-ethereum 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 go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package backend
    18  
    19  import (
    20  	"bytes"
    21  	"crypto/ecdsa"
    22  	"math/big"
    23  	"reflect"
    24  	"testing"
    25  	"time"
    26  
    27  	"github.com/kisexp/xdchain/common"
    28  	"github.com/kisexp/xdchain/consensus"
    29  	"github.com/kisexp/xdchain/consensus/istanbul"
    30  	istanbulcommon "github.com/kisexp/xdchain/consensus/istanbul/common"
    31  	"github.com/kisexp/xdchain/consensus/istanbul/testutils"
    32  	"github.com/kisexp/xdchain/core"
    33  	"github.com/kisexp/xdchain/core/rawdb"
    34  	"github.com/kisexp/xdchain/core/types"
    35  	"github.com/kisexp/xdchain/core/vm"
    36  	"github.com/kisexp/xdchain/crypto"
    37  )
    38  
    39  func newBlockchainFromConfig(genesis *core.Genesis, nodeKeys []*ecdsa.PrivateKey, cfg *istanbul.Config) (*core.BlockChain, *Backend) {
    40  	memDB := rawdb.NewMemoryDatabase()
    41  
    42  	// Use the first key as private key
    43  	backend := New(cfg, nodeKeys[0], memDB)
    44  
    45  	backend.qbftConsensusEnabled = backend.IsQBFTConsensus()
    46  	genesis.MustCommit(memDB)
    47  
    48  	blockchain, err := core.NewBlockChain(memDB, nil, genesis.Config, backend, vm.Config{}, nil, nil, nil)
    49  	if err != nil {
    50  		panic(err)
    51  	}
    52  
    53  	backend.Start(blockchain, blockchain.CurrentBlock, blockchain.HasBadBlock)
    54  
    55  	snap, err := backend.snapshot(blockchain, 0, common.Hash{}, nil)
    56  	if err != nil {
    57  		panic(err)
    58  	}
    59  	if snap == nil {
    60  		panic("failed to get snapshot")
    61  	}
    62  	proposerAddr := snap.ValSet.GetProposer().Address()
    63  
    64  	// find proposer key
    65  	for _, key := range nodeKeys {
    66  		addr := crypto.PubkeyToAddress(key.PublicKey)
    67  		if addr.String() == proposerAddr.String() {
    68  			backend.privateKey = key
    69  			backend.address = addr
    70  		}
    71  	}
    72  
    73  	return blockchain, backend
    74  }
    75  
    76  // in this test, we can set n to 1, and it means we can process Istanbul and commit a
    77  // block by one node. Otherwise, if n is larger than 1, we have to generate
    78  // other fake events to process Istanbul.
    79  func newBlockChain(n int, qbftBlock *big.Int) (*core.BlockChain, *Backend) {
    80  	isQBFT := qbftBlock != nil && qbftBlock.Uint64() == 0
    81  	genesis, nodeKeys := testutils.GenesisAndKeys(n, isQBFT)
    82  
    83  	config := copyConfig(istanbul.DefaultConfig)
    84  
    85  	config.TestQBFTBlock = qbftBlock
    86  
    87  	return newBlockchainFromConfig(genesis, nodeKeys, config)
    88  }
    89  
    90  // copyConfig create a copy of istanbul.Config, so that changing it does not update the original
    91  func copyConfig(config *istanbul.Config) *istanbul.Config {
    92  	cpy := *config
    93  	return &cpy
    94  }
    95  
    96  func makeHeader(parent *types.Block, config *istanbul.Config) *types.Header {
    97  	header := &types.Header{
    98  		ParentHash: parent.Hash(),
    99  		Number:     parent.Number().Add(parent.Number(), common.Big1),
   100  		GasLimit:   core.CalcGasLimit(parent, parent.GasLimit(), parent.GasLimit()),
   101  		GasUsed:    0,
   102  		Time:       parent.Time() + config.BlockPeriod,
   103  		Difficulty: istanbulcommon.DefaultDifficulty,
   104  	}
   105  	return header
   106  }
   107  
   108  func makeBlock(chain *core.BlockChain, engine *Backend, parent *types.Block) *types.Block {
   109  	block := makeBlockWithoutSeal(chain, engine, parent)
   110  	stopCh := make(chan struct{})
   111  	resultCh := make(chan *types.Block, 10)
   112  	go engine.Seal(chain, block, resultCh, stopCh)
   113  	blk := <-resultCh
   114  	return blk
   115  }
   116  
   117  func makeBlockWithoutSeal(chain *core.BlockChain, engine *Backend, parent *types.Block) *types.Block {
   118  	header := makeHeader(parent, engine.config)
   119  	engine.Prepare(chain, header)
   120  	state, _, _ := chain.StateAt(parent.Root())
   121  	block, _ := engine.FinalizeAndAssemble(chain, header, state, nil, nil, nil)
   122  	return block
   123  }
   124  
   125  func TestIBFTPrepare(t *testing.T) {
   126  	chain, engine := newBlockChain(1, nil)
   127  	defer engine.Stop()
   128  	chain.Config().Istanbul.TestQBFTBlock = nil
   129  	header := makeHeader(chain.Genesis(), engine.config)
   130  	err := engine.Prepare(chain, header)
   131  	if err != nil {
   132  		t.Errorf("error mismatch: have %v, want nil", err)
   133  	}
   134  	header.ParentHash = common.StringToHash("1234567890")
   135  	err = engine.Prepare(chain, header)
   136  	if err != consensus.ErrUnknownAncestor {
   137  		t.Errorf("error mismatch: have %v, want %v", err, consensus.ErrUnknownAncestor)
   138  	}
   139  }
   140  
   141  func TestQBFTPrepare(t *testing.T) {
   142  	chain, engine := newBlockChain(1, big.NewInt(0))
   143  	defer engine.Stop()
   144  	header := makeHeader(chain.Genesis(), engine.config)
   145  	err := engine.Prepare(chain, header)
   146  	if err != nil {
   147  		t.Errorf("error mismatch: have %v, want nil", err)
   148  	}
   149  
   150  	header.ParentHash = common.StringToHash("1234567890")
   151  	err = engine.Prepare(chain, header)
   152  	if err != consensus.ErrUnknownAncestor {
   153  		t.Errorf("error mismatch: have %v, want %v", err, consensus.ErrUnknownAncestor)
   154  	}
   155  }
   156  
   157  func TestSealStopChannel(t *testing.T) {
   158  	chain, engine := newBlockChain(1, big.NewInt(0))
   159  	defer engine.Stop()
   160  	block := makeBlockWithoutSeal(chain, engine, chain.Genesis())
   161  	stop := make(chan struct{}, 1)
   162  	eventSub := engine.EventMux().Subscribe(istanbul.RequestEvent{})
   163  	eventLoop := func() {
   164  		ev := <-eventSub.Chan()
   165  		_, ok := ev.Data.(istanbul.RequestEvent)
   166  		if !ok {
   167  			t.Errorf("unexpected event comes: %v", reflect.TypeOf(ev.Data))
   168  		}
   169  		stop <- struct{}{}
   170  		eventSub.Unsubscribe()
   171  	}
   172  	resultCh := make(chan *types.Block, 10)
   173  	go func() {
   174  		err := engine.Seal(chain, block, resultCh, stop)
   175  		if err != nil {
   176  			t.Errorf("error mismatch: have %v, want nil", err)
   177  		}
   178  	}()
   179  	go eventLoop()
   180  
   181  	finalBlock := <-resultCh
   182  	if finalBlock != nil {
   183  		t.Errorf("block mismatch: have %v, want nil", finalBlock)
   184  	}
   185  }
   186  
   187  func TestSealCommittedOtherHash(t *testing.T) {
   188  	chain, engine := newBlockChain(1, big.NewInt(0))
   189  	defer engine.Stop()
   190  	block := makeBlockWithoutSeal(chain, engine, chain.Genesis())
   191  	otherBlock := makeBlockWithoutSeal(chain, engine, block)
   192  	expectedCommittedSeal := append([]byte{1, 2, 3}, bytes.Repeat([]byte{0x00}, types.IstanbulExtraSeal-3)...)
   193  
   194  	eventSub := engine.EventMux().Subscribe(istanbul.RequestEvent{})
   195  	blockOutputChannel := make(chan *types.Block)
   196  	stopChannel := make(chan struct{})
   197  
   198  	go func() {
   199  		ev := <-eventSub.Chan()
   200  		if _, ok := ev.Data.(istanbul.RequestEvent); !ok {
   201  			t.Errorf("unexpected event comes: %v", reflect.TypeOf(ev.Data))
   202  		}
   203  		if err := engine.Commit(otherBlock, [][]byte{expectedCommittedSeal}, big.NewInt(0)); err != nil {
   204  			t.Error(err.Error())
   205  		}
   206  		eventSub.Unsubscribe()
   207  	}()
   208  
   209  	go func() {
   210  		if err := engine.Seal(chain, block, blockOutputChannel, stopChannel); err != nil {
   211  			t.Error(err.Error())
   212  		}
   213  	}()
   214  
   215  	select {
   216  	case <-blockOutputChannel:
   217  		t.Error("Wrong block found!")
   218  	default:
   219  		//no block found, stop the sealing
   220  		close(stopChannel)
   221  	}
   222  
   223  	output := <-blockOutputChannel
   224  	if output != nil {
   225  		t.Error("Block not nil!")
   226  	}
   227  }
   228  
   229  func updateQBFTBlock(block *types.Block, addr common.Address) *types.Block {
   230  	header := block.Header()
   231  	header.Coinbase = addr
   232  	return block.WithSeal(header)
   233  }
   234  
   235  func TestSealCommitted(t *testing.T) {
   236  	chain, engine := newBlockChain(1, big.NewInt(0))
   237  	defer engine.Stop()
   238  	block := makeBlockWithoutSeal(chain, engine, chain.Genesis())
   239  	expectedBlock := updateQBFTBlock(block, engine.Address())
   240  
   241  	resultCh := make(chan *types.Block, 10)
   242  	go func() {
   243  		err := engine.Seal(chain, block, resultCh, make(chan struct{}))
   244  
   245  		if err != nil {
   246  			t.Errorf("error mismatch: have %v, want %v", err, expectedBlock)
   247  		}
   248  	}()
   249  
   250  	finalBlock := <-resultCh
   251  	if finalBlock.Hash() != expectedBlock.Hash() {
   252  		t.Errorf("hash mismatch: have %v, want %v", finalBlock.Hash(), expectedBlock.Hash())
   253  	}
   254  }
   255  
   256  func TestVerifyHeader(t *testing.T) {
   257  	chain, engine := newBlockChain(1, big.NewInt(0))
   258  	defer engine.Stop()
   259  
   260  	// istanbulcommon.ErrEmptyCommittedSeals case
   261  	block := makeBlockWithoutSeal(chain, engine, chain.Genesis())
   262  	header := engine.chain.GetHeader(block.ParentHash(), block.NumberU64()-1)
   263  	block = updateQBFTBlock(block, engine.Address())
   264  	err := engine.VerifyHeader(chain, block.Header(), false)
   265  	if err != istanbulcommon.ErrEmptyCommittedSeals {
   266  		t.Errorf("error mismatch: have %v, want %v", err, istanbulcommon.ErrEmptyCommittedSeals)
   267  	}
   268  
   269  	// short extra data
   270  	header = block.Header()
   271  	header.Extra = []byte{}
   272  	err = engine.VerifyHeader(chain, header, false)
   273  	if err != istanbulcommon.ErrInvalidExtraDataFormat {
   274  		t.Errorf("error mismatch: have %v, want %v", err, istanbulcommon.ErrInvalidExtraDataFormat)
   275  	}
   276  	// incorrect extra format
   277  	header.Extra = []byte("0000000000000000000000000000000012300000000000000000000000000000000000000000000000000000000000000000")
   278  	err = engine.VerifyHeader(chain, header, false)
   279  	if err != istanbulcommon.ErrInvalidExtraDataFormat {
   280  		t.Errorf("error mismatch: have %v, want %v", err, istanbulcommon.ErrInvalidExtraDataFormat)
   281  	}
   282  
   283  	// non zero MixDigest
   284  	block = makeBlockWithoutSeal(chain, engine, chain.Genesis())
   285  	header = block.Header()
   286  	header.MixDigest = common.StringToHash("123456789")
   287  	err = engine.VerifyHeader(chain, header, false)
   288  	if err != istanbulcommon.ErrInvalidMixDigest {
   289  		t.Errorf("error mismatch: have %v, want %v", err, istanbulcommon.ErrInvalidMixDigest)
   290  	}
   291  
   292  	// invalid uncles hash
   293  	block = makeBlockWithoutSeal(chain, engine, chain.Genesis())
   294  	header = block.Header()
   295  	header.UncleHash = common.StringToHash("123456789")
   296  	err = engine.VerifyHeader(chain, header, false)
   297  	if err != istanbulcommon.ErrInvalidUncleHash {
   298  		t.Errorf("error mismatch: have %v, want %v", err, istanbulcommon.ErrInvalidUncleHash)
   299  	}
   300  
   301  	// invalid difficulty
   302  	block = makeBlockWithoutSeal(chain, engine, chain.Genesis())
   303  	header = block.Header()
   304  	header.Difficulty = big.NewInt(2)
   305  	err = engine.VerifyHeader(chain, header, false)
   306  	if err != istanbulcommon.ErrInvalidDifficulty {
   307  		t.Errorf("error mismatch: have %v, want %v", err, istanbulcommon.ErrInvalidDifficulty)
   308  	}
   309  
   310  	// invalid timestamp
   311  	block = makeBlockWithoutSeal(chain, engine, chain.Genesis())
   312  	header = block.Header()
   313  	header.Time = chain.Genesis().Time() + (engine.config.BlockPeriod - 1)
   314  	err = engine.VerifyHeader(chain, header, false)
   315  	if err != istanbulcommon.ErrInvalidTimestamp {
   316  		t.Errorf("error mismatch: have %v, want %v", err, istanbulcommon.ErrInvalidTimestamp)
   317  	}
   318  
   319  	// future block
   320  	block = makeBlockWithoutSeal(chain, engine, chain.Genesis())
   321  	header = block.Header()
   322  	header.Time = uint64(time.Now().Unix() + 10)
   323  	err = engine.VerifyHeader(chain, header, false)
   324  	if err != consensus.ErrFutureBlock {
   325  		t.Errorf("error mismatch: have %v, want %v", err, consensus.ErrFutureBlock)
   326  	}
   327  
   328  	// future block which is within AllowedFutureBlockTime
   329  	block = makeBlockWithoutSeal(chain, engine, chain.Genesis())
   330  	header = block.Header()
   331  	header.Time = new(big.Int).Add(big.NewInt(time.Now().Unix()), new(big.Int).SetUint64(10)).Uint64()
   332  	priorValue := engine.config.AllowedFutureBlockTime
   333  	engine.config.AllowedFutureBlockTime = 10
   334  	err = engine.VerifyHeader(chain, header, false)
   335  	engine.config.AllowedFutureBlockTime = priorValue //restore changed value
   336  	if err == consensus.ErrFutureBlock {
   337  		t.Errorf("error mismatch: have %v, want nil", err)
   338  	}
   339  
   340  	// TODO This test does not make sense anymore as validate vote type is not stored in nonce
   341  	// invalid nonce
   342  	/*block = makeBlockWithoutSeal(chain, engine, chain.Genesis())
   343  	header = block.Header()
   344  	copy(header.Nonce[:], hexutil.MustDecode("0x111111111111"))
   345  	header.Number = big.NewInt(int64(engine.config.Epoch))
   346  	err = engine.VerifyHeader(chain, header, false)
   347  	if err != errInvalidNonce {
   348  		t.Errorf("error mismatch: have %v, want %v", err, errInvalidNonce)
   349  	}*/
   350  }
   351  
   352  func TestVerifyHeaders(t *testing.T) {
   353  	chain, engine := newBlockChain(1, big.NewInt(0))
   354  	defer engine.Stop()
   355  	genesis := chain.Genesis()
   356  
   357  	// success case
   358  	headers := []*types.Header{}
   359  	blocks := []*types.Block{}
   360  	size := 100
   361  
   362  	for i := 0; i < size; i++ {
   363  		var b *types.Block
   364  		if i == 0 {
   365  			b = makeBlockWithoutSeal(chain, engine, genesis)
   366  			b = updateQBFTBlock(b, engine.Address())
   367  		} else {
   368  			b = makeBlockWithoutSeal(chain, engine, blocks[i-1])
   369  			b = updateQBFTBlock(b, engine.Address())
   370  		}
   371  		blocks = append(blocks, b)
   372  		headers = append(headers, blocks[i].Header())
   373  	}
   374  	// now = func() time.Time {
   375  	// 	return time.Unix(int64(headers[size-1].Time), 0)
   376  	// }
   377  	_, results := engine.VerifyHeaders(chain, headers, nil)
   378  	const timeoutDura = 2 * time.Second
   379  	timeout := time.NewTimer(timeoutDura)
   380  	index := 0
   381  OUT1:
   382  	for {
   383  		select {
   384  		case err := <-results:
   385  			if err != nil {
   386  				if err != istanbulcommon.ErrEmptyCommittedSeals && err != istanbulcommon.ErrInvalidCommittedSeals && err != consensus.ErrUnknownAncestor {
   387  					t.Errorf("error mismatch: have %v, want istanbulcommon.ErrEmptyCommittedSeals|istanbulcommon.ErrInvalidCommittedSeals|ErrUnknownAncestor", err)
   388  					break OUT1
   389  				}
   390  			}
   391  			index++
   392  			if index == size {
   393  				break OUT1
   394  			}
   395  		case <-timeout.C:
   396  			break OUT1
   397  		}
   398  	}
   399  	_, results = engine.VerifyHeaders(chain, headers, nil)
   400  	timeout = time.NewTimer(timeoutDura)
   401  OUT2:
   402  	for {
   403  		select {
   404  		case err := <-results:
   405  			if err != nil {
   406  				if err != istanbulcommon.ErrEmptyCommittedSeals && err != istanbulcommon.ErrInvalidCommittedSeals && err != consensus.ErrUnknownAncestor {
   407  					t.Errorf("error mismatch: have %v, want istanbulcommon.ErrEmptyCommittedSeals|istanbulcommon.ErrInvalidCommittedSeals|ErrUnknownAncestor", err)
   408  					break OUT2
   409  				}
   410  			}
   411  		case <-timeout.C:
   412  			break OUT2
   413  		}
   414  	}
   415  	// error header cases
   416  	headers[2].Number = big.NewInt(100)
   417  	_, results = engine.VerifyHeaders(chain, headers, nil)
   418  	timeout = time.NewTimer(timeoutDura)
   419  	index = 0
   420  	errors := 0
   421  	expectedErrors := 0
   422  OUT3:
   423  	for {
   424  		select {
   425  		case err := <-results:
   426  			if err != nil {
   427  				if err != istanbulcommon.ErrEmptyCommittedSeals && err != istanbulcommon.ErrInvalidCommittedSeals && err != consensus.ErrUnknownAncestor {
   428  					errors++
   429  				}
   430  			}
   431  			index++
   432  			if index == size {
   433  				if errors != expectedErrors {
   434  					t.Errorf("error mismatch: have %v, want %v", errors, expectedErrors)
   435  				}
   436  				break OUT3
   437  			}
   438  		case <-timeout.C:
   439  			break OUT3
   440  		}
   441  	}
   442  }