github.com/arieschain/arieschain@v0.0.0-20191023063405-37c074544356/consensus/dbft/core/preprepare_test.go (about)

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