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