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