github.com/reapchain/go-reapchain@v0.2.15-0.20210609012950-9735c110c705/consensus/podc/core/commit_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 "github.com/ethereum/go-ethereum/consensus/podc" 21 "math/big" 22 "testing" 23 "github.com/ethereum/go-ethereum/common" 24 "github.com/ethereum/go-ethereum/consensus/podc/validator" 25 "github.com/ethereum/go-ethereum/crypto" 26 ) 27 28 func TestHandleCommit(t *testing.T) { 29 N := uint64(4) 30 F := uint64(1) 31 32 proposal := newTestProposal() 33 expectedSubject := &podc.Subject{ 34 View: &podc.View{ 35 Round: big.NewInt(0), 36 Sequence: proposal.Number(), 37 }, 38 Digest: proposal.Hash(), 39 } 40 41 testCases := []struct { 42 system *testSystem 43 expectedErr error 44 }{ 45 { 46 // normal case 47 func() *testSystem { 48 sys := NewTestSystemWithBackend(N, F) 49 50 for i, backend := range sys.backends { 51 c := backend.engine.(*core) 52 c.valSet = backend.peers 53 c.current = newTestRoundState( 54 &podc.View{ 55 Round: big.NewInt(0), 56 Sequence: big.NewInt(1), 57 }, 58 c.valSet, 59 ) 60 61 if i == 0 { 62 // replica 0 is primary 63 c.state = StateDSelected //StatePrepared 64 } 65 } 66 return sys 67 }(), 68 nil, 69 }, 70 { 71 // future message 72 func() *testSystem { 73 sys := NewTestSystemWithBackend(N, F) 74 75 for i, backend := range sys.backends { 76 c := backend.engine.(*core) 77 c.valSet = backend.peers 78 if i == 0 { 79 // replica 0 is primary 80 c.current = newTestRoundState( 81 expectedSubject.View, 82 c.valSet, 83 ) 84 c.state = StatePreprepared 85 } else { 86 c.current = newTestRoundState( 87 &podc.View{ 88 Round: big.NewInt(2), 89 Sequence: big.NewInt(3), 90 }, 91 c.valSet, 92 ) 93 } 94 } 95 return sys 96 }(), 97 errFutureMessage, 98 }, 99 { 100 // subject not match 101 func() *testSystem { 102 sys := NewTestSystemWithBackend(N, F) 103 104 for i, backend := range sys.backends { 105 c := backend.engine.(*core) 106 c.valSet = backend.peers 107 if i == 0 { 108 // replica 0 is primary 109 c.current = newTestRoundState( 110 expectedSubject.View, 111 c.valSet, 112 ) 113 c.state = StatePreprepared 114 } else { 115 c.current = newTestRoundState( 116 &podc.View{ 117 Round: big.NewInt(0), 118 Sequence: big.NewInt(0), 119 }, 120 c.valSet, 121 ) 122 } 123 } 124 return sys 125 }(), 126 errOldMessage, 127 }, 128 { 129 // jump state 130 func() *testSystem { 131 sys := NewTestSystemWithBackend(N, F) 132 133 for i, backend := range sys.backends { 134 c := backend.engine.(*core) 135 c.valSet = backend.peers 136 c.current = newTestRoundState( 137 &podc.View{ 138 Round: big.NewInt(0), 139 Sequence: proposal.Number(), 140 }, 141 c.valSet, 142 ) 143 144 // only replica0 stays at StatePreprepared 145 // other replicas are at StatePrepared 146 if i != 0 { 147 c.state = StateDSelected //StatePrepared 148 } else { 149 c.state = StatePreprepared 150 } 151 } 152 return sys 153 }(), 154 nil, 155 }, 156 // TODO: double send message 157 } 158 159 OUTER: 160 for _, test := range testCases { 161 test.system.Run(false) 162 163 v0 := test.system.backends[0] 164 r0 := v0.engine.(*core) 165 166 for i, v := range test.system.backends { 167 validator := r0.valSet.GetByIndex(uint64(i)) 168 m, _ := Encode(v.engine.(*core).current.Subject()) 169 if err := r0.handleDCommit(&message{ 170 Code: msgDCommit, 171 Msg: m, 172 Address: validator.Address(), 173 Signature: []byte{}, 174 CommittedSeal: validator.Address().Bytes(), // small hack 175 }, validator); err != nil { 176 if err != test.expectedErr { 177 t.Errorf("error mismatch: have %v, want %v", err, test.expectedErr) 178 } 179 continue OUTER 180 } 181 } 182 183 // prepared is normal case 184 if r0.state != StateDCommitted { 185 // There are not enough commit messages in core 186 if r0.state != StateDSelected { //StatePrepared 187 t.Errorf("state mismatch: have %v, want %v", r0.state, StateDSelected) //StatePrepared 188 } 189 if r0.current.Dcommits.Size() > 2*r0.valSet.F() { 190 t.Errorf("the size of commit messages should be less than %v", 2*r0.valSet.F()+1) 191 } 192 193 continue 194 } 195 196 // core should have 2F+1 prepare messages 197 if r0.current.Dcommits.Size() <= 2*r0.valSet.F() { 198 t.Errorf("the size of commit messages should be larger than 2F+1: size %v", r0.current.Dcommits.Size()) 199 } 200 201 // check signatures large than 2F+1 202 signedCount := 0 203 signers := make([]common.Address, len(v0.committedSeals[0])/common.AddressLength) 204 for i := 0; i < len(signers); i++ { 205 copy(signers[i][:], v0.committedSeals[0][i*common.AddressLength:]) 206 } 207 for _, validator := range r0.valSet.List() { 208 for _, signer := range signers { 209 if validator.Address() == signer { 210 signedCount++ 211 break 212 } 213 } 214 } 215 if signedCount <= 2*r0.valSet.F() { 216 t.Errorf("the expected signed count should be larger than %v, but got %v", 2*r0.valSet.F(), signedCount) 217 } 218 } 219 } 220 221 // round is not checked for now 222 func TestVerifyCommit(t *testing.T) { 223 // for log purpose 224 privateKey, _ := crypto.GenerateKey() 225 peer := validator.New(getPublicKeyAddress(privateKey)) 226 valSet := validator.NewSet([]common.Address{peer.Address()}, podc.RoundRobin) 227 228 sys := NewTestSystemWithBackend(uint64(1), uint64(0)) 229 230 testCases := []struct { 231 expected error 232 commit *podc.Subject 233 roundState *roundState 234 }{ 235 { 236 // normal case 237 expected: nil, 238 commit: &podc.Subject{ 239 View: &podc.View{Round: big.NewInt(0), Sequence: big.NewInt(0)}, 240 Digest: newTestProposal().Hash(), 241 }, 242 roundState: newTestRoundState( 243 &podc.View{Round: big.NewInt(0), Sequence: big.NewInt(0)}, 244 valSet, 245 ), 246 }, 247 { 248 // old message 1. 249 expected: errInconsistentSubject, 250 commit: &podc.Subject{ 251 View: &podc.View{Round: big.NewInt(0), Sequence: big.NewInt(0)}, 252 Digest: newTestProposal().Hash(), 253 }, 254 roundState: newTestRoundState( 255 &podc.View{Round: big.NewInt(1), Sequence: big.NewInt(1)}, 256 valSet, 257 ), 258 }, 259 { 260 // different digest 2. 261 expected: errInconsistentSubject, 262 commit: &podc.Subject{ 263 View: &podc.View{Round: big.NewInt(0), Sequence: big.NewInt(0)}, 264 Digest: common.StringToHash("1234567890"), 265 }, 266 roundState: newTestRoundState( 267 &podc.View{Round: big.NewInt(1), Sequence: big.NewInt(1)}, 268 valSet, 269 ), 270 }, 271 { 272 // malicious package(lack of sequence) 3. got="{View: %!v(PANIC=String method: runtime error: invalid memory address or nil pointer dereference), 273 expected: errInconsistentSubject, 274 commit: &podc.Subject{ 275 View: &podc.View{Round: big.NewInt(0), Sequence: nil}, 276 Digest: newTestProposal().Hash(), 277 }, 278 roundState: newTestRoundState( 279 &podc.View{Round: big.NewInt(1), Sequence: big.NewInt(1)}, 280 valSet, 281 ), 282 }, 283 { 284 // wrong prepare message with same sequence but different round 285 expected: errInconsistentSubject, 286 commit: &podc.Subject{ 287 View: &podc.View{Round: big.NewInt(1), Sequence: big.NewInt(0)}, 288 Digest: newTestProposal().Hash(), 289 }, 290 roundState: newTestRoundState( 291 &podc.View{Round: big.NewInt(0), Sequence: big.NewInt(0)}, 292 valSet, 293 ), 294 }, 295 { 296 // wrong prepare message with same round but different sequence 297 expected: errInconsistentSubject, 298 commit: &podc.Subject{ 299 View: &podc.View{Round: big.NewInt(0), Sequence: big.NewInt(1)}, 300 Digest: newTestProposal().Hash(), 301 }, 302 roundState: newTestRoundState( 303 &podc.View{Round: big.NewInt(0), Sequence: big.NewInt(0)}, 304 valSet, 305 ), 306 }, 307 //begin - by yichoi 308 { 309 // wrong prepare message with same round but different sequence 310 expected: errInconsistentSubject, 311 commit: &podc.Subject{ 312 View: &podc.View{Round: big.NewInt(1), Sequence: nil }, //got="{View: {Round: 1, Sequence: 1}, 313 Digest: newTestProposal().Hash(), 314 }, 315 roundState: newTestRoundState( 316 &podc.View{Round: big.NewInt(1), Sequence: big.NewInt(1)}, //expected="{View: {Round: 2, Sequence: 2}, 317 valSet, 318 ), 319 }, 320 321 } 322 for i, test := range testCases { 323 c := sys.backends[0].engine.(*core) 324 c.current = test.roundState 325 326 //if err := c.verifyCommit(test.commit, peer); err != nil { 327 // if err != test.expected { 328 // t.Errorf("result %d: error mismatch: have %v, want %v", i, err, test.expected) 329 // } 330 //} 331 if err := c.verifyDCommit(test.commit, peer); err != nil { 332 if err != test.expected { 333 t.Errorf("result %d: error mismatch: have %v, want %v", i, err, test.expected) 334 } 335 } 336 } 337 }