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