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