github.com/kisexp/xdchain@v0.0.0-20211206025815-490d6b732aa7/consensus/istanbul/ibft/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/kisexp/xdchain/common"
    25  	"github.com/kisexp/xdchain/consensus/istanbul"
    26  	istanbulcommon "github.com/kisexp/xdchain/consensus/istanbul/common"
    27  	ibfttypes "github.com/kisexp/xdchain/consensus/istanbul/ibft/types"
    28  	"github.com/kisexp/xdchain/consensus/istanbul/validator"
    29  	"github.com/kisexp/xdchain/crypto"
    30  )
    31  
    32  func TestHandleCommit(t *testing.T) {
    33  	N := uint64(4)
    34  	F := uint64(1)
    35  
    36  	proposal := newTestProposal()
    37  	expectedSubject := &istanbul.Subject{
    38  		View: &istanbul.View{
    39  			Round:    big.NewInt(0),
    40  			Sequence: proposal.Number(),
    41  		},
    42  		Digest: proposal.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
    56  					c.valSet = backend.peers
    57  					c.current = newTestRoundState(
    58  						&istanbul.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 = ibfttypes.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
    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 = ibfttypes.StatePreprepared
    89  					} else {
    90  						c.current = newTestRoundState(
    91  							&istanbul.View{
    92  								Round:    big.NewInt(2),
    93  								Sequence: big.NewInt(3),
    94  							},
    95  							c.valSet,
    96  						)
    97  					}
    98  				}
    99  				return sys
   100  			}(),
   101  			istanbulcommon.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
   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 = ibfttypes.StatePreprepared
   118  					} else {
   119  						c.current = newTestRoundState(
   120  							&istanbul.View{
   121  								Round:    big.NewInt(0),
   122  								Sequence: big.NewInt(0),
   123  							},
   124  							c.valSet,
   125  						)
   126  					}
   127  				}
   128  				return sys
   129  			}(),
   130  			istanbulcommon.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
   139  					c.valSet = backend.peers
   140  					c.current = newTestRoundState(
   141  						&istanbul.View{
   142  							Round:    big.NewInt(0),
   143  							Sequence: proposal.Number(),
   144  						},
   145  						c.valSet,
   146  					)
   147  
   148  					// only replica0 stays at ibfttypes.StatePreprepared
   149  					// other replicas are at ibfttypes.StatePrepared
   150  					if i != 0 {
   151  						c.state = ibfttypes.StatePrepared
   152  					} else {
   153  						c.state = ibfttypes.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
   169  
   170  		for i, v := range test.system.backends {
   171  			validator := r0.valSet.GetByIndex(uint64(i))
   172  			m, _ := ibfttypes.Encode(v.engine.current.Subject())
   173  			if err := r0.handleCommit(&ibfttypes.Message{
   174  				Code:          ibfttypes.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 != ibfttypes.StateCommitted {
   192  			// There are not enough commit messages in core
   193  			if r0.state != ibfttypes.StatePrepared {
   194  				t.Errorf("state mismatch: have %v, want %v", r0.state, ibfttypes.StatePrepared)
   195  			}
   196  			if r0.current.Commits.Size() >= r0.QuorumSize() {
   197  				t.Errorf("the size of commit messages should be less than %v", r0.QuorumSize())
   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.QuorumSize() {
   207  			t.Errorf("the size of commit messages should be larger than 2F+1 or Ceil(2N/3): size %v", r0.QuorumSize())
   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()}, istanbul.NewRoundRobinProposerPolicy())
   236  
   237  	sys := NewTestSystemWithBackend(uint64(1), uint64(0))
   238  
   239  	testCases := []struct {
   240  		expected   error
   241  		commit     *istanbul.Subject
   242  		roundState *roundState
   243  	}{
   244  		{
   245  			// normal case
   246  			expected: nil,
   247  			commit: &istanbul.Subject{
   248  				View:   &istanbul.View{Round: big.NewInt(0), Sequence: big.NewInt(0)},
   249  				Digest: newTestProposal().Hash(),
   250  			},
   251  			roundState: newTestRoundState(
   252  				&istanbul.View{Round: big.NewInt(0), Sequence: big.NewInt(0)},
   253  				valSet,
   254  			),
   255  		},
   256  		{
   257  			// old message
   258  			expected: istanbulcommon.ErrInconsistentSubject,
   259  			commit: &istanbul.Subject{
   260  				View:   &istanbul.View{Round: big.NewInt(0), Sequence: big.NewInt(0)},
   261  				Digest: newTestProposal().Hash(),
   262  			},
   263  			roundState: newTestRoundState(
   264  				&istanbul.View{Round: big.NewInt(1), Sequence: big.NewInt(1)},
   265  				valSet,
   266  			),
   267  		},
   268  		{
   269  			// different digest
   270  			expected: istanbulcommon.ErrInconsistentSubject,
   271  			commit: &istanbul.Subject{
   272  				View:   &istanbul.View{Round: big.NewInt(0), Sequence: big.NewInt(0)},
   273  				Digest: common.StringToHash("1234567890"),
   274  			},
   275  			roundState: newTestRoundState(
   276  				&istanbul.View{Round: big.NewInt(1), Sequence: big.NewInt(1)},
   277  				valSet,
   278  			),
   279  		},
   280  		{
   281  			// malicious package(lack of sequence)
   282  			expected: istanbulcommon.ErrInconsistentSubject,
   283  			commit: &istanbul.Subject{
   284  				View:   &istanbul.View{Round: big.NewInt(0), Sequence: nil},
   285  				Digest: newTestProposal().Hash(),
   286  			},
   287  			roundState: newTestRoundState(
   288  				&istanbul.View{Round: big.NewInt(1), Sequence: big.NewInt(1)},
   289  				valSet,
   290  			),
   291  		},
   292  		{
   293  			// wrong prepare message with same sequence but different round
   294  			expected: istanbulcommon.ErrInconsistentSubject,
   295  			commit: &istanbul.Subject{
   296  				View:   &istanbul.View{Round: big.NewInt(1), Sequence: big.NewInt(0)},
   297  				Digest: newTestProposal().Hash(),
   298  			},
   299  			roundState: newTestRoundState(
   300  				&istanbul.View{Round: big.NewInt(0), Sequence: big.NewInt(0)},
   301  				valSet,
   302  			),
   303  		},
   304  		{
   305  			// wrong prepare message with same round but different sequence
   306  			expected: istanbulcommon.ErrInconsistentSubject,
   307  			commit: &istanbul.Subject{
   308  				View:   &istanbul.View{Round: big.NewInt(0), Sequence: big.NewInt(1)},
   309  				Digest: newTestProposal().Hash(),
   310  			},
   311  			roundState: newTestRoundState(
   312  				&istanbul.View{Round: big.NewInt(0), Sequence: big.NewInt(0)},
   313  				valSet,
   314  			),
   315  		},
   316  	}
   317  	for i, test := range testCases {
   318  		c := sys.backends[0].engine
   319  		c.current = test.roundState
   320  
   321  		if err := c.verifyCommit(test.commit, peer); err != nil {
   322  			if err != test.expected {
   323  				t.Errorf("result %d: error mismatch: have %v, want %v", i, err, test.expected)
   324  			}
   325  		}
   326  	}
   327  }