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