github.com/Blockdaemon/celo-blockchain@v0.0.0-20200129231733-e667f6b08419/consensus/istanbul/core/core_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 core
    18  
    19  import (
    20  	"errors"
    21  	"math/big"
    22  	"reflect"
    23  	"testing"
    24  	"time"
    25  
    26  	"github.com/ethereum/go-ethereum/consensus"
    27  	"github.com/ethereum/go-ethereum/consensus/istanbul"
    28  	"github.com/ethereum/go-ethereum/core/types"
    29  	elog "github.com/ethereum/go-ethereum/log"
    30  )
    31  
    32  func makeBlock(number int64) *types.Block {
    33  	header := &types.Header{
    34  		Difficulty: big.NewInt(0),
    35  		Number:     big.NewInt(number),
    36  		GasLimit:   0,
    37  		GasUsed:    0,
    38  		Time:       big.NewInt(0),
    39  	}
    40  	return types.NewBlock(header, nil, nil, nil, nil)
    41  }
    42  
    43  func makeBlockWithDifficulty(number, difficulty int64) *types.Block {
    44  	header := &types.Header{
    45  		Difficulty: big.NewInt(difficulty),
    46  		Number:     big.NewInt(number),
    47  		GasLimit:   0,
    48  		GasUsed:    0,
    49  		Time:       big.NewInt(0),
    50  	}
    51  	block := &types.Block{}
    52  	block = block.WithRandomness(&types.EmptyRandomness)
    53  	block = block.WithEpochSnarkData(&types.EmptyEpochSnarkData)
    54  	return block.WithSeal(header)
    55  }
    56  
    57  func newTestProposalWithNum(num int64) istanbul.Proposal {
    58  	return makeBlock(num)
    59  }
    60  
    61  func newTestProposal() istanbul.Proposal {
    62  	return makeBlock(1)
    63  }
    64  
    65  var InvalidProposalError = errors.New("invalid proposal")
    66  
    67  func TestNewRequest(t *testing.T) {
    68  
    69  	testLogger.SetHandler(elog.StdoutHandler)
    70  
    71  	N := uint64(4)
    72  	F := uint64(1)
    73  
    74  	sys := NewTestSystemWithBackend(N, F)
    75  
    76  	close := sys.Run(true)
    77  	defer close()
    78  
    79  	request1 := makeBlock(1)
    80  	sys.backends[0].NewRequest(request1)
    81  
    82  	<-time.After(1 * time.Second)
    83  
    84  	request2 := makeBlock(2)
    85  	sys.backends[0].NewRequest(request2)
    86  
    87  	<-time.After(1 * time.Second)
    88  
    89  	for _, backend := range sys.backends {
    90  		if len(backend.committedMsgs) != 2 {
    91  			t.Errorf("the number of executed requests mismatch: have %v, want 2", len(backend.committedMsgs))
    92  		} else {
    93  			if !reflect.DeepEqual(request1.Number(), backend.committedMsgs[0].commitProposal.Number()) {
    94  				t.Errorf("the number of requests mismatch: have %v, want %v", request1.Number(), backend.committedMsgs[0].commitProposal.Number())
    95  			}
    96  			if !reflect.DeepEqual(request2.Number(), backend.committedMsgs[1].commitProposal.Number()) {
    97  				t.Errorf("the number of requests mismatch: have %v, want %v", request2.Number(), backend.committedMsgs[1].commitProposal.Number())
    98  			}
    99  		}
   100  	}
   101  }
   102  
   103  func TestVerifyProposal(t *testing.T) {
   104  	// Check that it should not be in the cache
   105  	sys := NewTestSystemWithBackend(1, 0)
   106  
   107  	close := sys.Run(true)
   108  	defer close()
   109  
   110  	backendCore := sys.backends[0].engine.(*core)
   111  	backend := backendCore.backend.(*testSystemBackend)
   112  
   113  	testCases := []struct {
   114  		name             string
   115  		proposal         istanbul.Proposal
   116  		verifyImpl       func(proposal istanbul.Proposal) (time.Duration, error)
   117  		expectedErr      error
   118  		expectedDuration time.Duration
   119  	}{
   120  		// Test case with valid proposal
   121  		{
   122  			"Valid proposal",
   123  			newTestProposalWithNum(1),
   124  			backend.verifyWithSuccess,
   125  			nil,
   126  			0,
   127  		},
   128  
   129  		// Test case with invalid proposal
   130  		{
   131  			"Invalid proposal",
   132  			newTestProposalWithNum(2),
   133  			backend.verifyWithFailure,
   134  			InvalidProposalError,
   135  			0,
   136  		},
   137  
   138  		// Test case with future proposal
   139  		{
   140  			"Future proposal",
   141  			newTestProposalWithNum(3),
   142  			backend.verifyWithFutureProposal,
   143  			consensus.ErrFutureBlock,
   144  			5,
   145  		},
   146  	}
   147  
   148  	for _, testCase := range testCases {
   149  		t.Run(testCase.name,
   150  			func(t *testing.T) {
   151  				// Inject in the verification function implementation
   152  				backend.setVerifyImpl(testCase.verifyImpl)
   153  
   154  				// Verify a cache miss
   155  				_, isCached := backendCore.current.GetProposalVerificationStatus(testCase.proposal.Hash())
   156  				if isCached {
   157  					t.Errorf("Should of had a cache miss")
   158  				}
   159  
   160  				// Do a verification with success
   161  				_, err := backendCore.verifyProposal(testCase.proposal)
   162  				if err != testCase.expectedErr {
   163  					t.Errorf("Unexpected return status on first verifyProposal call. Want: %v, Actual: %v", testCase.expectedErr, err)
   164  				}
   165  
   166  				// The cache entry for this proposal should be created, if it wasn't the future proposal case
   167  				err, isCached = backendCore.current.GetProposalVerificationStatus(testCase.proposal.Hash())
   168  				if testCase.name != "Future proposal" {
   169  					if !isCached {
   170  						t.Errorf("Should of had a cache hit")
   171  					}
   172  
   173  					if err != testCase.expectedErr {
   174  						t.Errorf("Unexpected cached proposal verification status. Want: %v, actual: %v", testCase.expectedErr, err)
   175  					}
   176  				} else { // testCase.name == "Future proposal"
   177  					if isCached {
   178  						t.Errorf("Should of had a cache miss for the future proposal test case")
   179  					}
   180  				}
   181  
   182  				// Call verify proposal again to check for the cached verifcation result and duration
   183  				duration, err := backendCore.verifyProposal(testCase.proposal)
   184  				if duration != testCase.expectedDuration || err != testCase.expectedErr {
   185  					t.Errorf("Unexpected return status on second verifyProposal call.  Want: err - %v, duration - %v; Actual: err - %v, duration - %v", testCase.expectedErr, testCase.expectedDuration, err, duration)
   186  				}
   187  			})
   188  	}
   189  }