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