github.com/ConsenSys/Quorum@v20.10.0+incompatible/consensus/istanbul/core/preprepare_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/consensus/istanbul" 25 ) 26 27 func newTestPreprepare(v *istanbul.View) *istanbul.Preprepare { 28 return &istanbul.Preprepare{ 29 View: v, 30 Proposal: newTestProposal(), 31 } 32 } 33 34 func TestHandlePreprepare(t *testing.T) { 35 N := uint64(4) // replica 0 is the proposer, it will send messages to others 36 F := uint64(1) // F does not affect tests 37 38 testCases := []struct { 39 system *testSystem 40 expectedRequest istanbul.Proposal 41 expectedErr error 42 existingBlock bool 43 }{ 44 { 45 // normal case 46 func() *testSystem { 47 sys := NewTestSystemWithBackend(N, F) 48 49 for i, backend := range sys.backends { 50 c := backend.engine.(*core) 51 c.valSet = backend.peers 52 if i != 0 { 53 c.state = StateAcceptRequest 54 } 55 } 56 return sys 57 }(), 58 newTestProposal(), 59 nil, 60 false, 61 }, 62 { 63 // future message 64 func() *testSystem { 65 sys := NewTestSystemWithBackend(N, F) 66 67 for i, backend := range sys.backends { 68 c := backend.engine.(*core) 69 c.valSet = backend.peers 70 if i != 0 { 71 c.state = StateAcceptRequest 72 // hack: force set subject that future message can be simulated 73 c.current = newTestRoundState( 74 &istanbul.View{ 75 Round: big.NewInt(0), 76 Sequence: big.NewInt(0), 77 }, 78 c.valSet, 79 ) 80 81 } else { 82 c.current.SetSequence(big.NewInt(10)) 83 } 84 } 85 return sys 86 }(), 87 makeBlock(1), 88 errFutureMessage, 89 false, 90 }, 91 { 92 // non-proposer 93 func() *testSystem { 94 sys := NewTestSystemWithBackend(N, F) 95 96 // force remove replica 0, let replica 1 be the proposer 97 sys.backends = sys.backends[1:] 98 99 for i, backend := range sys.backends { 100 c := backend.engine.(*core) 101 c.valSet = backend.peers 102 if i != 0 { 103 // replica 0 is the proposer 104 c.state = StatePreprepared 105 } 106 } 107 return sys 108 }(), 109 makeBlock(1), 110 errNotFromProposer, 111 false, 112 }, 113 { 114 // errOldMessage 115 func() *testSystem { 116 sys := NewTestSystemWithBackend(N, F) 117 118 for i, backend := range sys.backends { 119 c := backend.engine.(*core) 120 c.valSet = backend.peers 121 if i != 0 { 122 c.state = StatePreprepared 123 c.current.SetSequence(big.NewInt(10)) 124 c.current.SetRound(big.NewInt(10)) 125 } 126 } 127 return sys 128 }(), 129 makeBlock(1), 130 errOldMessage, 131 false, 132 }, 133 } 134 135 OUTER: 136 for _, test := range testCases { 137 test.system.Run(false) 138 139 v0 := test.system.backends[0] 140 r0 := v0.engine.(*core) 141 142 curView := r0.currentView() 143 144 preprepare := &istanbul.Preprepare{ 145 View: curView, 146 Proposal: test.expectedRequest, 147 } 148 149 for i, v := range test.system.backends { 150 // i == 0 is primary backend, it is responsible for send PRE-PREPARE messages to others. 151 if i == 0 { 152 continue 153 } 154 155 c := v.engine.(*core) 156 157 m, _ := Encode(preprepare) 158 _, val := r0.valSet.GetByAddress(v0.Address()) 159 // run each backends and verify handlePreprepare function. 160 if err := c.handlePreprepare(&message{ 161 Code: msgPreprepare, 162 Msg: m, 163 Address: v0.Address(), 164 }, val); err != nil { 165 if err != test.expectedErr { 166 t.Errorf("error mismatch: have %v, want %v", err, test.expectedErr) 167 } 168 continue OUTER 169 } 170 171 if c.state != StatePreprepared { 172 t.Errorf("state mismatch: have %v, want %v", c.state, StatePreprepared) 173 } 174 175 if !test.existingBlock && !reflect.DeepEqual(c.current.Subject().View, curView) { 176 t.Errorf("view mismatch: have %v, want %v", c.current.Subject().View, curView) 177 } 178 179 // verify prepare messages 180 decodedMsg := new(message) 181 err := decodedMsg.FromPayload(v.sentMsgs[0], nil) 182 if err != nil { 183 t.Errorf("error mismatch: have %v, want nil", err) 184 } 185 186 expectedCode := msgPrepare 187 if test.existingBlock { 188 expectedCode = msgCommit 189 } 190 if decodedMsg.Code != expectedCode { 191 t.Errorf("message code mismatch: have %v, want %v", decodedMsg.Code, expectedCode) 192 } 193 194 var subject *istanbul.Subject 195 err = decodedMsg.Decode(&subject) 196 if err != nil { 197 t.Errorf("error mismatch: have %v, want nil", err) 198 } 199 if !test.existingBlock && !reflect.DeepEqual(subject, c.current.Subject()) { 200 t.Errorf("subject mismatch: have %v, want %v", subject, c.current.Subject()) 201 } 202 203 } 204 } 205 } 206 207 func TestHandlePreprepareWithLock(t *testing.T) { 208 N := uint64(4) // replica 0 is the proposer, it will send messages to others 209 F := uint64(1) // F does not affect tests 210 proposal := newTestProposal() 211 mismatchProposal := makeBlock(10) 212 newSystem := func() *testSystem { 213 sys := NewTestSystemWithBackend(N, F) 214 215 for i, backend := range sys.backends { 216 c := backend.engine.(*core) 217 c.valSet = backend.peers 218 if i != 0 { 219 c.state = StateAcceptRequest 220 } 221 c.roundChangeSet = newRoundChangeSet(c.valSet) 222 } 223 return sys 224 } 225 226 testCases := []struct { 227 system *testSystem 228 proposal istanbul.Proposal 229 lockProposal istanbul.Proposal 230 }{ 231 { 232 newSystem(), 233 proposal, 234 proposal, 235 }, 236 { 237 newSystem(), 238 proposal, 239 mismatchProposal, 240 }, 241 } 242 243 for _, test := range testCases { 244 test.system.Run(false) 245 v0 := test.system.backends[0] 246 r0 := v0.engine.(*core) 247 curView := r0.currentView() 248 preprepare := &istanbul.Preprepare{ 249 View: curView, 250 Proposal: test.proposal, 251 } 252 lockPreprepare := &istanbul.Preprepare{ 253 View: curView, 254 Proposal: test.lockProposal, 255 } 256 257 for i, v := range test.system.backends { 258 // i == 0 is primary backend, it is responsible for send PRE-PREPARE messages to others. 259 if i == 0 { 260 continue 261 } 262 263 c := v.engine.(*core) 264 c.current.SetPreprepare(lockPreprepare) 265 c.current.LockHash() 266 m, _ := Encode(preprepare) 267 _, val := r0.valSet.GetByAddress(v0.Address()) 268 if err := c.handlePreprepare(&message{ 269 Code: msgPreprepare, 270 Msg: m, 271 Address: v0.Address(), 272 }, val); err != nil { 273 t.Errorf("error mismatch: have %v, want nil", err) 274 } 275 if test.proposal == test.lockProposal { 276 if c.state != StatePrepared { 277 t.Errorf("state mismatch: have %v, want %v", c.state, StatePreprepared) 278 } 279 if !reflect.DeepEqual(curView, c.currentView()) { 280 t.Errorf("view mismatch: have %v, want %v", c.currentView(), curView) 281 } 282 } else { 283 // Should stay at StateAcceptRequest 284 if c.state != StateAcceptRequest { 285 t.Errorf("state mismatch: have %v, want %v", c.state, StateAcceptRequest) 286 } 287 // Should have triggered a round change 288 expectedView := &istanbul.View{ 289 Sequence: curView.Sequence, 290 Round: big.NewInt(1), 291 } 292 if !reflect.DeepEqual(expectedView, c.currentView()) { 293 t.Errorf("view mismatch: have %v, want %v", c.currentView(), expectedView) 294 } 295 } 296 } 297 } 298 }