github.com/ConsenSys/Quorum@v20.10.0+incompatible/consensus/istanbul/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/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 TestHandleCommit(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 = StatePrepared 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 // jump state 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 c.current = newTestRoundState( 139 &istanbul.View{ 140 Round: big.NewInt(0), 141 Sequence: proposal.Number(), 142 }, 143 c.valSet, 144 ) 145 146 // only replica0 stays at StatePreprepared 147 // other replicas are at StatePrepared 148 if i != 0 { 149 c.state = StatePrepared 150 } else { 151 c.state = StatePreprepared 152 } 153 } 154 return sys 155 }(), 156 nil, 157 }, 158 // TODO: double send message 159 } 160 161 OUTER: 162 for _, test := range testCases { 163 test.system.Run(false) 164 165 v0 := test.system.backends[0] 166 r0 := v0.engine.(*core) 167 168 for i, v := range test.system.backends { 169 validator := r0.valSet.GetByIndex(uint64(i)) 170 m, _ := Encode(v.engine.(*core).current.Subject()) 171 if err := r0.handleCommit(&message{ 172 Code: msgCommit, 173 Msg: m, 174 Address: validator.Address(), 175 Signature: []byte{}, 176 CommittedSeal: validator.Address().Bytes(), // small hack 177 }, validator); err != nil { 178 if err != test.expectedErr { 179 t.Errorf("error mismatch: have %v, want %v", err, test.expectedErr) 180 } 181 if r0.current.IsHashLocked() { 182 t.Errorf("block should not be locked") 183 } 184 continue OUTER 185 } 186 } 187 188 // prepared is normal case 189 if r0.state != StateCommitted { 190 // There are not enough commit messages in core 191 if r0.state != StatePrepared { 192 t.Errorf("state mismatch: have %v, want %v", r0.state, StatePrepared) 193 } 194 if r0.current.Commits.Size() >= r0.QuorumSize() { 195 t.Errorf("the size of commit messages should be less than %v", r0.QuorumSize()) 196 } 197 if r0.current.IsHashLocked() { 198 t.Errorf("block should not be locked") 199 } 200 continue 201 } 202 203 // core should have 2F+1 before Ceil2Nby3Block or Ceil(2N/3) prepare messages 204 if r0.current.Commits.Size() < r0.QuorumSize() { 205 t.Errorf("the size of commit messages should be larger than 2F+1 or Ceil(2N/3): size %v", r0.QuorumSize()) 206 } 207 208 // check signatures large than F 209 signedCount := 0 210 committedSeals := v0.committedMsgs[0].committedSeals 211 for _, validator := range r0.valSet.List() { 212 for _, seal := range committedSeals { 213 if bytes.Equal(validator.Address().Bytes(), seal[:common.AddressLength]) { 214 signedCount++ 215 break 216 } 217 } 218 } 219 if signedCount <= r0.valSet.F() { 220 t.Errorf("the expected signed count should be larger than %v, but got %v", r0.valSet.F(), signedCount) 221 } 222 if !r0.current.IsHashLocked() { 223 t.Errorf("block should be locked") 224 } 225 } 226 } 227 228 // round is not checked for now 229 func TestVerifyCommit(t *testing.T) { 230 // for log purpose 231 privateKey, _ := crypto.GenerateKey() 232 peer := validator.New(getPublicKeyAddress(privateKey)) 233 valSet := validator.NewSet([]common.Address{peer.Address()}, istanbul.RoundRobin) 234 235 sys := NewTestSystemWithBackend(uint64(1), uint64(0)) 236 237 testCases := []struct { 238 expected error 239 commit *istanbul.Subject 240 roundState *roundState 241 }{ 242 { 243 // normal case 244 expected: nil, 245 commit: &istanbul.Subject{ 246 View: &istanbul.View{Round: big.NewInt(0), Sequence: big.NewInt(0)}, 247 Digest: newTestProposal().Hash(), 248 }, 249 roundState: newTestRoundState( 250 &istanbul.View{Round: big.NewInt(0), Sequence: big.NewInt(0)}, 251 valSet, 252 ), 253 }, 254 { 255 // old message 256 expected: errInconsistentSubject, 257 commit: &istanbul.Subject{ 258 View: &istanbul.View{Round: big.NewInt(0), Sequence: big.NewInt(0)}, 259 Digest: newTestProposal().Hash(), 260 }, 261 roundState: newTestRoundState( 262 &istanbul.View{Round: big.NewInt(1), Sequence: big.NewInt(1)}, 263 valSet, 264 ), 265 }, 266 { 267 // different digest 268 expected: errInconsistentSubject, 269 commit: &istanbul.Subject{ 270 View: &istanbul.View{Round: big.NewInt(0), Sequence: big.NewInt(0)}, 271 Digest: common.StringToHash("1234567890"), 272 }, 273 roundState: newTestRoundState( 274 &istanbul.View{Round: big.NewInt(1), Sequence: big.NewInt(1)}, 275 valSet, 276 ), 277 }, 278 { 279 // malicious package(lack of sequence) 280 expected: errInconsistentSubject, 281 commit: &istanbul.Subject{ 282 View: &istanbul.View{Round: big.NewInt(0), Sequence: nil}, 283 Digest: newTestProposal().Hash(), 284 }, 285 roundState: newTestRoundState( 286 &istanbul.View{Round: big.NewInt(1), Sequence: big.NewInt(1)}, 287 valSet, 288 ), 289 }, 290 { 291 // wrong prepare message with same sequence but different round 292 expected: errInconsistentSubject, 293 commit: &istanbul.Subject{ 294 View: &istanbul.View{Round: big.NewInt(1), Sequence: big.NewInt(0)}, 295 Digest: newTestProposal().Hash(), 296 }, 297 roundState: newTestRoundState( 298 &istanbul.View{Round: big.NewInt(0), Sequence: big.NewInt(0)}, 299 valSet, 300 ), 301 }, 302 { 303 // wrong prepare message with same round but different sequence 304 expected: errInconsistentSubject, 305 commit: &istanbul.Subject{ 306 View: &istanbul.View{Round: big.NewInt(0), Sequence: big.NewInt(1)}, 307 Digest: newTestProposal().Hash(), 308 }, 309 roundState: newTestRoundState( 310 &istanbul.View{Round: big.NewInt(0), Sequence: big.NewInt(0)}, 311 valSet, 312 ), 313 }, 314 } 315 for i, test := range testCases { 316 c := sys.backends[0].engine.(*core) 317 c.current = test.roundState 318 319 if err := c.verifyCommit(test.commit, peer); err != nil { 320 if err != test.expected { 321 t.Errorf("result %d: error mismatch: have %v, want %v", i, err, test.expected) 322 } 323 } 324 } 325 }