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