github.com/kisexp/xdchain@v0.0.0-20211206025815-490d6b732aa7/consensus/istanbul/ibft/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/kisexp/xdchain/common" 26 "github.com/kisexp/xdchain/consensus/istanbul" 27 istanbulcommon "github.com/kisexp/xdchain/consensus/istanbul/common" 28 ibfttypes "github.com/kisexp/xdchain/consensus/istanbul/ibft/types" 29 "github.com/kisexp/xdchain/consensus/istanbul/validator" 30 "github.com/kisexp/xdchain/crypto" 31 ) 32 33 func TestHandlePrepare(t *testing.T) { 34 N := uint64(4) 35 F := uint64(1) 36 37 proposal := newTestProposal() 38 expectedSubject := &istanbul.Subject{ 39 View: &istanbul.View{ 40 Round: big.NewInt(0), 41 Sequence: proposal.Number(), 42 }, 43 Digest: proposal.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 57 c.valSet = backend.peers 58 c.current = newTestRoundState( 59 &istanbul.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 = ibfttypes.StatePreprepared 69 } 70 } 71 return sys 72 }(), 73 nil, 74 }, 75 { 76 // future ibfttypes.Message 77 func() *testSystem { 78 sys := NewTestSystemWithBackend(N, F) 79 80 for i, backend := range sys.backends { 81 c := backend.engine 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 = ibfttypes.StatePreprepared 90 } else { 91 c.current = newTestRoundState( 92 &istanbul.View{ 93 Round: big.NewInt(2), 94 Sequence: big.NewInt(3), 95 }, 96 c.valSet, 97 ) 98 } 99 } 100 return sys 101 }(), 102 istanbulcommon.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 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 = ibfttypes.StatePreprepared 119 } else { 120 c.current = newTestRoundState( 121 &istanbul.View{ 122 Round: big.NewInt(0), 123 Sequence: big.NewInt(0), 124 }, 125 c.valSet, 126 ) 127 } 128 } 129 return sys 130 }(), 131 istanbulcommon.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 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 = ibfttypes.StatePreprepared 148 } else { 149 c.current = newTestRoundState( 150 &istanbul.View{ 151 Round: big.NewInt(0), 152 Sequence: big.NewInt(1)}, 153 c.valSet, 154 ) 155 } 156 } 157 return sys 158 }(), 159 istanbulcommon.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 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 = ibfttypes.StatePreprepared 179 } 180 } 181 return sys 182 }(), 183 nil, 184 }, 185 // TODO: double send ibfttypes.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 194 195 for i, v := range test.system.backends { 196 validator := r0.valSet.GetByIndex(uint64(i)) 197 m, _ := ibfttypes.Encode(v.engine.current.Subject()) 198 if err := r0.handlePrepare(&ibfttypes.Message{ 199 Code: ibfttypes.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 != ibfttypes.StatePrepared { 215 // There are not enough PREPARE messages in core 216 if r0.state != ibfttypes.StatePreprepared { 217 t.Errorf("state mismatch: have %v, want %v", r0.state, ibfttypes.StatePreprepared) 218 } 219 if r0.current.Prepares.Size() >= r0.QuorumSize() { 220 t.Errorf("the size of PREPARE messages should be less than %v", r0.QuorumSize()) 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.QuorumSize() { 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 ibfttypes.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(ibfttypes.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 != ibfttypes.MsgCommit { 247 t.Errorf("ibfttypes.Message code mismatch: have %v, want %v", decodedMsg.Code, ibfttypes.MsgCommit) 248 } 249 var m *istanbul.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()}, istanbul.NewRoundRobinProposerPolicy()) 269 270 sys := NewTestSystemWithBackend(uint64(1), uint64(0)) 271 272 testCases := []struct { 273 expected error 274 275 prepare *istanbul.Subject 276 roundState *roundState 277 }{ 278 { 279 // normal case 280 expected: nil, 281 prepare: &istanbul.Subject{ 282 View: &istanbul.View{Round: big.NewInt(0), Sequence: big.NewInt(0)}, 283 Digest: newTestProposal().Hash(), 284 }, 285 roundState: newTestRoundState( 286 &istanbul.View{Round: big.NewInt(0), Sequence: big.NewInt(0)}, 287 valSet, 288 ), 289 }, 290 { 291 // old ibfttypes.Message 292 expected: istanbulcommon.ErrInconsistentSubject, 293 prepare: &istanbul.Subject{ 294 View: &istanbul.View{Round: big.NewInt(0), Sequence: big.NewInt(0)}, 295 Digest: newTestProposal().Hash(), 296 }, 297 roundState: newTestRoundState( 298 &istanbul.View{Round: big.NewInt(1), Sequence: big.NewInt(1)}, 299 valSet, 300 ), 301 }, 302 { 303 // different digest 304 expected: istanbulcommon.ErrInconsistentSubject, 305 prepare: &istanbul.Subject{ 306 View: &istanbul.View{Round: big.NewInt(0), Sequence: big.NewInt(0)}, 307 Digest: common.StringToHash("1234567890"), 308 }, 309 roundState: newTestRoundState( 310 &istanbul.View{Round: big.NewInt(1), Sequence: big.NewInt(1)}, 311 valSet, 312 ), 313 }, 314 { 315 // malicious package(lack of sequence) 316 expected: istanbulcommon.ErrInconsistentSubject, 317 prepare: &istanbul.Subject{ 318 View: &istanbul.View{Round: big.NewInt(0), Sequence: nil}, 319 Digest: newTestProposal().Hash(), 320 }, 321 roundState: newTestRoundState( 322 &istanbul.View{Round: big.NewInt(1), Sequence: big.NewInt(1)}, 323 valSet, 324 ), 325 }, 326 { 327 // wrong PREPARE ibfttypes.Message with same sequence but different round 328 expected: istanbulcommon.ErrInconsistentSubject, 329 prepare: &istanbul.Subject{ 330 View: &istanbul.View{Round: big.NewInt(1), Sequence: big.NewInt(0)}, 331 Digest: newTestProposal().Hash(), 332 }, 333 roundState: newTestRoundState( 334 &istanbul.View{Round: big.NewInt(0), Sequence: big.NewInt(0)}, 335 valSet, 336 ), 337 }, 338 { 339 // wrong PREPARE ibfttypes.Message with same round but different sequence 340 expected: istanbulcommon.ErrInconsistentSubject, 341 prepare: &istanbul.Subject{ 342 View: &istanbul.View{Round: big.NewInt(0), Sequence: big.NewInt(1)}, 343 Digest: newTestProposal().Hash(), 344 }, 345 roundState: newTestRoundState( 346 &istanbul.View{Round: big.NewInt(0), Sequence: big.NewInt(0)}, 347 valSet, 348 ), 349 }, 350 } 351 for i, test := range testCases { 352 c := sys.backends[0].engine 353 c.current = test.roundState 354 355 if err := c.verifyPrepare(test.prepare, peer); err != nil { 356 if err != test.expected { 357 t.Errorf("result %d: error mismatch: have %v, want %v", i, err, test.expected) 358 } 359 } 360 } 361 }