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