github.com/pfcoder/quorum@v2.0.3-0.20180501191142-d4a1b0958135+incompatible/consensus/istanbul/core/backlog_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  	"math/big"
    21  	"reflect"
    22  	"sync"
    23  	"testing"
    24  	"time"
    25  
    26  	"github.com/ethereum/go-ethereum/common"
    27  	"github.com/ethereum/go-ethereum/consensus/istanbul"
    28  	"github.com/ethereum/go-ethereum/consensus/istanbul/validator"
    29  	"github.com/ethereum/go-ethereum/event"
    30  	"github.com/ethereum/go-ethereum/log"
    31  	"gopkg.in/karalabe/cookiejar.v2/collections/prque"
    32  )
    33  
    34  func TestCheckMessage(t *testing.T) {
    35  	c := &core{
    36  		state: StateAcceptRequest,
    37  		current: newRoundState(&istanbul.View{
    38  			Sequence: big.NewInt(1),
    39  			Round:    big.NewInt(0),
    40  		}, newTestValidatorSet(4), common.Hash{}, nil, nil, nil),
    41  	}
    42  
    43  	// invalid view format
    44  	err := c.checkMessage(msgPreprepare, nil)
    45  	if err != errInvalidMessage {
    46  		t.Errorf("error mismatch: have %v, want %v", err, errInvalidMessage)
    47  	}
    48  
    49  	testStates := []State{StateAcceptRequest, StatePreprepared, StatePrepared, StateCommitted}
    50  	testCode := []uint64{msgPreprepare, msgPrepare, msgCommit, msgRoundChange}
    51  
    52  	// future sequence
    53  	v := &istanbul.View{
    54  		Sequence: big.NewInt(2),
    55  		Round:    big.NewInt(0),
    56  	}
    57  	for i := 0; i < len(testStates); i++ {
    58  		c.state = testStates[i]
    59  		for j := 0; j < len(testCode); j++ {
    60  			err := c.checkMessage(testCode[j], v)
    61  			if err != errFutureMessage {
    62  				t.Errorf("error mismatch: have %v, want %v", err, errFutureMessage)
    63  			}
    64  		}
    65  	}
    66  
    67  	// future round
    68  	v = &istanbul.View{
    69  		Sequence: big.NewInt(1),
    70  		Round:    big.NewInt(1),
    71  	}
    72  	for i := 0; i < len(testStates); i++ {
    73  		c.state = testStates[i]
    74  		for j := 0; j < len(testCode); j++ {
    75  			err := c.checkMessage(testCode[j], v)
    76  			if testCode[j] == msgRoundChange {
    77  				if err != nil {
    78  					t.Errorf("error mismatch: have %v, want nil", err)
    79  				}
    80  			} else if err != errFutureMessage {
    81  				t.Errorf("error mismatch: have %v, want %v", err, errFutureMessage)
    82  			}
    83  		}
    84  	}
    85  
    86  	// current view but waiting for round change
    87  	v = &istanbul.View{
    88  		Sequence: big.NewInt(1),
    89  		Round:    big.NewInt(0),
    90  	}
    91  	c.waitingForRoundChange = true
    92  	for i := 0; i < len(testStates); i++ {
    93  		c.state = testStates[i]
    94  		for j := 0; j < len(testCode); j++ {
    95  			err := c.checkMessage(testCode[j], v)
    96  			if testCode[j] == msgRoundChange {
    97  				if err != nil {
    98  					t.Errorf("error mismatch: have %v, want nil", err)
    99  				}
   100  			} else if err != errFutureMessage {
   101  				t.Errorf("error mismatch: have %v, want %v", err, errFutureMessage)
   102  			}
   103  		}
   104  	}
   105  	c.waitingForRoundChange = false
   106  
   107  	v = c.currentView()
   108  	// current view, state = StateAcceptRequest
   109  	c.state = StateAcceptRequest
   110  	for i := 0; i < len(testCode); i++ {
   111  		err = c.checkMessage(testCode[i], v)
   112  		if testCode[i] == msgRoundChange {
   113  			if err != nil {
   114  				t.Errorf("error mismatch: have %v, want nil", err)
   115  			}
   116  		} else if testCode[i] == msgPreprepare {
   117  			if err != nil {
   118  				t.Errorf("error mismatch: have %v, want nil", err)
   119  			}
   120  		} else {
   121  			if err != errFutureMessage {
   122  				t.Errorf("error mismatch: have %v, want %v", err, errFutureMessage)
   123  			}
   124  		}
   125  	}
   126  
   127  	// current view, state = StatePreprepared
   128  	c.state = StatePreprepared
   129  	for i := 0; i < len(testCode); i++ {
   130  		err = c.checkMessage(testCode[i], v)
   131  		if testCode[i] == msgRoundChange {
   132  			if err != nil {
   133  				t.Errorf("error mismatch: have %v, want nil", err)
   134  			}
   135  		} else if err != nil {
   136  			t.Errorf("error mismatch: have %v, want nil", err)
   137  		}
   138  	}
   139  
   140  	// current view, state = StatePrepared
   141  	c.state = StatePrepared
   142  	for i := 0; i < len(testCode); i++ {
   143  		err = c.checkMessage(testCode[i], v)
   144  		if testCode[i] == msgRoundChange {
   145  			if err != nil {
   146  				t.Errorf("error mismatch: have %v, want nil", err)
   147  			}
   148  		} else if err != nil {
   149  			t.Errorf("error mismatch: have %v, want nil", err)
   150  		}
   151  	}
   152  
   153  	// current view, state = StateCommitted
   154  	c.state = StateCommitted
   155  	for i := 0; i < len(testCode); i++ {
   156  		err = c.checkMessage(testCode[i], v)
   157  		if testCode[i] == msgRoundChange {
   158  			if err != nil {
   159  				t.Errorf("error mismatch: have %v, want nil", err)
   160  			}
   161  		} else if err != nil {
   162  			t.Errorf("error mismatch: have %v, want nil", err)
   163  		}
   164  	}
   165  
   166  }
   167  
   168  func TestStoreBacklog(t *testing.T) {
   169  	c := &core{
   170  		logger:     log.New("backend", "test", "id", 0),
   171  		backlogs:   make(map[istanbul.Validator]*prque.Prque),
   172  		backlogsMu: new(sync.Mutex),
   173  	}
   174  	v := &istanbul.View{
   175  		Round:    big.NewInt(10),
   176  		Sequence: big.NewInt(10),
   177  	}
   178  	p := validator.New(common.StringToAddress("12345667890"))
   179  	// push preprepare msg
   180  	preprepare := &istanbul.Preprepare{
   181  		View:     v,
   182  		Proposal: makeBlock(1),
   183  	}
   184  	prepreparePayload, _ := Encode(preprepare)
   185  	m := &message{
   186  		Code: msgPreprepare,
   187  		Msg:  prepreparePayload,
   188  	}
   189  	c.storeBacklog(m, p)
   190  	msg := c.backlogs[p].PopItem()
   191  	if !reflect.DeepEqual(msg, m) {
   192  		t.Errorf("message mismatch: have %v, want %v", msg, m)
   193  	}
   194  
   195  	// push prepare msg
   196  	subject := &istanbul.Subject{
   197  		View:   v,
   198  		Digest: common.StringToHash("1234567890"),
   199  	}
   200  	subjectPayload, _ := Encode(subject)
   201  
   202  	m = &message{
   203  		Code: msgPrepare,
   204  		Msg:  subjectPayload,
   205  	}
   206  	c.storeBacklog(m, p)
   207  	msg = c.backlogs[p].PopItem()
   208  	if !reflect.DeepEqual(msg, m) {
   209  		t.Errorf("message mismatch: have %v, want %v", msg, m)
   210  	}
   211  
   212  	// push commit msg
   213  	m = &message{
   214  		Code: msgCommit,
   215  		Msg:  subjectPayload,
   216  	}
   217  	c.storeBacklog(m, p)
   218  	msg = c.backlogs[p].PopItem()
   219  	if !reflect.DeepEqual(msg, m) {
   220  		t.Errorf("message mismatch: have %v, want %v", msg, m)
   221  	}
   222  
   223  	// push roundChange msg
   224  	m = &message{
   225  		Code: msgRoundChange,
   226  		Msg:  subjectPayload,
   227  	}
   228  	c.storeBacklog(m, p)
   229  	msg = c.backlogs[p].PopItem()
   230  	if !reflect.DeepEqual(msg, m) {
   231  		t.Errorf("message mismatch: have %v, want %v", msg, m)
   232  	}
   233  }
   234  
   235  func TestProcessFutureBacklog(t *testing.T) {
   236  	backend := &testSystemBackend{
   237  		events: new(event.TypeMux),
   238  	}
   239  	c := &core{
   240  		logger:     log.New("backend", "test", "id", 0),
   241  		backlogs:   make(map[istanbul.Validator]*prque.Prque),
   242  		backlogsMu: new(sync.Mutex),
   243  		backend:    backend,
   244  		current: newRoundState(&istanbul.View{
   245  			Sequence: big.NewInt(1),
   246  			Round:    big.NewInt(0),
   247  		}, newTestValidatorSet(4), common.Hash{}, nil, nil, nil),
   248  		state: StateAcceptRequest,
   249  	}
   250  	c.subscribeEvents()
   251  	defer c.unsubscribeEvents()
   252  
   253  	v := &istanbul.View{
   254  		Round:    big.NewInt(10),
   255  		Sequence: big.NewInt(10),
   256  	}
   257  	p := validator.New(common.StringToAddress("12345667890"))
   258  	// push a future msg
   259  	subject := &istanbul.Subject{
   260  		View:   v,
   261  		Digest: common.StringToHash("1234567890"),
   262  	}
   263  	subjectPayload, _ := Encode(subject)
   264  	m := &message{
   265  		Code: msgCommit,
   266  		Msg:  subjectPayload,
   267  	}
   268  	c.storeBacklog(m, p)
   269  	c.processBacklog()
   270  
   271  	const timeoutDura = 2 * time.Second
   272  	timeout := time.NewTimer(timeoutDura)
   273  	select {
   274  	case e, ok := <-c.events.Chan():
   275  		if !ok {
   276  			return
   277  		}
   278  		t.Errorf("unexpected events comes: %v", e)
   279  	case <-timeout.C:
   280  		// success
   281  	}
   282  }
   283  
   284  func TestProcessBacklog(t *testing.T) {
   285  	v := &istanbul.View{
   286  		Round:    big.NewInt(0),
   287  		Sequence: big.NewInt(1),
   288  	}
   289  	preprepare := &istanbul.Preprepare{
   290  		View:     v,
   291  		Proposal: makeBlock(1),
   292  	}
   293  	prepreparePayload, _ := Encode(preprepare)
   294  
   295  	subject := &istanbul.Subject{
   296  		View:   v,
   297  		Digest: common.StringToHash("1234567890"),
   298  	}
   299  	subjectPayload, _ := Encode(subject)
   300  
   301  	msgs := []*message{
   302  		&message{
   303  			Code: msgPreprepare,
   304  			Msg:  prepreparePayload,
   305  		},
   306  		&message{
   307  			Code: msgPrepare,
   308  			Msg:  subjectPayload,
   309  		},
   310  		&message{
   311  			Code: msgCommit,
   312  			Msg:  subjectPayload,
   313  		},
   314  		&message{
   315  			Code: msgRoundChange,
   316  			Msg:  subjectPayload,
   317  		},
   318  	}
   319  	for i := 0; i < len(msgs); i++ {
   320  		testProcessBacklog(t, msgs[i])
   321  	}
   322  }
   323  
   324  func testProcessBacklog(t *testing.T, msg *message) {
   325  	vset := newTestValidatorSet(1)
   326  	backend := &testSystemBackend{
   327  		events: new(event.TypeMux),
   328  		peers:  vset,
   329  	}
   330  	c := &core{
   331  		logger:     log.New("backend", "test", "id", 0),
   332  		backlogs:   make(map[istanbul.Validator]*prque.Prque),
   333  		backlogsMu: new(sync.Mutex),
   334  		backend:    backend,
   335  		state:      State(msg.Code),
   336  		current: newRoundState(&istanbul.View{
   337  			Sequence: big.NewInt(1),
   338  			Round:    big.NewInt(0),
   339  		}, newTestValidatorSet(4), common.Hash{}, nil, nil, nil),
   340  	}
   341  	c.subscribeEvents()
   342  	defer c.unsubscribeEvents()
   343  
   344  	c.storeBacklog(msg, vset.GetByIndex(0))
   345  	c.processBacklog()
   346  
   347  	const timeoutDura = 2 * time.Second
   348  	timeout := time.NewTimer(timeoutDura)
   349  	select {
   350  	case ev := <-c.events.Chan():
   351  		e, ok := ev.Data.(backlogEvent)
   352  		if !ok {
   353  			t.Errorf("unexpected event comes: %v", reflect.TypeOf(ev.Data))
   354  		}
   355  		if e.msg.Code != msg.Code {
   356  			t.Errorf("message code mismatch: have %v, want %v", e.msg.Code, msg.Code)
   357  		}
   358  		// success
   359  	case <-timeout.C:
   360  		t.Error("unexpected timeout occurs")
   361  	}
   362  }