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