github.com/cranelv/ethereum_mpc@v0.0.0-20191031014521-23aeb1415092/consensus_pbft/executor/executor_test.go (about) 1 /* 2 Copyright IBM Corp. 2016 All Rights Reserved. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package executor 18 19 import ( 20 "fmt" 21 "testing" 22 23 "github.com/ethereum/go-ethereum/consensus_pbft/util/events" 24 25 // pb "github.com/hyperledger/fabric/protos" 26 27 // "github.com/op/go-logging" 28 "github.com/ethereum/go-ethereum/consensus_pbft/message" 29 "github.com/ethereum/go-ethereum/consensus_pbft/pbftTypes" 30 "github.com/ethereum/go-ethereum/log" 31 ) 32 33 func init() { 34 // logging.SetLevel(logging.DEBUG, "") 35 } 36 37 // ------------------------- 38 // 39 // Mock consumer 40 // 41 // ------------------------- 42 43 type mockConsumer struct { 44 ExecutedImpl func(tag interface{}) // Called whenever Execute completes 45 CommittedImpl func(tag interface{}, state *message.StateInfo) // Called whenever Commit completes 46 RolledBackImpl func(tag interface{}) // Called whenever a Rollback completes 47 StateUpdatedImpl func(tag interface{}, state *message.StateInfo) // Called when state transfer completes, if target is nil, this indicates a failure and a new target should be supplied 48 } 49 50 func (mock *mockConsumer) Executed(tag interface{}) { 51 if mock.ExecutedImpl != nil { 52 mock.ExecutedImpl(tag) 53 } 54 } 55 56 func (mock *mockConsumer) Committed(tag interface{}, state *message.StateInfo) { 57 if mock.CommittedImpl != nil { 58 mock.CommittedImpl(tag, state) 59 } 60 } 61 62 func (mock *mockConsumer) RolledBack(tag interface{}) { 63 if mock.RolledBackImpl != nil { 64 mock.RolledBackImpl(tag) 65 } 66 } 67 68 func (mock *mockConsumer) StateUpdated(tag interface{}, state *message.StateInfo) { 69 if mock.StateUpdatedImpl != nil { 70 mock.StateUpdatedImpl(tag, state) 71 } 72 } 73 74 // ------------------------- 75 // 76 // Mock rawExecutor 77 // 78 // ------------------------- 79 80 type mockRawExecutor struct { 81 t *testing.T 82 curBatch interface{} 83 curTxs []*message.Task 84 commitCount uint64 85 } 86 87 func (mock *mockRawExecutor) BeginTaskBatch(id interface{}) error { 88 if mock.curBatch != nil { 89 e := fmt.Errorf("Attempted to start a new batch without stopping the other") 90 mock.t.Fatal(e) 91 return e 92 } 93 mock.curBatch = id 94 return nil 95 } 96 97 func (mock *mockRawExecutor) ExecTasks(id interface{}, txs []*message.Task) ([]*message.Result, error) { 98 if mock.curBatch != id { 99 e := fmt.Errorf("Attempted to exec on a different batch") 100 mock.t.Fatal(e) 101 return nil, e 102 } 103 mock.curTxs = append(mock.curTxs, txs...) 104 return nil, nil 105 } 106 107 func (mock *mockRawExecutor) CommitTaskBatch(id interface{}, meta []byte) (*message.StateInfo, error) { 108 if mock.curBatch != id { 109 e := fmt.Errorf("Attempted to commit a batch which doesn't exist") 110 mock.t.Fatal(e) 111 return nil, e 112 } 113 mock.commitCount++ 114 return nil, nil 115 } 116 117 func (mock *mockRawExecutor) RollbackTaskBatch(id interface{}) error { 118 if mock.curBatch == nil { 119 e := fmt.Errorf("Attempted to rollback a batch which doesn't exist") 120 mock.t.Fatal(e) 121 return e 122 } 123 124 mock.curTxs = nil 125 mock.curBatch = nil 126 127 return nil 128 } 129 130 func (mock *mockRawExecutor) PreviewCommitTaskBatch(id interface{}, meta []byte) ([]byte, error) { 131 if mock.curBatch != nil { 132 e := fmt.Errorf("Attempted to preview a batch which doesn't exist") 133 mock.t.Fatal(e) 134 return nil, e 135 } 136 137 return nil, nil 138 } 139 140 func (mock *mockRawExecutor) GetStateInfo() *message.StateInfo{ 141 // return nil 142 return &message.StateInfo{ 143 Hash: pbftTypes.MessageDigest(fmt.Sprintf("%d", mock.commitCount)), 144 Number: mock.commitCount, 145 } 146 // Number: big.NewInt(int64(mock.commitCount)), 147 // ParentHash: []byte(fmt.Sprintf("%d", mock.commitCount-1)) 148 // Height: mock.commitCount, 149 // CurrentBlockHash: []byte(fmt.Sprintf("%d", mock.commitCount)), 150 // PreviousBlockHash: []byte(fmt.Sprintf("%d", mock.commitCount-1)), 151 // } 152 } 153 154 // ------------------------- 155 // 156 // Mock stateTransfer 157 // 158 // ------------------------- 159 160 type mockStateTransfer struct { 161 StartImpl func() 162 StopImpl func() 163 SyncToTargetImpl func(blockNumber uint64, digest pbftTypes.MessageDigest, peerIDs []*pbftTypes.PeerID) (error, bool) 164 } 165 166 func (mock *mockStateTransfer) Start() {} 167 func (mock *mockStateTransfer) Stop() {} 168 169 func (mock *mockStateTransfer) SyncToTarget(blockNumber uint64, digest pbftTypes.MessageDigest, peerIDs []*pbftTypes.PeerID) (error, bool) { 170 if mock.SyncToTargetImpl != nil { 171 return mock.SyncToTargetImpl(blockNumber, digest, peerIDs) 172 } 173 return nil, false 174 } 175 176 // ------------------------- 177 // 178 // Mock event manager 179 // 180 // ------------------------- 181 182 type mockEventManager struct { 183 target events.Receiver 184 bufferedChannel chan events.Event // This is buffered so that queueing never blocks 185 } 186 187 func (mock *mockEventManager) Start() {} 188 189 func (mock *mockEventManager) Halt() {} 190 191 func (mock *mockEventManager) Inject(event events.Event) {} 192 193 func (mock *mockEventManager) SetReceiver(receiver events.Receiver) { 194 mock.target = receiver 195 } 196 197 func (mock *mockEventManager) Queue() chan<- events.Event { 198 return mock.bufferedChannel 199 } 200 201 func (mock *mockEventManager) process() { 202 for { 203 select { 204 case ev := <-mock.bufferedChannel: 205 events.SendEvent(mock.target, ev) 206 default: 207 return 208 } 209 } 210 } 211 212 // ------------------------- 213 // 214 // Util functions 215 // 216 // ------------------------- 217 218 func newMocks(t *testing.T) (*coordinatorImpl, *mockConsumer, *mockRawExecutor, *mockStateTransfer, *mockEventManager) { 219 mc := &mockConsumer{} 220 mre := &mockRawExecutor{t: t} 221 mst := &mockStateTransfer{} 222 mev := &mockEventManager{bufferedChannel: make(chan events.Event, 100)} 223 co := &coordinatorImpl{ 224 consumer: mc, 225 rawExecutor: mre, 226 stc: mst, 227 manager: mev, 228 } 229 mev.target = co 230 return co, mc, mre, mst, mev 231 } 232 233 // ------------------------- 234 // 235 // Actual Tests 236 // 237 // ------------------------- 238 239 // TestNormalExecutes executes 50 transactions, then commits, ensuring that the callbacks are called appropriately 240 func TestNormalExecutes(t *testing.T) { 241 log.InitLog(5) 242 co, mc, _, _, mev := newMocks(t) 243 244 times := uint64(50) 245 246 id := struct{}{} 247 testTxs := []*message.Task{&message.Task{}, &message.Task{}, &message.Task{}} 248 249 executed := uint64(0) 250 mc.ExecutedImpl = func(tag interface{}) { 251 if tag != id { 252 t.Fatalf("Executed got wrong ID") 253 } 254 executed++ 255 } 256 257 committed := false 258 mc.CommittedImpl = func(tag interface{}, info *message.StateInfo) { 259 if tag != id { 260 t.Fatalf("Committed got wrong ID") 261 } 262 committed = true 263 if info.Height() != 1 { 264 t.Fatalf("Blockchain info should have returned height of %d, returned %d", 1, info.Height()) 265 } 266 } 267 268 for i := uint64(0); i < times; i++ { 269 co.Execute(id, testTxs) 270 } 271 272 co.Commit(id, nil) 273 mev.process() 274 275 if executed != times { 276 t.Fatalf("Should have executed %d times but executed %d times", times, executed) 277 } 278 279 if !committed { 280 t.Fatalf("Should have committed") 281 } 282 } 283 284 // TestRollbackExecutes executes 5 transactions, then rolls back, executes 5 more and commits, ensuring that the callbacks are called appropriately 285 func TestRollbackExecutes(t *testing.T) { 286 log.InitLog(5) 287 co, mc, _, _, mev := newMocks(t) 288 289 times := uint64(5) 290 291 id := struct{}{} 292 testTxs := []*message.Task{&message.Task{}, &message.Task{}, &message.Task{}} 293 294 executed := uint64(0) 295 mc.ExecutedImpl = func(tag interface{}) { 296 if tag != id { 297 t.Fatalf("Executed got wrong ID") 298 } 299 executed++ 300 } 301 302 committed := false 303 mc.CommittedImpl = func(tag interface{}, info *message.StateInfo) { 304 if tag != id { 305 t.Fatalf("Committed got wrong ID") 306 } 307 committed = true 308 if info.Height() != 1 { 309 t.Fatalf("Blockchain info should have returned height of %d, returned %d", 1, info.Height()) 310 } 311 } 312 313 rolledBack := false 314 mc.RolledBackImpl = func(tag interface{}) { 315 if tag != id { 316 t.Fatalf("RolledBack got wrong ID") 317 } 318 rolledBack = true 319 } 320 321 for i := uint64(0); i < times; i++ { 322 co.Execute(id, testTxs) 323 } 324 325 co.Rollback(id) 326 mev.process() 327 328 if !rolledBack { 329 t.Fatalf("Should have rolled back") 330 } 331 332 for i := uint64(0); i < times; i++ { 333 co.Execute(id, testTxs) 334 } 335 co.Commit(id, nil) 336 mev.process() 337 338 if executed != 2*times { 339 t.Fatalf("Should have executed %d times but executed %d times", 2*times, executed) 340 } 341 342 if !committed { 343 t.Fatalf("Should have committed") 344 } 345 } 346 347 // TestEmptyCommit attempts to commit without executing any transactions, this is considered a fatal error and no callback should occur 348 func TestEmptyCommit(t *testing.T) { 349 log.InitLog(5) 350 co, mc, _, _, mev := newMocks(t) 351 352 mc.CommittedImpl = func(tag interface{}, info *message.StateInfo) { 353 t.Fatalf("Should not have committed") 354 } 355 356 co.Commit(nil, nil) 357 mev.process() 358 } 359 360 // TestEmptyRollback attempts a rollback without executing any transactions, this is considered an error and no callback should occur 361 func TestEmptyRollback(t *testing.T) { 362 log.InitLog(5) 363 co, mc, _, _, mev := newMocks(t) 364 365 mc.RolledBackImpl = func(tag interface{}) { 366 t.Fatalf("Should not have committed") 367 } 368 369 co.Rollback(nil) 370 mev.process() 371 } 372 373 // TestNormalStateTransfer attempts a simple state transfer request with 10 recoverable failures 374 func TestNormalStateTransfer(t *testing.T) { 375 log.InitLog(5) 376 co, mc, _, mst, mev := newMocks(t) 377 //co, mc, mre, mst, mev := newMocks(t) 378 379 id := struct{}{} 380 blockNumber := uint64(2389) 381 blockHash := pbftTypes.MessageDigest("BlockHash") 382 383 stateUpdated := false 384 mc.StateUpdatedImpl = func(tag interface{}, info *message.StateInfo) { 385 if id != tag { 386 t.Fatalf("Incorrect tag received") 387 } 388 if stateUpdated { 389 t.Fatalf("State should only be updated once") 390 } 391 if info.Height() != blockNumber+1 { 392 t.Fatalf("Final height should have been %d", blockNumber+1) 393 } 394 if info.Digest() != blockHash { 395 t.Fatalf("Final height should have been %d", blockNumber+1) 396 } 397 stateUpdated = true 398 } 399 400 count := 0 401 mst.SyncToTargetImpl = func(bn uint64, bh pbftTypes.MessageDigest, ps []*pbftTypes.PeerID) (error, bool) { 402 count++ 403 if count <= 10 { 404 return fmt.Errorf("Transient state transfer error"), true 405 } 406 407 return nil, true 408 } 409 410 // co.UpdateState(id, &message.StateInfo{Height: blockNumber + 1, CurrentBlockHash: blockHash}, nil) 411 co.UpdateState(id, &message.StateInfo{Number: blockNumber + 1, Hash: blockHash}, nil) 412 mev.process() 413 414 if !stateUpdated { 415 t.Fatalf("State should have been updated") 416 } 417 } 418 419 // TestFailingStateTransfer attempts a failing simple state transfer request with 10 recoverable failures, then a fatal error, then a success 420 func TestFailingStateTransfer(t *testing.T) { 421 log.InitLog(5) 422 co, mc, _, mst, mev := newMocks(t) 423 //co, mc, mre, mst, mev := newMocks(t) 424 425 id := struct{}{} 426 blockNumber1 := uint64(1) 427 blockHash1 := pbftTypes.MessageDigest("BlockHash1") 428 blockNumber2 := uint64(2) 429 blockHash2 := pbftTypes.MessageDigest("BlockHash2") 430 431 stateUpdated := false 432 mc.StateUpdatedImpl = func(tag interface{}, info *message.StateInfo) { 433 if id != tag { 434 t.Fatalf("Incorrect tag received") 435 } 436 if stateUpdated { 437 t.Fatalf("State should only be updated once") 438 } 439 if info == nil { 440 return 441 } 442 if info.Height() != blockNumber2+1 { 443 t.Fatalf("Final height should have been %d", blockNumber2+1) 444 } 445 if info.Digest()!= blockHash2 { 446 t.Fatalf("Final height should have been %d", blockNumber2+1) 447 } 448 stateUpdated = true 449 } 450 451 count := 0 452 mst.SyncToTargetImpl = func(bn uint64, digest pbftTypes.MessageDigest, peerIDs []*pbftTypes.PeerID) (error, bool) { 453 count++ 454 if count <= 10 { 455 return fmt.Errorf("Transient state transfer error"), true 456 } 457 458 if bn == blockNumber1 { 459 return fmt.Errorf("Irrecoverable state transfer error"), false 460 } 461 462 return nil, true 463 } 464 465 co.UpdateState(id, &message.StateInfo{Number: blockNumber1 + 1, Hash: blockHash1}, nil) 466 // co.UpdateState(id, &pb.BlockchainInfo{Height: blockNumber1 + 1, CurrentBlockHash: blockHash1}, nil) 467 mev.process() 468 469 if stateUpdated { 470 t.Fatalf("State should not have been updated") 471 } 472 co.UpdateState(id, &message.StateInfo{Number: blockNumber2 + 1, Hash: blockHash2}, nil) 473 // co.UpdateState(id, &pb.BlockchainInfo{Height: blockNumber2 + 1, CurrentBlockHash: blockHash2}, nil) 474 mev.process() 475 476 if !stateUpdated { 477 t.Fatalf("State should have been updated") 478 } 479 } 480 481 // TestExecuteAfterStateTransfer attempts an execute and commit after a simple state transfer request 482 func TestExecuteAfterStateTransfer(t *testing.T) { 483 log.InitLog(5) 484 co, mc, _, _, mev := newMocks(t) 485 testTxs := []*message.Task{&message.Task{}, &message.Task{}, &message.Task{}} 486 487 id := struct{}{} 488 blockNumber := uint64(2389) 489 blockHash := pbftTypes.MessageDigest("BlockHash") 490 491 stateTransferred := false 492 mc.StateUpdatedImpl = func(tag interface{}, info *message.StateInfo) { 493 if nil == info { 494 t.Fatalf("State transfer should have succeeded") 495 } 496 stateTransferred = true 497 } 498 499 executed := false 500 mc.ExecutedImpl = func(tag interface{}) { 501 executed = true 502 } 503 504 co.UpdateState(id, &message.StateInfo{Number: blockNumber + 1, Hash: blockHash}, nil) 505 // co.UpdateState(id, &pb.BlockchainInfo{Height: blockNumber + 1, CurrentBlockHash: blockHash}, nil) 506 co.Execute(id, testTxs) 507 mev.process() 508 509 if !executed { 510 t.Fatalf("Execution should have occurred") 511 } 512 } 513 514 // TestExecuteDuringStateTransfer attempts a state transfer which fails, then an execute which should not be performed 515 func TestExecuteDuringStateTransfer(t *testing.T) { 516 log.InitLog(5) 517 co, mc, mre, mst, mev := newMocks(t) 518 testTxs := []*message.Task{&message.Task{}, &message.Task{}, &message.Task{}} 519 520 id := struct{}{} 521 blockNumber := uint64(2389) 522 blockHash := pbftTypes.MessageDigest("BlockHash") 523 524 mc.StateUpdatedImpl = func(tag interface{}, info *message.StateInfo) { 525 if info != nil { 526 t.Fatalf("State transfer should not succeed") 527 } 528 } 529 530 mst.SyncToTargetImpl = func(bn uint64, digest pbftTypes.MessageDigest, peerIDs []*pbftTypes.PeerID) (error, bool) { 531 return fmt.Errorf("Irrecoverable error"), false 532 } 533 534 co.UpdateState(id, &message.StateInfo{Number: blockNumber + 1, Hash: blockHash}, nil) 535 // co.UpdateState(id, &pb.BlockchainInfo{Height: blockNumber + 1, CurrentBlockHash: blockHash}, nil) 536 co.Execute(id, testTxs) 537 mev.process() 538 539 if mre.curBatch != nil { 540 t.Fatalf("Execution should not have executed beginning a new batch") 541 } 542 }