github.com/ConsenSys/Quorum@v20.10.0+incompatible/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"
    21  	"math/big"
    22  	"reflect"
    23  	"testing"
    24  
    25  	"github.com/ethereum/go-ethereum/common"
    26  	"github.com/ethereum/go-ethereum/consensus/istanbul"
    27  	"github.com/ethereum/go-ethereum/consensus/istanbul/validator"
    28  	"github.com/ethereum/go-ethereum/crypto"
    29  )
    30  
    31  func TestHandlePrepare(t *testing.T) {
    32  	N := uint64(4)
    33  	F := uint64(1)
    34  
    35  	proposal := newTestProposal()
    36  	expectedSubject := &istanbul.Subject{
    37  		View: &istanbul.View{
    38  			Round:    big.NewInt(0),
    39  			Sequence: proposal.Number(),
    40  		},
    41  		Digest: proposal.Hash(),
    42  	}
    43  
    44  	testCases := []struct {
    45  		system      *testSystem
    46  		expectedErr error
    47  	}{
    48  		{
    49  			// normal case
    50  			func() *testSystem {
    51  				sys := NewTestSystemWithBackend(N, F)
    52  
    53  				for i, backend := range sys.backends {
    54  					c := backend.engine.(*core)
    55  					c.valSet = backend.peers
    56  					c.current = newTestRoundState(
    57  						&istanbul.View{
    58  							Round:    big.NewInt(0),
    59  							Sequence: big.NewInt(1),
    60  						},
    61  						c.valSet,
    62  					)
    63  
    64  					if i == 0 {
    65  						// replica 0 is the proposer
    66  						c.state = StatePreprepared
    67  					}
    68  				}
    69  				return sys
    70  			}(),
    71  			nil,
    72  		},
    73  		{
    74  			// future message
    75  			func() *testSystem {
    76  				sys := NewTestSystemWithBackend(N, F)
    77  
    78  				for i, backend := range sys.backends {
    79  					c := backend.engine.(*core)
    80  					c.valSet = backend.peers
    81  					if i == 0 {
    82  						// replica 0 is the proposer
    83  						c.current = newTestRoundState(
    84  							expectedSubject.View,
    85  							c.valSet,
    86  						)
    87  						c.state = StatePreprepared
    88  					} else {
    89  						c.current = newTestRoundState(
    90  							&istanbul.View{
    91  								Round:    big.NewInt(2),
    92  								Sequence: big.NewInt(3),
    93  							},
    94  							c.valSet,
    95  						)
    96  					}
    97  				}
    98  				return sys
    99  			}(),
   100  			errFutureMessage,
   101  		},
   102  		{
   103  			// subject not match
   104  			func() *testSystem {
   105  				sys := NewTestSystemWithBackend(N, F)
   106  
   107  				for i, backend := range sys.backends {
   108  					c := backend.engine.(*core)
   109  					c.valSet = backend.peers
   110  					if i == 0 {
   111  						// replica 0 is the proposer
   112  						c.current = newTestRoundState(
   113  							expectedSubject.View,
   114  							c.valSet,
   115  						)
   116  						c.state = StatePreprepared
   117  					} else {
   118  						c.current = newTestRoundState(
   119  							&istanbul.View{
   120  								Round:    big.NewInt(0),
   121  								Sequence: big.NewInt(0),
   122  							},
   123  							c.valSet,
   124  						)
   125  					}
   126  				}
   127  				return sys
   128  			}(),
   129  			errOldMessage,
   130  		},
   131  		{
   132  			// subject not match
   133  			func() *testSystem {
   134  				sys := NewTestSystemWithBackend(N, F)
   135  
   136  				for i, backend := range sys.backends {
   137  					c := backend.engine.(*core)
   138  					c.valSet = backend.peers
   139  					if i == 0 {
   140  						// replica 0 is the proposer
   141  						c.current = newTestRoundState(
   142  							expectedSubject.View,
   143  							c.valSet,
   144  						)
   145  						c.state = StatePreprepared
   146  					} else {
   147  						c.current = newTestRoundState(
   148  							&istanbul.View{
   149  								Round:    big.NewInt(0),
   150  								Sequence: big.NewInt(1)},
   151  							c.valSet,
   152  						)
   153  					}
   154  				}
   155  				return sys
   156  			}(),
   157  			errInconsistentSubject,
   158  		},
   159  		{
   160  			func() *testSystem {
   161  				sys := NewTestSystemWithBackend(N, F)
   162  
   163  				// save less than Ceil(2*N/3) replica
   164  				sys.backends = sys.backends[int(math.Ceil(float64(2*N)/3)):]
   165  
   166  				for i, backend := range sys.backends {
   167  					c := backend.engine.(*core)
   168  					c.valSet = backend.peers
   169  					c.current = newTestRoundState(
   170  						expectedSubject.View,
   171  						c.valSet,
   172  					)
   173  
   174  					if i == 0 {
   175  						// replica 0 is the proposer
   176  						c.state = StatePreprepared
   177  					}
   178  				}
   179  				return sys
   180  			}(),
   181  			nil,
   182  		},
   183  		// TODO: double send message
   184  	}
   185  
   186  OUTER:
   187  	for _, test := range testCases {
   188  		test.system.Run(false)
   189  
   190  		v0 := test.system.backends[0]
   191  		r0 := v0.engine.(*core)
   192  
   193  		for i, v := range test.system.backends {
   194  			validator := r0.valSet.GetByIndex(uint64(i))
   195  			m, _ := Encode(v.engine.(*core).current.Subject())
   196  			if err := r0.handlePrepare(&message{
   197  				Code:    msgPrepare,
   198  				Msg:     m,
   199  				Address: validator.Address(),
   200  			}, validator); err != nil {
   201  				if err != test.expectedErr {
   202  					t.Errorf("error mismatch: have %v, want %v", err, test.expectedErr)
   203  				}
   204  				if r0.current.IsHashLocked() {
   205  					t.Errorf("block should not be locked")
   206  				}
   207  				continue OUTER
   208  			}
   209  		}
   210  
   211  		// prepared is normal case
   212  		if r0.state != StatePrepared {
   213  			// There are not enough PREPARE messages in core
   214  			if r0.state != StatePreprepared {
   215  				t.Errorf("state mismatch: have %v, want %v", r0.state, StatePreprepared)
   216  			}
   217  			if r0.current.Prepares.Size() >= r0.QuorumSize() {
   218  				t.Errorf("the size of PREPARE messages should be less than %v", r0.QuorumSize())
   219  			}
   220  			if r0.current.IsHashLocked() {
   221  				t.Errorf("block should not be locked")
   222  			}
   223  
   224  			continue
   225  		}
   226  
   227  		// core should have 2F+1 before Ceil2Nby3Block and Ceil(2N/3) after Ceil2Nby3Block PREPARE messages
   228  		if r0.current.Prepares.Size() < r0.QuorumSize() {
   229  			t.Errorf("the size of PREPARE messages should be larger than 2F+1 or ceil(2N/3): size %v", r0.current.Commits.Size())
   230  		}
   231  
   232  		// a message will be delivered to backend if ceil(2N/3)
   233  		if int64(len(v0.sentMsgs)) != 1 {
   234  			t.Errorf("the Send() should be called once: times %v", len(test.system.backends[0].sentMsgs))
   235  		}
   236  
   237  		// verify COMMIT messages
   238  		decodedMsg := new(message)
   239  		err := decodedMsg.FromPayload(v0.sentMsgs[0], nil)
   240  		if err != nil {
   241  			t.Errorf("error mismatch: have %v, want nil", err)
   242  		}
   243  
   244  		if decodedMsg.Code != msgCommit {
   245  			t.Errorf("message code mismatch: have %v, want %v", decodedMsg.Code, msgCommit)
   246  		}
   247  		var m *istanbul.Subject
   248  		err = decodedMsg.Decode(&m)
   249  		if err != nil {
   250  			t.Errorf("error mismatch: have %v, want nil", err)
   251  		}
   252  		if !reflect.DeepEqual(m, expectedSubject) {
   253  			t.Errorf("subject mismatch: have %v, want %v", m, expectedSubject)
   254  		}
   255  		if !r0.current.IsHashLocked() {
   256  			t.Errorf("block should be locked")
   257  		}
   258  	}
   259  }
   260  
   261  // round is not checked for now
   262  func TestVerifyPrepare(t *testing.T) {
   263  	// for log purpose
   264  	privateKey, _ := crypto.GenerateKey()
   265  	peer := validator.New(getPublicKeyAddress(privateKey))
   266  	valSet := validator.NewSet([]common.Address{peer.Address()}, istanbul.RoundRobin)
   267  
   268  	sys := NewTestSystemWithBackend(uint64(1), uint64(0))
   269  
   270  	testCases := []struct {
   271  		expected error
   272  
   273  		prepare    *istanbul.Subject
   274  		roundState *roundState
   275  	}{
   276  		{
   277  			// normal case
   278  			expected: nil,
   279  			prepare: &istanbul.Subject{
   280  				View:   &istanbul.View{Round: big.NewInt(0), Sequence: big.NewInt(0)},
   281  				Digest: newTestProposal().Hash(),
   282  			},
   283  			roundState: newTestRoundState(
   284  				&istanbul.View{Round: big.NewInt(0), Sequence: big.NewInt(0)},
   285  				valSet,
   286  			),
   287  		},
   288  		{
   289  			// old message
   290  			expected: errInconsistentSubject,
   291  			prepare: &istanbul.Subject{
   292  				View:   &istanbul.View{Round: big.NewInt(0), Sequence: big.NewInt(0)},
   293  				Digest: newTestProposal().Hash(),
   294  			},
   295  			roundState: newTestRoundState(
   296  				&istanbul.View{Round: big.NewInt(1), Sequence: big.NewInt(1)},
   297  				valSet,
   298  			),
   299  		},
   300  		{
   301  			// different digest
   302  			expected: errInconsistentSubject,
   303  			prepare: &istanbul.Subject{
   304  				View:   &istanbul.View{Round: big.NewInt(0), Sequence: big.NewInt(0)},
   305  				Digest: common.StringToHash("1234567890"),
   306  			},
   307  			roundState: newTestRoundState(
   308  				&istanbul.View{Round: big.NewInt(1), Sequence: big.NewInt(1)},
   309  				valSet,
   310  			),
   311  		},
   312  		{
   313  			// malicious package(lack of sequence)
   314  			expected: errInconsistentSubject,
   315  			prepare: &istanbul.Subject{
   316  				View:   &istanbul.View{Round: big.NewInt(0), Sequence: nil},
   317  				Digest: newTestProposal().Hash(),
   318  			},
   319  			roundState: newTestRoundState(
   320  				&istanbul.View{Round: big.NewInt(1), Sequence: big.NewInt(1)},
   321  				valSet,
   322  			),
   323  		},
   324  		{
   325  			// wrong PREPARE message with same sequence but different round
   326  			expected: errInconsistentSubject,
   327  			prepare: &istanbul.Subject{
   328  				View:   &istanbul.View{Round: big.NewInt(1), Sequence: big.NewInt(0)},
   329  				Digest: newTestProposal().Hash(),
   330  			},
   331  			roundState: newTestRoundState(
   332  				&istanbul.View{Round: big.NewInt(0), Sequence: big.NewInt(0)},
   333  				valSet,
   334  			),
   335  		},
   336  		{
   337  			// wrong PREPARE message with same round but different sequence
   338  			expected: errInconsistentSubject,
   339  			prepare: &istanbul.Subject{
   340  				View:   &istanbul.View{Round: big.NewInt(0), Sequence: big.NewInt(1)},
   341  				Digest: newTestProposal().Hash(),
   342  			},
   343  			roundState: newTestRoundState(
   344  				&istanbul.View{Round: big.NewInt(0), Sequence: big.NewInt(0)},
   345  				valSet,
   346  			),
   347  		},
   348  	}
   349  	for i, test := range testCases {
   350  		c := sys.backends[0].engine.(*core)
   351  		c.current = test.roundState
   352  
   353  		if err := c.verifyPrepare(test.prepare, peer); err != nil {
   354  			if err != test.expected {
   355  				t.Errorf("result %d: error mismatch: have %v, want %v", i, err, test.expected)
   356  			}
   357  		}
   358  	}
   359  }