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