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  }