github.com/Blockdaemon/celo-blockchain@v0.0.0-20200129231733-e667f6b08419/consensus/istanbul/core/prepare_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/common"
    25  	"github.com/ethereum/go-ethereum/consensus/istanbul"
    26  	"github.com/ethereum/go-ethereum/consensus/istanbul/validator"
    27  	"github.com/ethereum/go-ethereum/crypto"
    28  	blscrypto "github.com/ethereum/go-ethereum/crypto/bls"
    29  )
    30  
    31  func TestVerifyPreparedCertificate(t *testing.T) {
    32  	N := uint64(4) // replica 0 is the proposer, it will send messages to others
    33  	F := uint64(1)
    34  	sys := NewTestSystemWithBackend(N, F)
    35  	view := istanbul.View{
    36  		Round:    big.NewInt(0),
    37  		Sequence: big.NewInt(1),
    38  	}
    39  	proposal := makeBlock(0)
    40  
    41  	for _, b := range sys.backends {
    42  		b.engine.Start() // start Istanbul core
    43  	}
    44  
    45  	testCases := []struct {
    46  		name         string
    47  		certificate  istanbul.PreparedCertificate
    48  		expectedErr  error
    49  		expectedView *istanbul.View
    50  	}{
    51  		{
    52  			"Valid PREPARED certificate",
    53  			sys.getPreparedCertificate(t, []istanbul.View{view}, proposal),
    54  			nil,
    55  			&view,
    56  		},
    57  		{
    58  			"Invalid PREPARED certificate, duplicate message",
    59  			func() istanbul.PreparedCertificate {
    60  				preparedCertificate := sys.getPreparedCertificate(t, []istanbul.View{view}, proposal)
    61  				preparedCertificate.PrepareOrCommitMessages[1] = preparedCertificate.PrepareOrCommitMessages[0]
    62  				return preparedCertificate
    63  			}(),
    64  			errInvalidPreparedCertificateDuplicate,
    65  			nil,
    66  		},
    67  		{
    68  			"Invalid PREPARED certificate, future message",
    69  			func() istanbul.PreparedCertificate {
    70  				futureView := istanbul.View{
    71  					Round:    big.NewInt(0),
    72  					Sequence: big.NewInt(10),
    73  				}
    74  				preparedCertificate := sys.getPreparedCertificate(t, []istanbul.View{futureView}, proposal)
    75  				return preparedCertificate
    76  			}(),
    77  			errInvalidPreparedCertificateMsgView,
    78  			nil,
    79  		},
    80  		{
    81  			"Invalid PREPARED certificate, includes preprepare message",
    82  			func() istanbul.PreparedCertificate {
    83  				preparedCertificate := sys.getPreparedCertificate(t, []istanbul.View{view}, proposal)
    84  				testInvalidMsg, _ := sys.backends[0].getRoundChangeMessage(view, sys.getPreparedCertificate(t, []istanbul.View{view}, proposal))
    85  				preparedCertificate.PrepareOrCommitMessages[0] = testInvalidMsg
    86  				return preparedCertificate
    87  			}(),
    88  			errInvalidPreparedCertificateMsgCode,
    89  			nil,
    90  		},
    91  		{
    92  			"Invalid PREPARED certificate, hash mismatch",
    93  			func() istanbul.PreparedCertificate {
    94  				preparedCertificate := sys.getPreparedCertificate(t, []istanbul.View{view}, proposal)
    95  				preparedCertificate.PrepareOrCommitMessages[1] = preparedCertificate.PrepareOrCommitMessages[0]
    96  				preparedCertificate.Proposal = makeBlock(1)
    97  				return preparedCertificate
    98  			}(),
    99  			errInvalidPreparedCertificateDigestMismatch,
   100  			nil,
   101  		},
   102  		{
   103  			"Invalid PREPARED certificate, view inconsistencies",
   104  			func() istanbul.PreparedCertificate {
   105  				var view2 istanbul.View
   106  				view2.Sequence = big.NewInt(view.Sequence.Int64())
   107  				view2.Round = big.NewInt(view.Round.Int64() + 1)
   108  				preparedCertificate := sys.getPreparedCertificate(t, []istanbul.View{view, view2}, proposal)
   109  				return preparedCertificate
   110  			}(),
   111  			errInvalidPreparedCertificateInconsistentViews,
   112  			nil,
   113  		},
   114  		{
   115  			"Empty certificate",
   116  			istanbul.EmptyPreparedCertificate(),
   117  			errInvalidPreparedCertificateNumMsgs,
   118  			nil,
   119  		},
   120  	}
   121  	for _, test := range testCases {
   122  		t.Run(test.name, func(t *testing.T) {
   123  			for _, backend := range sys.backends {
   124  				c := backend.engine.(*core)
   125  				view, err := c.verifyPreparedCertificate(test.certificate)
   126  				if err != test.expectedErr {
   127  					t.Errorf("error mismatch: have %v, want %v", err, test.expectedErr)
   128  				}
   129  				if err == nil {
   130  					if view.Cmp(test.expectedView) != 0 {
   131  						t.Errorf("view mismatch: have %v, want %v", view, test.expectedView)
   132  					}
   133  					view, err := c.getViewFromVerifiedPreparedCertificate(test.certificate)
   134  					if err != nil {
   135  						t.Errorf("error mismatch: have %v, want nil", err)
   136  					}
   137  					if view.Cmp(test.expectedView) != 0 {
   138  						t.Errorf("view mismatch: have %v, want %v", view, test.expectedView)
   139  					}
   140  				}
   141  			}
   142  		})
   143  	}
   144  }
   145  
   146  func TestHandlePrepare(t *testing.T) {
   147  	N := uint64(4)
   148  	F := uint64(1)
   149  
   150  	proposal := newTestProposal()
   151  	expectedSubject := &istanbul.Subject{
   152  		View: &istanbul.View{
   153  			Round:    big.NewInt(0),
   154  			Sequence: proposal.Number(),
   155  		},
   156  		Digest: proposal.Hash(),
   157  	}
   158  
   159  	testCases := []struct {
   160  		name        string
   161  		system      *testSystem
   162  		expectedErr error
   163  	}{
   164  		{
   165  			"normal case",
   166  			func() *testSystem {
   167  				sys := NewTestSystemWithBackend(N, F)
   168  
   169  				for i, backend := range sys.backends {
   170  					c := backend.engine.(*core)
   171  
   172  					c.current = newTestRoundState(
   173  						&istanbul.View{
   174  							Round:    big.NewInt(0),
   175  							Sequence: big.NewInt(1),
   176  						},
   177  						backend.peers,
   178  					)
   179  
   180  					if i == 0 {
   181  						// replica 0 is the proposer
   182  						c.current.(*roundStateImpl).state = StatePreprepared
   183  					}
   184  				}
   185  				return sys
   186  			}(),
   187  			nil,
   188  		},
   189  		{
   190  			"normal case with prepared certificate",
   191  			func() *testSystem {
   192  				sys := NewTestSystemWithBackend(N, F)
   193  				preparedCert := sys.getPreparedCertificate(
   194  					t,
   195  					[]istanbul.View{
   196  						{
   197  							Round:    big.NewInt(0),
   198  							Sequence: big.NewInt(1),
   199  						},
   200  					},
   201  					proposal)
   202  
   203  				for i, backend := range sys.backends {
   204  					c := backend.engine.(*core)
   205  					c.current = newTestRoundState(
   206  						&istanbul.View{
   207  							Round:    big.NewInt(0),
   208  							Sequence: big.NewInt(1),
   209  						},
   210  						backend.peers,
   211  					)
   212  					c.current.(*roundStateImpl).preparedCertificate = preparedCert
   213  
   214  					if i == 0 {
   215  						// replica 0 is the proposer
   216  						c.current.(*roundStateImpl).state = StatePreprepared
   217  					}
   218  				}
   219  				return sys
   220  			}(),
   221  			nil,
   222  		},
   223  		{
   224  			"Inconsistent subject due to prepared certificate",
   225  			func() *testSystem {
   226  				sys := NewTestSystemWithBackend(N, F)
   227  				preparedCert := sys.getPreparedCertificate(
   228  					t,
   229  					[]istanbul.View{
   230  						{
   231  							Round:    big.NewInt(0),
   232  							Sequence: big.NewInt(10),
   233  						},
   234  					},
   235  					proposal)
   236  
   237  				for i, backend := range sys.backends {
   238  					c := backend.engine.(*core)
   239  					c.current = newTestRoundState(
   240  						&istanbul.View{
   241  							Round:    big.NewInt(0),
   242  							Sequence: big.NewInt(1),
   243  						},
   244  						backend.peers,
   245  					)
   246  					c.current.(*roundStateImpl).preparedCertificate = preparedCert
   247  
   248  					if i == 0 {
   249  						// replica 0 is the proposer
   250  						c.current.(*roundStateImpl).state = StatePreprepared
   251  					}
   252  				}
   253  				return sys
   254  			}(),
   255  			errInconsistentSubject,
   256  		},
   257  		{
   258  			"future message",
   259  			func() *testSystem {
   260  				sys := NewTestSystemWithBackend(N, F)
   261  
   262  				for i, backend := range sys.backends {
   263  					c := backend.engine.(*core)
   264  					if i == 0 {
   265  						// replica 0 is the proposer
   266  						c.current = newTestRoundState(
   267  							expectedSubject.View,
   268  							backend.peers,
   269  						)
   270  						c.current.(*roundStateImpl).state = StatePreprepared
   271  					} else {
   272  						c.current = newTestRoundState(
   273  							&istanbul.View{
   274  								Round:    big.NewInt(2),
   275  								Sequence: big.NewInt(3),
   276  							},
   277  							backend.peers,
   278  						)
   279  					}
   280  				}
   281  				return sys
   282  			}(),
   283  			errFutureMessage,
   284  		},
   285  		{
   286  			"subject not match",
   287  			func() *testSystem {
   288  				sys := NewTestSystemWithBackend(N, F)
   289  
   290  				for i, backend := range sys.backends {
   291  					c := backend.engine.(*core)
   292  					if i == 0 {
   293  						// replica 0 is the proposer
   294  						c.current = newTestRoundState(
   295  							expectedSubject.View,
   296  							backend.peers,
   297  						)
   298  						c.current.(*roundStateImpl).state = StatePreprepared
   299  					} else {
   300  						c.current = newTestRoundState(
   301  							&istanbul.View{
   302  								Round:    big.NewInt(0),
   303  								Sequence: big.NewInt(0),
   304  							},
   305  							backend.peers,
   306  						)
   307  					}
   308  				}
   309  				return sys
   310  			}(),
   311  			errOldMessage,
   312  		},
   313  		{
   314  			"subject not match",
   315  			func() *testSystem {
   316  				sys := NewTestSystemWithBackend(N, F)
   317  
   318  				for i, backend := range sys.backends {
   319  					c := backend.engine.(*core)
   320  					if i == 0 {
   321  						// replica 0 is the proposer
   322  						c.current = newTestRoundState(
   323  							expectedSubject.View,
   324  							backend.peers,
   325  						)
   326  						c.current.(*roundStateImpl).state = StatePreprepared
   327  					} else {
   328  						c.current = newTestRoundState(
   329  							&istanbul.View{
   330  								Round:    big.NewInt(0),
   331  								Sequence: big.NewInt(1)},
   332  							backend.peers,
   333  						)
   334  					}
   335  				}
   336  				return sys
   337  			}(),
   338  			errInconsistentSubject,
   339  		},
   340  		{
   341  			"less than 2F+1",
   342  			func() *testSystem {
   343  				sys := NewTestSystemWithBackend(N, F)
   344  
   345  				// save less than 2*F+1 replica
   346  				sys.backends = sys.backends[2*int(F)+1:]
   347  
   348  				for i, backend := range sys.backends {
   349  					c := backend.engine.(*core)
   350  					c.current = newTestRoundState(
   351  						expectedSubject.View,
   352  						backend.peers,
   353  					)
   354  
   355  					if i == 0 {
   356  						// replica 0 is the proposer
   357  						c.current.(*roundStateImpl).state = StatePreprepared
   358  					}
   359  				}
   360  				return sys
   361  			}(),
   362  			nil,
   363  		},
   364  		// TODO: double send message
   365  	}
   366  
   367  	for _, test := range testCases {
   368  		t.Run(test.name, func(t *testing.T) {
   369  
   370  			test.system.Run(false)
   371  
   372  			v0 := test.system.backends[0]
   373  			r0 := v0.engine.(*core)
   374  
   375  			for i, v := range test.system.backends {
   376  				validator := r0.current.ValidatorSet().GetByIndex(uint64(i))
   377  				m, _ := Encode(v.engine.(*core).current.Subject())
   378  				err := r0.handlePrepare(&istanbul.Message{
   379  					Code:    istanbul.MsgPrepare,
   380  					Msg:     m,
   381  					Address: validator.Address(),
   382  				})
   383  				if err != nil {
   384  					if err != test.expectedErr {
   385  						t.Errorf("error mismatch: have %v, want %v", err, test.expectedErr)
   386  					}
   387  					return
   388  				}
   389  			}
   390  
   391  			// prepared is normal case
   392  			if r0.current.State() != StatePrepared {
   393  				// There are not enough PREPARE messages in core
   394  				if r0.current.State() != StatePreprepared {
   395  					t.Errorf("state mismatch: have %v, want %v", r0.current.State(), StatePreprepared)
   396  				}
   397  				if r0.current.Prepares().Size() >= r0.current.ValidatorSet().MinQuorumSize() {
   398  					t.Errorf("the size of PREPARE messages should be less than %v", 2*r0.current.ValidatorSet().MinQuorumSize()+1)
   399  				}
   400  
   401  				return
   402  			}
   403  
   404  			// core should have MinQuorumSize PREPARE messages
   405  			if r0.current.Prepares().Size() < r0.current.ValidatorSet().MinQuorumSize() {
   406  				t.Errorf("the size of PREPARE messages should be greater than or equal to MinQuorumSize: size %v", r0.current.Prepares().Size())
   407  			}
   408  
   409  			// a message will be delivered to backend if 2F+1
   410  			if int64(len(v0.sentMsgs)) != 1 {
   411  				t.Errorf("the Send() should be called once: times %v", len(test.system.backends[0].sentMsgs))
   412  			}
   413  
   414  			// verify COMMIT messages
   415  			decodedMsg := new(istanbul.Message)
   416  			err := decodedMsg.FromPayload(v0.sentMsgs[0], nil)
   417  			if err != nil {
   418  				t.Errorf("error mismatch: have %v, want nil", err)
   419  			}
   420  
   421  			if decodedMsg.Code != istanbul.MsgCommit {
   422  				t.Errorf("message code mismatch: have %v, want %v", decodedMsg.Code, istanbul.MsgCommit)
   423  			}
   424  			var m *istanbul.CommittedSubject
   425  			err = decodedMsg.Decode(&m)
   426  			if err != nil {
   427  				t.Errorf("error mismatch: have %v, want nil", err)
   428  			}
   429  			if !reflect.DeepEqual(m.Subject, expectedSubject) {
   430  				t.Errorf("subject mismatch: have %v, want %v", m, expectedSubject)
   431  			}
   432  		})
   433  	}
   434  }
   435  
   436  // round is not checked for now
   437  func TestVerifyPrepare(t *testing.T) {
   438  
   439  	// for log purpose
   440  	privateKey, _ := crypto.GenerateKey()
   441  	blsPrivateKey, _ := blscrypto.ECDSAToBLS(privateKey)
   442  	blsPublicKey, _ := blscrypto.PrivateToPublic(blsPrivateKey)
   443  	peer := validator.New(getPublicKeyAddress(privateKey), blsPublicKey)
   444  	valSet := validator.NewSet([]istanbul.ValidatorData{
   445  		{
   446  			peer.Address(),
   447  			blsPublicKey,
   448  		},
   449  	})
   450  
   451  	sys := NewTestSystemWithBackend(uint64(1), uint64(0))
   452  
   453  	testCases := []struct {
   454  		expected error
   455  
   456  		prepare    *istanbul.Subject
   457  		roundState RoundState
   458  	}{
   459  		{
   460  			// normal case
   461  			expected: nil,
   462  			prepare: &istanbul.Subject{
   463  				View:   &istanbul.View{Round: big.NewInt(0), Sequence: big.NewInt(0)},
   464  				Digest: newTestProposal().Hash(),
   465  			},
   466  			roundState: newTestRoundState(
   467  				&istanbul.View{Round: big.NewInt(0), Sequence: big.NewInt(0)},
   468  				valSet,
   469  			),
   470  		},
   471  		{
   472  			// old message
   473  			expected: errInconsistentSubject,
   474  			prepare: &istanbul.Subject{
   475  				View:   &istanbul.View{Round: big.NewInt(0), Sequence: big.NewInt(0)},
   476  				Digest: newTestProposal().Hash(),
   477  			},
   478  			roundState: newTestRoundState(
   479  				&istanbul.View{Round: big.NewInt(1), Sequence: big.NewInt(1)},
   480  				valSet,
   481  			),
   482  		},
   483  		{
   484  			// different digest
   485  			expected: errInconsistentSubject,
   486  			prepare: &istanbul.Subject{
   487  				View:   &istanbul.View{Round: big.NewInt(0), Sequence: big.NewInt(0)},
   488  				Digest: common.BytesToHash([]byte("1234567890")),
   489  			},
   490  			roundState: newTestRoundState(
   491  				&istanbul.View{Round: big.NewInt(1), Sequence: big.NewInt(1)},
   492  				valSet,
   493  			),
   494  		},
   495  		{
   496  			// malicious package(lack of sequence)
   497  			expected: errInconsistentSubject,
   498  			prepare: &istanbul.Subject{
   499  				View:   &istanbul.View{Round: big.NewInt(0), Sequence: nil},
   500  				Digest: newTestProposal().Hash(),
   501  			},
   502  			roundState: newTestRoundState(
   503  				&istanbul.View{Round: big.NewInt(1), Sequence: big.NewInt(1)},
   504  				valSet,
   505  			),
   506  		},
   507  		{
   508  			// wrong PREPARE message with same sequence but different round
   509  			expected: errInconsistentSubject,
   510  			prepare: &istanbul.Subject{
   511  				View:   &istanbul.View{Round: big.NewInt(1), Sequence: big.NewInt(0)},
   512  				Digest: newTestProposal().Hash(),
   513  			},
   514  			roundState: newTestRoundState(
   515  				&istanbul.View{Round: big.NewInt(0), Sequence: big.NewInt(0)},
   516  				valSet,
   517  			),
   518  		},
   519  		{
   520  			// wrong PREPARE message with same round but different sequence
   521  			expected: errInconsistentSubject,
   522  			prepare: &istanbul.Subject{
   523  				View:   &istanbul.View{Round: big.NewInt(0), Sequence: big.NewInt(1)},
   524  				Digest: newTestProposal().Hash(),
   525  			},
   526  			roundState: newTestRoundState(
   527  				&istanbul.View{Round: big.NewInt(0), Sequence: big.NewInt(0)},
   528  				valSet,
   529  			),
   530  		},
   531  	}
   532  	for i, test := range testCases {
   533  		c := sys.backends[0].engine.(*core)
   534  		c.current = test.roundState
   535  
   536  		if err := c.verifyPrepare(test.prepare); err != nil {
   537  			if err != test.expected {
   538  				t.Errorf("result %d: error mismatch: have %v, want %v", i, err, test.expected)
   539  			}
   540  		}
   541  	}
   542  }