github.com/reapchain/go-reapchain@v0.2.15-0.20210609012950-9735c110c705/consensus/podc/core/prepare_test.go (about) 1 // Copyright 2017 AMIS Technologies 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/common" 25 "github.com/ethereum/go-ethereum/consensus/podc" 26 "github.com/ethereum/go-ethereum/consensus/podc/validator" 27 "github.com/ethereum/go-ethereum/crypto" 28 ) 29 30 func TestHandlePrepare(t *testing.T) { 31 N := uint64(4) 32 F := uint64(1) 33 34 proposal := newTestProposal() 35 expectedSubject := &podc.Subject{ 36 View: &podc.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 &podc.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 primary 65 c.state = StatePreprepared 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 primary 82 c.current = newTestRoundState( 83 expectedSubject.View, 84 c.valSet, 85 ) 86 c.state = StatePreprepared 87 } else { 88 c.current = newTestRoundState( 89 &podc.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 primary 111 c.current = newTestRoundState( 112 expectedSubject.View, 113 c.valSet, 114 ) 115 c.state = StatePreprepared 116 } else { 117 c.current = newTestRoundState( 118 &podc.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 // subject not match 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 if i == 0 { 139 // replica 0 is primary 140 c.current = newTestRoundState( 141 expectedSubject.View, 142 c.valSet, 143 ) 144 c.state = StatePreprepared 145 } else { 146 c.current = newTestRoundState( 147 &podc.View{ 148 Round: big.NewInt(0), 149 Sequence: big.NewInt(1)}, 150 c.valSet, 151 ) 152 } 153 } 154 return sys 155 }(), 156 errInconsistentSubject, 157 }, 158 { 159 // less than 2F+1 160 func() *testSystem { 161 sys := NewTestSystemWithBackend(N, F) 162 163 // save less than 2*F+1 replica 164 sys.backends = sys.backends[2*int(F)+1:] 165 166 for i, backend := range sys.backends { 167 c := backend.engine.(*core) 168 c.valSet = backend.peers 169 c.current = newTestRoundState( 170 expectedSubject.View, 171 c.valSet, 172 ) 173 174 if i == 0 { 175 // replica 0 is primary 176 c.state = StatePreprepared 177 } 178 } 179 return sys 180 }(), 181 nil, 182 }, 183 // TODO: double send message 184 } 185 186 OUTER: 187 for _, test := range testCases { 188 test.system.Run(false) 189 190 v0 := test.system.backends[0] 191 r0 := v0.engine.(*core) 192 193 for i, v := range test.system.backends { 194 validator := r0.valSet.GetByIndex(uint64(i)) 195 m, _ := Encode(v.engine.(*core).current.Subject()) 196 if err := r0.handleDSelect(&message{ //handlePrepare 197 Code: msgDSelect, //msgDSelect 198 Msg: m, 199 Address: validator.Address(), 200 }, validator); err != nil { 201 if err != test.expectedErr { 202 t.Errorf("error mismatch: have %v, want %v", err, test.expectedErr) 203 } 204 continue OUTER 205 } 206 } 207 208 // prepared is normal case 209 if r0.state != StateDSelected { //StatePrepared 210 // There are not enough prepared messages in core 211 if r0.state != StatePreprepared { 212 t.Errorf("state mismatch: have %v, want %v", r0.state, StatePreprepared) 213 } 214 if r0.current.Prepares.Size() > 2*r0.valSet.F() { 215 t.Errorf("the size of prepare messages should be less than %v", 2*r0.valSet.F()+1) 216 } 217 218 continue 219 } 220 221 // core should have 2F+1 prepare messages 222 if r0.current.Prepares.Size() <= 2*r0.valSet.F() { 223 t.Errorf("the size of prepare messages should be larger than 2F+1: size %v", r0.current.Commits.Size()) 224 } 225 226 // a message will be delivered to backend if 2F+1 227 if int64(len(v0.sentMsgs)) != 1 { 228 t.Errorf("the Send() should be called once: times %v", len(test.system.backends[0].sentMsgs)) 229 } 230 231 // verify commit messages 232 decodedMsg := new(message) 233 err := decodedMsg.FromPayload(v0.sentMsgs[0], nil) 234 if err != nil { 235 t.Errorf("error mismatch: have %v, want nil", err) 236 } 237 238 if decodedMsg.Code != msgCommit { 239 t.Errorf("message code mismatch: have %v, want %v", decodedMsg.Code, msgCommit) 240 } 241 var m *podc.Subject 242 err = decodedMsg.Decode(&m) 243 if err != nil { 244 t.Errorf("error mismatch: have %v, want nil", err) 245 } 246 if !reflect.DeepEqual(m, expectedSubject) { 247 t.Errorf("subject mismatch: have %v, want %v", m, expectedSubject) 248 } 249 } 250 } 251 252 // round is not checked for now 253 func TestVerifyPrepare(t *testing.T) { 254 // for log purpose 255 privateKey, _ := crypto.GenerateKey() 256 peer := validator.New(getPublicKeyAddress(privateKey)) 257 valSet := validator.NewSet([]common.Address{peer.Address()}, podc.RoundRobin) 258 259 sys := NewTestSystemWithBackend(uint64(1), uint64(0)) 260 261 testCases := []struct { 262 expected error 263 264 prepare *podc.Subject 265 roundState *roundState 266 }{ 267 { 268 // normal case 269 expected: nil, 270 prepare: &podc.Subject{ 271 View: &podc.View{Round: big.NewInt(0), Sequence: big.NewInt(0)}, 272 Digest: newTestProposal().Hash(), 273 }, 274 roundState: newTestRoundState( 275 &podc.View{Round: big.NewInt(0), Sequence: big.NewInt(0)}, 276 valSet, 277 ), 278 }, 279 { 280 // old message 281 expected: errInconsistentSubject, 282 prepare: &podc.Subject{ 283 View: &podc.View{Round: big.NewInt(0), Sequence: big.NewInt(0)}, 284 Digest: newTestProposal().Hash(), 285 }, 286 roundState: newTestRoundState( 287 &podc.View{Round: big.NewInt(1), Sequence: big.NewInt(1)}, 288 valSet, 289 ), 290 }, 291 { 292 // different digest 293 expected: errInconsistentSubject, 294 prepare: &podc.Subject{ 295 View: &podc.View{Round: big.NewInt(0), Sequence: big.NewInt(0)}, 296 Digest: common.StringToHash("1234567890"), 297 }, 298 roundState: newTestRoundState( 299 &podc.View{Round: big.NewInt(1), Sequence: big.NewInt(1)}, 300 valSet, 301 ), 302 }, 303 { 304 // malicious package(lack of sequence) 305 expected: errInconsistentSubject, 306 prepare: &podc.Subject{ 307 View: &podc.View{Round: big.NewInt(0), Sequence: nil}, 308 Digest: newTestProposal().Hash(), 309 }, 310 roundState: newTestRoundState( 311 &podc.View{Round: big.NewInt(1), Sequence: big.NewInt(1)}, 312 valSet, 313 ), 314 }, 315 { 316 // wrong prepare message with same sequence but different round 317 expected: errInconsistentSubject, 318 prepare: &podc.Subject{ 319 View: &podc.View{Round: big.NewInt(1), Sequence: big.NewInt(0)}, 320 Digest: newTestProposal().Hash(), 321 }, 322 roundState: newTestRoundState( 323 &podc.View{Round: big.NewInt(0), Sequence: big.NewInt(0)}, 324 valSet, 325 ), 326 }, 327 { 328 // wrong prepare message with same round but different sequence 329 expected: errInconsistentSubject, 330 prepare: &podc.Subject{ 331 View: &podc.View{Round: big.NewInt(0), Sequence: big.NewInt(1)}, 332 Digest: newTestProposal().Hash(), 333 }, 334 roundState: newTestRoundState( 335 &podc.View{Round: big.NewInt(0), Sequence: big.NewInt(0)}, 336 valSet, 337 ), 338 }, 339 } 340 for i, test := range testCases { 341 c := sys.backends[0].engine.(*core) 342 c.current = test.roundState 343 344 if err := c.verifyDCommit(test.prepare, peer); err != nil { //verifyPrepare 345 if err != test.expected { 346 t.Errorf("result %d: error mismatch: have %v, want %v", i, err, test.expected) 347 } 348 } 349 } 350 }