github.com/ConsenSys/Quorum@v20.10.0+incompatible/consensus/istanbul/core/preprepare_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  	"math/big"
    21  	"reflect"
    22  	"testing"
    23  
    24  	"github.com/ethereum/go-ethereum/consensus/istanbul"
    25  )
    26  
    27  func newTestPreprepare(v *istanbul.View) *istanbul.Preprepare {
    28  	return &istanbul.Preprepare{
    29  		View:     v,
    30  		Proposal: newTestProposal(),
    31  	}
    32  }
    33  
    34  func TestHandlePreprepare(t *testing.T) {
    35  	N := uint64(4) // replica 0 is the proposer, it will send messages to others
    36  	F := uint64(1) // F does not affect tests
    37  
    38  	testCases := []struct {
    39  		system          *testSystem
    40  		expectedRequest istanbul.Proposal
    41  		expectedErr     error
    42  		existingBlock   bool
    43  	}{
    44  		{
    45  			// normal case
    46  			func() *testSystem {
    47  				sys := NewTestSystemWithBackend(N, F)
    48  
    49  				for i, backend := range sys.backends {
    50  					c := backend.engine.(*core)
    51  					c.valSet = backend.peers
    52  					if i != 0 {
    53  						c.state = StateAcceptRequest
    54  					}
    55  				}
    56  				return sys
    57  			}(),
    58  			newTestProposal(),
    59  			nil,
    60  			false,
    61  		},
    62  		{
    63  			// future message
    64  			func() *testSystem {
    65  				sys := NewTestSystemWithBackend(N, F)
    66  
    67  				for i, backend := range sys.backends {
    68  					c := backend.engine.(*core)
    69  					c.valSet = backend.peers
    70  					if i != 0 {
    71  						c.state = StateAcceptRequest
    72  						// hack: force set subject that future message can be simulated
    73  						c.current = newTestRoundState(
    74  							&istanbul.View{
    75  								Round:    big.NewInt(0),
    76  								Sequence: big.NewInt(0),
    77  							},
    78  							c.valSet,
    79  						)
    80  
    81  					} else {
    82  						c.current.SetSequence(big.NewInt(10))
    83  					}
    84  				}
    85  				return sys
    86  			}(),
    87  			makeBlock(1),
    88  			errFutureMessage,
    89  			false,
    90  		},
    91  		{
    92  			// non-proposer
    93  			func() *testSystem {
    94  				sys := NewTestSystemWithBackend(N, F)
    95  
    96  				// force remove replica 0, let replica 1 be the proposer
    97  				sys.backends = sys.backends[1:]
    98  
    99  				for i, backend := range sys.backends {
   100  					c := backend.engine.(*core)
   101  					c.valSet = backend.peers
   102  					if i != 0 {
   103  						// replica 0 is the proposer
   104  						c.state = StatePreprepared
   105  					}
   106  				}
   107  				return sys
   108  			}(),
   109  			makeBlock(1),
   110  			errNotFromProposer,
   111  			false,
   112  		},
   113  		{
   114  			// errOldMessage
   115  			func() *testSystem {
   116  				sys := NewTestSystemWithBackend(N, F)
   117  
   118  				for i, backend := range sys.backends {
   119  					c := backend.engine.(*core)
   120  					c.valSet = backend.peers
   121  					if i != 0 {
   122  						c.state = StatePreprepared
   123  						c.current.SetSequence(big.NewInt(10))
   124  						c.current.SetRound(big.NewInt(10))
   125  					}
   126  				}
   127  				return sys
   128  			}(),
   129  			makeBlock(1),
   130  			errOldMessage,
   131  			false,
   132  		},
   133  	}
   134  
   135  OUTER:
   136  	for _, test := range testCases {
   137  		test.system.Run(false)
   138  
   139  		v0 := test.system.backends[0]
   140  		r0 := v0.engine.(*core)
   141  
   142  		curView := r0.currentView()
   143  
   144  		preprepare := &istanbul.Preprepare{
   145  			View:     curView,
   146  			Proposal: test.expectedRequest,
   147  		}
   148  
   149  		for i, v := range test.system.backends {
   150  			// i == 0 is primary backend, it is responsible for send PRE-PREPARE messages to others.
   151  			if i == 0 {
   152  				continue
   153  			}
   154  
   155  			c := v.engine.(*core)
   156  
   157  			m, _ := Encode(preprepare)
   158  			_, val := r0.valSet.GetByAddress(v0.Address())
   159  			// run each backends and verify handlePreprepare function.
   160  			if err := c.handlePreprepare(&message{
   161  				Code:    msgPreprepare,
   162  				Msg:     m,
   163  				Address: v0.Address(),
   164  			}, val); err != nil {
   165  				if err != test.expectedErr {
   166  					t.Errorf("error mismatch: have %v, want %v", err, test.expectedErr)
   167  				}
   168  				continue OUTER
   169  			}
   170  
   171  			if c.state != StatePreprepared {
   172  				t.Errorf("state mismatch: have %v, want %v", c.state, StatePreprepared)
   173  			}
   174  
   175  			if !test.existingBlock && !reflect.DeepEqual(c.current.Subject().View, curView) {
   176  				t.Errorf("view mismatch: have %v, want %v", c.current.Subject().View, curView)
   177  			}
   178  
   179  			// verify prepare messages
   180  			decodedMsg := new(message)
   181  			err := decodedMsg.FromPayload(v.sentMsgs[0], nil)
   182  			if err != nil {
   183  				t.Errorf("error mismatch: have %v, want nil", err)
   184  			}
   185  
   186  			expectedCode := msgPrepare
   187  			if test.existingBlock {
   188  				expectedCode = msgCommit
   189  			}
   190  			if decodedMsg.Code != expectedCode {
   191  				t.Errorf("message code mismatch: have %v, want %v", decodedMsg.Code, expectedCode)
   192  			}
   193  
   194  			var subject *istanbul.Subject
   195  			err = decodedMsg.Decode(&subject)
   196  			if err != nil {
   197  				t.Errorf("error mismatch: have %v, want nil", err)
   198  			}
   199  			if !test.existingBlock && !reflect.DeepEqual(subject, c.current.Subject()) {
   200  				t.Errorf("subject mismatch: have %v, want %v", subject, c.current.Subject())
   201  			}
   202  
   203  		}
   204  	}
   205  }
   206  
   207  func TestHandlePreprepareWithLock(t *testing.T) {
   208  	N := uint64(4) // replica 0 is the proposer, it will send messages to others
   209  	F := uint64(1) // F does not affect tests
   210  	proposal := newTestProposal()
   211  	mismatchProposal := makeBlock(10)
   212  	newSystem := func() *testSystem {
   213  		sys := NewTestSystemWithBackend(N, F)
   214  
   215  		for i, backend := range sys.backends {
   216  			c := backend.engine.(*core)
   217  			c.valSet = backend.peers
   218  			if i != 0 {
   219  				c.state = StateAcceptRequest
   220  			}
   221  			c.roundChangeSet = newRoundChangeSet(c.valSet)
   222  		}
   223  		return sys
   224  	}
   225  
   226  	testCases := []struct {
   227  		system       *testSystem
   228  		proposal     istanbul.Proposal
   229  		lockProposal istanbul.Proposal
   230  	}{
   231  		{
   232  			newSystem(),
   233  			proposal,
   234  			proposal,
   235  		},
   236  		{
   237  			newSystem(),
   238  			proposal,
   239  			mismatchProposal,
   240  		},
   241  	}
   242  
   243  	for _, test := range testCases {
   244  		test.system.Run(false)
   245  		v0 := test.system.backends[0]
   246  		r0 := v0.engine.(*core)
   247  		curView := r0.currentView()
   248  		preprepare := &istanbul.Preprepare{
   249  			View:     curView,
   250  			Proposal: test.proposal,
   251  		}
   252  		lockPreprepare := &istanbul.Preprepare{
   253  			View:     curView,
   254  			Proposal: test.lockProposal,
   255  		}
   256  
   257  		for i, v := range test.system.backends {
   258  			// i == 0 is primary backend, it is responsible for send PRE-PREPARE messages to others.
   259  			if i == 0 {
   260  				continue
   261  			}
   262  
   263  			c := v.engine.(*core)
   264  			c.current.SetPreprepare(lockPreprepare)
   265  			c.current.LockHash()
   266  			m, _ := Encode(preprepare)
   267  			_, val := r0.valSet.GetByAddress(v0.Address())
   268  			if err := c.handlePreprepare(&message{
   269  				Code:    msgPreprepare,
   270  				Msg:     m,
   271  				Address: v0.Address(),
   272  			}, val); err != nil {
   273  				t.Errorf("error mismatch: have %v, want nil", err)
   274  			}
   275  			if test.proposal == test.lockProposal {
   276  				if c.state != StatePrepared {
   277  					t.Errorf("state mismatch: have %v, want %v", c.state, StatePreprepared)
   278  				}
   279  				if !reflect.DeepEqual(curView, c.currentView()) {
   280  					t.Errorf("view mismatch: have %v, want %v", c.currentView(), curView)
   281  				}
   282  			} else {
   283  				// Should stay at StateAcceptRequest
   284  				if c.state != StateAcceptRequest {
   285  					t.Errorf("state mismatch: have %v, want %v", c.state, StateAcceptRequest)
   286  				}
   287  				// Should have triggered a round change
   288  				expectedView := &istanbul.View{
   289  					Sequence: curView.Sequence,
   290  					Round:    big.NewInt(1),
   291  				}
   292  				if !reflect.DeepEqual(expectedView, c.currentView()) {
   293  					t.Errorf("view mismatch: have %v, want %v", c.currentView(), expectedView)
   294  				}
   295  			}
   296  		}
   297  	}
   298  }