github.com/Blockdaemon/celo-blockchain@v0.0.0-20200129231733-e667f6b08419/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  	getRoundState := func(c *core) *roundStateImpl {
    39  		return c.current.(*rsSaveDecorator).rs.(*roundStateImpl)
    40  	}
    41  
    42  	testCases := []struct {
    43  		name            string
    44  		system          func() *testSystem
    45  		getCert         func(*testSystem) istanbul.RoundChangeCertificate
    46  		expectedRequest istanbul.Proposal
    47  		expectedErr     error
    48  		existingBlock   bool
    49  	}{
    50  		{
    51  			"normal case",
    52  			func() *testSystem {
    53  				sys := NewTestSystemWithBackend(N, F)
    54  
    55  				for _, backend := range sys.backends {
    56  					backend.engine.(*core).Start()
    57  				}
    58  				return sys
    59  			},
    60  			func(_ *testSystem) istanbul.RoundChangeCertificate {
    61  				return istanbul.RoundChangeCertificate{}
    62  			},
    63  			newTestProposal(),
    64  			nil,
    65  			false,
    66  		},
    67  		{
    68  			"proposal sequence doesn't match message sequence",
    69  			func() *testSystem {
    70  				sys := NewTestSystemWithBackend(N, F)
    71  
    72  				for _, backend := range sys.backends {
    73  					backend.engine.(*core).Start()
    74  				}
    75  				return sys
    76  			},
    77  			func(_ *testSystem) istanbul.RoundChangeCertificate {
    78  				return istanbul.RoundChangeCertificate{}
    79  			},
    80  			makeBlock(3),
    81  			errInvalidProposal,
    82  			false,
    83  		},
    84  		{
    85  			"non-proposer",
    86  			func() *testSystem {
    87  				sys := NewTestSystemWithBackend(N, F)
    88  
    89  				// force remove replica 0, let replica 1 be the proposer
    90  				sys.backends = sys.backends[1:]
    91  
    92  				for i, backend := range sys.backends {
    93  					backend.engine.(*core).Start()
    94  					c := backend.engine.(*core)
    95  					if i != 0 {
    96  						// replica 0 is the proposer
    97  						getRoundState(c).state = StatePreprepared
    98  					}
    99  				}
   100  				return sys
   101  			},
   102  			func(_ *testSystem) istanbul.RoundChangeCertificate {
   103  				return istanbul.RoundChangeCertificate{}
   104  			},
   105  			makeBlock(1),
   106  			errNotFromProposer,
   107  			false,
   108  		},
   109  		{
   110  			"errOldMessage",
   111  			func() *testSystem {
   112  				sys := NewTestSystemWithBackend(N, F)
   113  
   114  				for i, backend := range sys.backends {
   115  					backend.engine.(*core).Start()
   116  					c := backend.engine.(*core)
   117  					if i != 0 {
   118  						getRoundState(c).state = StatePreprepared
   119  						getRoundState(c).sequence = big.NewInt(10)
   120  						getRoundState(c).round = big.NewInt(10)
   121  					}
   122  				}
   123  				return sys
   124  			},
   125  			func(_ *testSystem) istanbul.RoundChangeCertificate {
   126  				return istanbul.RoundChangeCertificate{}
   127  			},
   128  			makeBlock(1),
   129  			errOldMessage,
   130  			false,
   131  		},
   132  		{
   133  			"test existing block",
   134  			func() *testSystem {
   135  				sys := NewTestSystemWithBackend(N, F)
   136  
   137  				for _, backend := range sys.backends {
   138  					backend.engine.(*core).Start()
   139  					c := backend.engine.(*core)
   140  					getRoundState(c).state = StatePreprepared
   141  					getRoundState(c).sequence = big.NewInt(10)
   142  					getRoundState(c).round = big.NewInt(10)
   143  				}
   144  				return sys
   145  			},
   146  			func(_ *testSystem) istanbul.RoundChangeCertificate {
   147  				return istanbul.RoundChangeCertificate{}
   148  			},
   149  			// In the method testbackend_test.go:HasBlockMatching(), it will return true if the proposal's block number == 5
   150  			makeBlock(5),
   151  			nil,
   152  			true,
   153  		},
   154  		{
   155  			"ROUND CHANGE certificate missing",
   156  			func() *testSystem {
   157  				sys := NewTestSystemWithBackend(N, F)
   158  
   159  				for _, backend := range sys.backends {
   160  					backend.engine.(*core).Start()
   161  					c := backend.engine.(*core)
   162  					getRoundState(c).state = StatePreprepared
   163  					getRoundState(c).round = big.NewInt(int64(N))
   164  				}
   165  				return sys
   166  			},
   167  			func(_ *testSystem) istanbul.RoundChangeCertificate {
   168  				return istanbul.RoundChangeCertificate{}
   169  			},
   170  			makeBlock(1),
   171  			errMissingRoundChangeCertificate,
   172  			false,
   173  		},
   174  		{
   175  			"ROUND CHANGE certificate invalid, duplicate messages.",
   176  			func() *testSystem {
   177  				sys := NewTestSystemWithBackend(N, F)
   178  
   179  				for _, backend := range sys.backends {
   180  					backend.engine.(*core).Start()
   181  					c := backend.engine.(*core)
   182  					getRoundState(c).state = StatePreprepared
   183  					getRoundState(c).round = big.NewInt(int64(N))
   184  				}
   185  				return sys
   186  			},
   187  			func(sys *testSystem) istanbul.RoundChangeCertificate {
   188  				// Duplicate messages
   189  				roundChangeCertificate := sys.getRoundChangeCertificate(t, []istanbul.View{*(sys.backends[0].engine.(*core).current.View())}, istanbul.EmptyPreparedCertificate())
   190  				roundChangeCertificate.RoundChangeMessages[1] = roundChangeCertificate.RoundChangeMessages[0]
   191  				return roundChangeCertificate
   192  			},
   193  			makeBlock(1),
   194  			errInvalidRoundChangeCertificateDuplicate,
   195  			false,
   196  		},
   197  		{
   198  			"ROUND CHANGE certificate contains PREPARED certificate with inconsistent views among the cert's messages",
   199  			func() *testSystem {
   200  				sys := NewTestSystemWithBackend(N, F)
   201  
   202  				for _, backend := range sys.backends {
   203  					backend.engine.(*core).Start()
   204  					c := backend.engine.(*core)
   205  					getRoundState(c).state = StatePreprepared
   206  					getRoundState(c).round = big.NewInt(int64(N))
   207  					getRoundState(c).sequence = big.NewInt(1)
   208  					c.current.TransitionToPreprepared(&istanbul.Preprepare{
   209  						View: &istanbul.View{
   210  							Round:    big.NewInt(int64(N)),
   211  							Sequence: big.NewInt(1),
   212  						},
   213  						Proposal:               makeBlock(1),
   214  						RoundChangeCertificate: istanbul.RoundChangeCertificate{},
   215  					})
   216  				}
   217  				return sys
   218  			},
   219  			func(sys *testSystem) istanbul.RoundChangeCertificate {
   220  				view1 := *(sys.backends[0].engine.(*core).current.View())
   221  
   222  				var view2 istanbul.View
   223  				view2.Sequence = big.NewInt(view1.Sequence.Int64())
   224  				view2.Round = big.NewInt(view1.Round.Int64() + 1)
   225  
   226  				preparedCertificate := sys.getPreparedCertificate(t, []istanbul.View{view1, view2}, makeBlock(1))
   227  				roundChangeCertificate := sys.getRoundChangeCertificate(t, []istanbul.View{*(sys.backends[0].engine.(*core).current.View())}, preparedCertificate)
   228  				return roundChangeCertificate
   229  			},
   230  			makeBlock(1),
   231  			errInvalidPreparedCertificateInconsistentViews,
   232  			false,
   233  		},
   234  		{
   235  			"ROUND CHANGE certificate contains PREPARED certificate for a different block.",
   236  			func() *testSystem {
   237  				sys := NewTestSystemWithBackend(N, F)
   238  
   239  				for _, backend := range sys.backends {
   240  					backend.engine.(*core).Start()
   241  					c := backend.engine.(*core)
   242  					getRoundState(c).state = StatePreprepared
   243  					getRoundState(c).round = big.NewInt(int64(N))
   244  					c.current.TransitionToPreprepared(&istanbul.Preprepare{
   245  						View: &istanbul.View{
   246  							Round:    big.NewInt(int64(N)),
   247  							Sequence: big.NewInt(0),
   248  						},
   249  						Proposal:               makeBlock(2),
   250  						RoundChangeCertificate: istanbul.RoundChangeCertificate{},
   251  					})
   252  				}
   253  				return sys
   254  			},
   255  			func(sys *testSystem) istanbul.RoundChangeCertificate {
   256  				preparedCertificate := sys.getPreparedCertificate(t, []istanbul.View{*(sys.backends[0].engine.(*core).current.View())}, makeBlock(2))
   257  				roundChangeCertificate := sys.getRoundChangeCertificate(t, []istanbul.View{*(sys.backends[0].engine.(*core).current.View())}, preparedCertificate)
   258  				return roundChangeCertificate
   259  			},
   260  			makeBlock(1),
   261  			errInvalidPreparedCertificateDigestMismatch,
   262  			false,
   263  		},
   264  		{
   265  			"ROUND CHANGE certificate for N+1 round with valid PREPARED certificates",
   266  			// Round is N+1 to match the correct proposer.
   267  			func() *testSystem {
   268  				sys := NewTestSystemWithBackend(N, F)
   269  
   270  				for i, backend := range sys.backends {
   271  					backend.engine.(*core).Start()
   272  					c := backend.engine.(*core)
   273  					getRoundState(c).round = big.NewInt(int64(N))
   274  					if i != 0 {
   275  						getRoundState(c).state = StateAcceptRequest
   276  					}
   277  				}
   278  				return sys
   279  			},
   280  			func(sys *testSystem) istanbul.RoundChangeCertificate {
   281  				preparedCertificate := sys.getPreparedCertificate(t, []istanbul.View{*(sys.backends[0].engine.(*core).current.View())}, makeBlock(1))
   282  				roundChangeCertificate := sys.getRoundChangeCertificate(t, []istanbul.View{*(sys.backends[0].engine.(*core).current.View())}, preparedCertificate)
   283  				return roundChangeCertificate
   284  			},
   285  			makeBlock(1),
   286  			nil,
   287  			false,
   288  		},
   289  		{
   290  			"ROUND CHANGE certificate for N+1 round with empty PREPARED certificates",
   291  			// Round is N+1 to match the correct proposer.
   292  			func() *testSystem {
   293  				sys := NewTestSystemWithBackend(N, F)
   294  
   295  				for i, backend := range sys.backends {
   296  					backend.engine.(*core).Start()
   297  					c := backend.engine.(*core)
   298  					getRoundState(c).round = big.NewInt(int64(N))
   299  					if i != 0 {
   300  						getRoundState(c).state = StateAcceptRequest
   301  					}
   302  				}
   303  				return sys
   304  			},
   305  			func(sys *testSystem) istanbul.RoundChangeCertificate {
   306  				roundChangeCertificate := sys.getRoundChangeCertificate(t, []istanbul.View{*(sys.backends[0].engine.(*core).current.View())}, istanbul.EmptyPreparedCertificate())
   307  				return roundChangeCertificate
   308  			},
   309  			makeBlock(1),
   310  			nil,
   311  			false,
   312  		},
   313  		{
   314  			"ROUND CHANGE certificate for N+1 or later rounds with empty PREPARED certificates",
   315  			// Round is N+1 to match the correct proposer.
   316  			func() *testSystem {
   317  				sys := NewTestSystemWithBackend(N, F)
   318  
   319  				for i, backend := range sys.backends {
   320  					backend.engine.(*core).Start()
   321  					c := backend.engine.(*core)
   322  					getRoundState(c).round = big.NewInt(int64(N))
   323  					if i != 0 {
   324  						getRoundState(c).state = StateAcceptRequest
   325  					}
   326  				}
   327  				return sys
   328  			},
   329  			func(sys *testSystem) istanbul.RoundChangeCertificate {
   330  				v1 := *(sys.backends[0].engine.(*core).current.View())
   331  				v2 := istanbul.View{Sequence: v1.Sequence, Round: big.NewInt(v1.Round.Int64() + 1)}
   332  				v3 := istanbul.View{Sequence: v1.Sequence, Round: big.NewInt(v1.Round.Int64() + 2)}
   333  				roundChangeCertificate := sys.getRoundChangeCertificate(t, []istanbul.View{v1, v2, v3}, istanbul.EmptyPreparedCertificate())
   334  				return roundChangeCertificate
   335  			},
   336  			makeBlock(1),
   337  			nil,
   338  			false,
   339  		},
   340  	}
   341  
   342  	for _, test := range testCases {
   343  		t.Run(test.name, func(t *testing.T) {
   344  
   345  			t.Log("Running", "test", test.name)
   346  			sys := test.system()
   347  			closer := sys.Run(false)
   348  
   349  			v0 := sys.backends[0]
   350  			r0 := v0.engine.(*core)
   351  
   352  			curView := r0.current.View()
   353  
   354  			preprepareView := curView
   355  			if test.existingBlock {
   356  				preprepareView = &istanbul.View{Round: big.NewInt(0), Sequence: big.NewInt(5)}
   357  			}
   358  
   359  			preprepare := &istanbul.Preprepare{
   360  				View:                   preprepareView,
   361  				Proposal:               test.expectedRequest,
   362  				RoundChangeCertificate: test.getCert(sys),
   363  			}
   364  
   365  			for i, v := range sys.backends {
   366  				// i == 0 is primary backend, it is responsible for send PRE-PREPARE messages to others.
   367  				if i == 0 {
   368  					continue
   369  				}
   370  
   371  				c := v.engine.(*core)
   372  
   373  				m, _ := Encode(preprepare)
   374  				// run each backends and verify handlePreprepare function.
   375  				if err := c.handlePreprepare(&istanbul.Message{
   376  					Code:    istanbul.MsgPreprepare,
   377  					Msg:     m,
   378  					Address: v0.Address(),
   379  				}); err != nil {
   380  					if err != test.expectedErr {
   381  						t.Errorf("error mismatch: have %v, want %v", err, test.expectedErr)
   382  					}
   383  					return
   384  				}
   385  
   386  				if c.current.State() != StatePreprepared {
   387  					t.Errorf("state mismatch: have %v, want %v", c.current.State(), StatePreprepared)
   388  				}
   389  
   390  				if !test.existingBlock && !reflect.DeepEqual(c.current.Subject().View, curView) {
   391  					t.Errorf("view mismatch: have %v, want %v", c.current.Subject().View, curView)
   392  				}
   393  
   394  				if test.existingBlock && len(v.sentMsgs) > 0 {
   395  					t.Errorf("expecting to ignore commits for old messages %v", v.sentMsgs)
   396  				} else {
   397  					continue
   398  				}
   399  
   400  				// verify prepare messages
   401  				decodedMsg := new(istanbul.Message)
   402  				err := decodedMsg.FromPayload(v.sentMsgs[0], nil)
   403  				if err != nil {
   404  					t.Errorf("error mismatch: have %v, want nil", err)
   405  				}
   406  
   407  				expectedCode := istanbul.MsgPrepare
   408  				if test.existingBlock {
   409  					expectedCode = istanbul.MsgCommit
   410  				}
   411  				if decodedMsg.Code != expectedCode {
   412  					t.Errorf("message code mismatch: have %v, want %v", decodedMsg.Code, expectedCode)
   413  				}
   414  
   415  				var subject *istanbul.Subject
   416  				var committedSubject *istanbul.CommittedSubject
   417  
   418  				if decodedMsg.Code == istanbul.MsgPrepare {
   419  					err = decodedMsg.Decode(&subject)
   420  				} else if decodedMsg.Code == istanbul.MsgCommit {
   421  					err = decodedMsg.Decode(&committedSubject)
   422  					subject = committedSubject.Subject
   423  				}
   424  
   425  				if err != nil {
   426  					t.Errorf("error mismatch: have %v, want nil", err)
   427  				}
   428  
   429  				expectedSubject := c.current.Subject()
   430  				if test.existingBlock {
   431  					expectedSubject = &istanbul.Subject{View: &istanbul.View{Round: big.NewInt(0), Sequence: big.NewInt(5)},
   432  						Digest: test.expectedRequest.Hash()}
   433  				}
   434  
   435  				if !reflect.DeepEqual(subject, expectedSubject) {
   436  					t.Errorf("subject mismatch: have %v, want %v", subject, expectedSubject)
   437  				}
   438  
   439  				if expectedCode == istanbul.MsgCommit {
   440  					srcValidator := c.current.GetValidatorByAddress(v.address)
   441  
   442  					if err := c.verifyCommittedSeal(committedSubject, srcValidator); err != nil {
   443  						t.Errorf("invalid seal.  verify commmited seal error: %v, subject: %v, committedSeal: %v", err, expectedSubject, committedSubject.CommittedSeal)
   444  					}
   445  				}
   446  			}
   447  
   448  			closer()
   449  		})
   450  	}
   451  }