github.com/kisexp/xdchain@v0.0.0-20211206025815-490d6b732aa7/consensus/istanbul/ibft/core/preprepare_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/kisexp/xdchain/consensus/istanbul"
    25  	istanbulcommon "github.com/kisexp/xdchain/consensus/istanbul/common"
    26  	ibfttypes "github.com/kisexp/xdchain/consensus/istanbul/ibft/types"
    27  )
    28  
    29  func newTestPreprepare(v *istanbul.View) *istanbul.Preprepare {
    30  	return &istanbul.Preprepare{
    31  		View:     v,
    32  		Proposal: newTestProposal(),
    33  	}
    34  }
    35  
    36  func TestHandlePreprepare(t *testing.T) {
    37  	N := uint64(4) // replica 0 is the proposer, it will send messages to others
    38  	F := uint64(1) // F does not affect tests
    39  
    40  	testCases := []struct {
    41  		system          *testSystem
    42  		expectedRequest istanbul.Proposal
    43  		expectedErr     error
    44  		existingBlock   bool
    45  	}{
    46  		{
    47  			// normal case
    48  			func() *testSystem {
    49  				sys := NewTestSystemWithBackend(N, F)
    50  
    51  				for i, backend := range sys.backends {
    52  					c := backend.engine
    53  					c.valSet = backend.peers
    54  					if i != 0 {
    55  						c.state = ibfttypes.StateAcceptRequest
    56  					}
    57  				}
    58  				return sys
    59  			}(),
    60  			newTestProposal(),
    61  			nil,
    62  			false,
    63  		},
    64  		{
    65  			// future message
    66  			func() *testSystem {
    67  				sys := NewTestSystemWithBackend(N, F)
    68  
    69  				for i, backend := range sys.backends {
    70  					c := backend.engine
    71  					c.valSet = backend.peers
    72  					if i != 0 {
    73  						c.state = ibfttypes.StateAcceptRequest
    74  						// hack: force set subject that future message can be simulated
    75  						c.current = newTestRoundState(
    76  							&istanbul.View{
    77  								Round:    big.NewInt(0),
    78  								Sequence: big.NewInt(0),
    79  							},
    80  							c.valSet,
    81  						)
    82  
    83  					} else {
    84  						c.current.SetSequence(big.NewInt(10))
    85  					}
    86  				}
    87  				return sys
    88  			}(),
    89  			makeBlock(1),
    90  			istanbulcommon.ErrFutureMessage,
    91  			false,
    92  		},
    93  		{
    94  			// non-proposer
    95  			func() *testSystem {
    96  				sys := NewTestSystemWithBackend(N, F)
    97  
    98  				// force remove replica 0, let replica 1 be the proposer
    99  				sys.backends = sys.backends[1:]
   100  
   101  				for i, backend := range sys.backends {
   102  					c := backend.engine
   103  					c.valSet = backend.peers
   104  					if i != 0 {
   105  						// replica 0 is the proposer
   106  						c.state = ibfttypes.StatePreprepared
   107  					}
   108  				}
   109  				return sys
   110  			}(),
   111  			makeBlock(1),
   112  			istanbulcommon.ErrNotFromProposer,
   113  			false,
   114  		},
   115  		{
   116  			// errOldMessage
   117  			func() *testSystem {
   118  				sys := NewTestSystemWithBackend(N, F)
   119  
   120  				for i, backend := range sys.backends {
   121  					c := backend.engine
   122  					c.valSet = backend.peers
   123  					if i != 0 {
   124  						c.state = ibfttypes.StatePreprepared
   125  						c.current.SetSequence(big.NewInt(10))
   126  						c.current.SetRound(big.NewInt(10))
   127  					}
   128  				}
   129  				return sys
   130  			}(),
   131  			makeBlock(1),
   132  			istanbulcommon.ErrOldMessage,
   133  			false,
   134  		},
   135  	}
   136  
   137  OUTER:
   138  	for _, test := range testCases {
   139  		test.system.Run(false)
   140  
   141  		v0 := test.system.backends[0]
   142  		r0 := v0.engine
   143  
   144  		curView := r0.currentView()
   145  
   146  		preprepare := &istanbul.Preprepare{
   147  			View:     curView,
   148  			Proposal: test.expectedRequest,
   149  		}
   150  
   151  		for i, v := range test.system.backends {
   152  			// i == 0 is primary backend, it is responsible for send PRE-PREPARE messages to others.
   153  			if i == 0 {
   154  				continue
   155  			}
   156  
   157  			c := v.engine
   158  
   159  			m, _ := ibfttypes.Encode(preprepare)
   160  			_, val := r0.valSet.GetByAddress(v0.Address())
   161  			// run each backends and verify handlePreprepare function.
   162  			if err := c.handlePreprepare(&ibfttypes.Message{
   163  				Code:    ibfttypes.MsgPreprepare,
   164  				Msg:     m,
   165  				Address: v0.Address(),
   166  			}, val); err != nil {
   167  				if err != test.expectedErr {
   168  					t.Errorf("error mismatch: have %v, want %v", err, test.expectedErr)
   169  				}
   170  				continue OUTER
   171  			}
   172  
   173  			if c.state != ibfttypes.StatePreprepared {
   174  				t.Errorf("state mismatch: have %v, want %v", c.state, ibfttypes.StatePreprepared)
   175  			}
   176  
   177  			if !test.existingBlock && !reflect.DeepEqual(c.current.Subject().View, curView) {
   178  				t.Errorf("view mismatch: have %v, want %v", c.current.Subject().View, curView)
   179  			}
   180  
   181  			// verify prepare messages
   182  			decodedMsg := new(ibfttypes.Message)
   183  			err := decodedMsg.FromPayload(v.sentMsgs[0], nil)
   184  			if err != nil {
   185  				t.Errorf("error mismatch: have %v, want nil", err)
   186  			}
   187  
   188  			expectedCode := ibfttypes.MsgPrepare
   189  			if test.existingBlock {
   190  				expectedCode = ibfttypes.MsgCommit
   191  			}
   192  			if decodedMsg.Code != expectedCode {
   193  				t.Errorf("message code mismatch: have %v, want %v", decodedMsg.Code, expectedCode)
   194  			}
   195  
   196  			var subject *istanbul.Subject
   197  			err = decodedMsg.Decode(&subject)
   198  			if err != nil {
   199  				t.Errorf("error mismatch: have %v, want nil", err)
   200  			}
   201  			if !test.existingBlock && !reflect.DeepEqual(subject, c.current.Subject()) {
   202  				t.Errorf("subject mismatch: have %v, want %v", subject, c.current.Subject())
   203  			}
   204  
   205  		}
   206  	}
   207  }
   208  
   209  func TestHandlePreprepareWithLock(t *testing.T) {
   210  	N := uint64(4) // replica 0 is the proposer, it will send messages to others
   211  	F := uint64(1) // F does not affect tests
   212  	proposal := newTestProposal()
   213  	mismatchProposal := makeBlock(10)
   214  	newSystem := func() *testSystem {
   215  		sys := NewTestSystemWithBackend(N, F)
   216  
   217  		for i, backend := range sys.backends {
   218  			c := backend.engine
   219  			c.valSet = backend.peers
   220  			if i != 0 {
   221  				c.state = ibfttypes.StateAcceptRequest
   222  			}
   223  			c.roundChangeSet = newRoundChangeSet(c.valSet)
   224  		}
   225  		return sys
   226  	}
   227  
   228  	testCases := []struct {
   229  		system       *testSystem
   230  		proposal     istanbul.Proposal
   231  		lockProposal istanbul.Proposal
   232  	}{
   233  		{
   234  			newSystem(),
   235  			proposal,
   236  			proposal,
   237  		},
   238  		{
   239  			newSystem(),
   240  			proposal,
   241  			mismatchProposal,
   242  		},
   243  	}
   244  
   245  	for _, test := range testCases {
   246  		test.system.Run(false)
   247  		v0 := test.system.backends[0]
   248  		r0 := v0.engine
   249  		curView := r0.currentView()
   250  		preprepare := &istanbul.Preprepare{
   251  			View:     curView,
   252  			Proposal: test.proposal,
   253  		}
   254  		lockPreprepare := &istanbul.Preprepare{
   255  			View:     curView,
   256  			Proposal: test.lockProposal,
   257  		}
   258  
   259  		for i, v := range test.system.backends {
   260  			// i == 0 is primary backend, it is responsible for send PRE-PREPARE messages to others.
   261  			if i == 0 {
   262  				continue
   263  			}
   264  
   265  			c := v.engine
   266  			c.current.SetPreprepare(lockPreprepare)
   267  			c.current.LockHash()
   268  			m, _ := ibfttypes.Encode(preprepare)
   269  			_, val := r0.valSet.GetByAddress(v0.Address())
   270  			if err := c.handlePreprepare(&ibfttypes.Message{
   271  				Code:    ibfttypes.MsgPreprepare,
   272  				Msg:     m,
   273  				Address: v0.Address(),
   274  			}, val); err != nil {
   275  				t.Errorf("error mismatch: have %v, want nil", err)
   276  			}
   277  			if test.proposal == test.lockProposal {
   278  				if c.state != ibfttypes.StatePrepared {
   279  					t.Errorf("state mismatch: have %v, want %v", c.state, ibfttypes.StatePreprepared)
   280  				}
   281  				if !reflect.DeepEqual(curView, c.currentView()) {
   282  					t.Errorf("view mismatch: have %v, want %v", c.currentView(), curView)
   283  				}
   284  			} else {
   285  				// Should stay at ibfttypes.StateAcceptRequest
   286  				if c.state != ibfttypes.StateAcceptRequest {
   287  					t.Errorf("state mismatch: have %v, want %v", c.state, ibfttypes.StateAcceptRequest)
   288  				}
   289  				// Should have triggered a round change
   290  				expectedView := &istanbul.View{
   291  					Sequence: curView.Sequence,
   292  					Round:    big.NewInt(1),
   293  				}
   294  				if !reflect.DeepEqual(expectedView, c.currentView()) {
   295  					t.Errorf("view mismatch: have %v, want %v", c.currentView(), expectedView)
   296  				}
   297  			}
   298  		}
   299  	}
   300  }