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 }