github.com/reapchain/go-reapchain@v0.2.15-0.20210609012950-9735c110c705/consensus/podc/core/prepare_test.go (about)

     1  // Copyright 2017 AMIS Technologies
     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  	"testing"
    23  
    24  	"github.com/ethereum/go-ethereum/common"
    25  	"github.com/ethereum/go-ethereum/consensus/podc"
    26  	"github.com/ethereum/go-ethereum/consensus/podc/validator"
    27  	"github.com/ethereum/go-ethereum/crypto"
    28  )
    29  
    30  func TestHandlePrepare(t *testing.T) {
    31  	N := uint64(4)
    32  	F := uint64(1)
    33  
    34  	proposal := newTestProposal()
    35  	expectedSubject := &podc.Subject{
    36  		View: &podc.View{
    37  			Round:    big.NewInt(0),
    38  			Sequence: proposal.Number(),
    39  		},
    40  		Digest: proposal.Hash(),
    41  	}
    42  
    43  	testCases := []struct {
    44  		system      *testSystem
    45  		expectedErr error
    46  	}{
    47  		{
    48  			// normal case
    49  			func() *testSystem {
    50  				sys := NewTestSystemWithBackend(N, F)
    51  
    52  				for i, backend := range sys.backends {
    53  					c := backend.engine.(*core)
    54  					c.valSet = backend.peers
    55  					c.current = newTestRoundState(
    56  						&podc.View{
    57  							Round:    big.NewInt(0),
    58  							Sequence: big.NewInt(1),
    59  						},
    60  						c.valSet,
    61  					)
    62  
    63  					if i == 0 {
    64  						// replica 0 is primary
    65  						c.state = StatePreprepared
    66  					}
    67  				}
    68  				return sys
    69  			}(),
    70  			nil,
    71  		},
    72  		{
    73  			// future message
    74  			func() *testSystem {
    75  				sys := NewTestSystemWithBackend(N, F)
    76  
    77  				for i, backend := range sys.backends {
    78  					c := backend.engine.(*core)
    79  					c.valSet = backend.peers
    80  					if i == 0 {
    81  						// replica 0 is primary
    82  						c.current = newTestRoundState(
    83  							expectedSubject.View,
    84  							c.valSet,
    85  						)
    86  						c.state = StatePreprepared
    87  					} else {
    88  						c.current = newTestRoundState(
    89  							&podc.View{
    90  								Round:    big.NewInt(2),
    91  								Sequence: big.NewInt(3),
    92  							},
    93  							c.valSet,
    94  						)
    95  					}
    96  				}
    97  				return sys
    98  			}(),
    99  			errFutureMessage,
   100  		},
   101  		{
   102  			// subject not match
   103  			func() *testSystem {
   104  				sys := NewTestSystemWithBackend(N, F)
   105  
   106  				for i, backend := range sys.backends {
   107  					c := backend.engine.(*core)
   108  					c.valSet = backend.peers
   109  					if i == 0 {
   110  						// replica 0 is primary
   111  						c.current = newTestRoundState(
   112  							expectedSubject.View,
   113  							c.valSet,
   114  						)
   115  						c.state = StatePreprepared
   116  					} else {
   117  						c.current = newTestRoundState(
   118  							&podc.View{
   119  								Round:    big.NewInt(0),
   120  								Sequence: big.NewInt(0),
   121  							},
   122  							c.valSet,
   123  						)
   124  					}
   125  				}
   126  				return sys
   127  			}(),
   128  			errOldMessage,
   129  		},
   130  		{
   131  			// subject not match
   132  			func() *testSystem {
   133  				sys := NewTestSystemWithBackend(N, F)
   134  
   135  				for i, backend := range sys.backends {
   136  					c := backend.engine.(*core)
   137  					c.valSet = backend.peers
   138  					if i == 0 {
   139  						// replica 0 is primary
   140  						c.current = newTestRoundState(
   141  							expectedSubject.View,
   142  							c.valSet,
   143  						)
   144  						c.state = StatePreprepared
   145  					} else {
   146  						c.current = newTestRoundState(
   147  							&podc.View{
   148  								Round:    big.NewInt(0),
   149  								Sequence: big.NewInt(1)},
   150  							c.valSet,
   151  						)
   152  					}
   153  				}
   154  				return sys
   155  			}(),
   156  			errInconsistentSubject,
   157  		},
   158  		{
   159  			// less than 2F+1
   160  			func() *testSystem {
   161  				sys := NewTestSystemWithBackend(N, F)
   162  
   163  				// save less than 2*F+1 replica
   164  				sys.backends = sys.backends[2*int(F)+1:]
   165  
   166  				for i, backend := range sys.backends {
   167  					c := backend.engine.(*core)
   168  					c.valSet = backend.peers
   169  					c.current = newTestRoundState(
   170  						expectedSubject.View,
   171  						c.valSet,
   172  					)
   173  
   174  					if i == 0 {
   175  						// replica 0 is primary
   176  						c.state = StatePreprepared
   177  					}
   178  				}
   179  				return sys
   180  			}(),
   181  			nil,
   182  		},
   183  		// TODO: double send message
   184  	}
   185  
   186  OUTER:
   187  	for _, test := range testCases {
   188  		test.system.Run(false)
   189  
   190  		v0 := test.system.backends[0]
   191  		r0 := v0.engine.(*core)
   192  
   193  		for i, v := range test.system.backends {
   194  			validator := r0.valSet.GetByIndex(uint64(i))
   195  			m, _ := Encode(v.engine.(*core).current.Subject())
   196  			if err := r0.handleDSelect(&message{  //handlePrepare
   197  				Code:    msgDSelect,  //msgDSelect
   198  				Msg:     m,
   199  				Address: validator.Address(),
   200  			}, validator); err != nil {
   201  				if err != test.expectedErr {
   202  					t.Errorf("error mismatch: have %v, want %v", err, test.expectedErr)
   203  				}
   204  				continue OUTER
   205  			}
   206  		}
   207  
   208  		// prepared is normal case
   209  		if r0.state != StateDSelected { //StatePrepared
   210  			// There are not enough prepared messages in core
   211  			if r0.state != StatePreprepared {
   212  				t.Errorf("state mismatch: have %v, want %v", r0.state, StatePreprepared)
   213  			}
   214  			if r0.current.Prepares.Size() > 2*r0.valSet.F() {
   215  				t.Errorf("the size of prepare messages should be less than %v", 2*r0.valSet.F()+1)
   216  			}
   217  
   218  			continue
   219  		}
   220  
   221  		// core should have 2F+1 prepare messages
   222  		if r0.current.Prepares.Size() <= 2*r0.valSet.F() {
   223  			t.Errorf("the size of prepare messages should be larger than 2F+1: size %v", r0.current.Commits.Size())
   224  		}
   225  
   226  		// a message will be delivered to backend if 2F+1
   227  		if int64(len(v0.sentMsgs)) != 1 {
   228  			t.Errorf("the Send() should be called once: times %v", len(test.system.backends[0].sentMsgs))
   229  		}
   230  
   231  		// verify commit messages
   232  		decodedMsg := new(message)
   233  		err := decodedMsg.FromPayload(v0.sentMsgs[0], nil)
   234  		if err != nil {
   235  			t.Errorf("error mismatch: have %v, want nil", err)
   236  		}
   237  
   238  		if decodedMsg.Code != msgCommit {
   239  			t.Errorf("message code mismatch: have %v, want %v", decodedMsg.Code, msgCommit)
   240  		}
   241  		var m *podc.Subject
   242  		err = decodedMsg.Decode(&m)
   243  		if err != nil {
   244  			t.Errorf("error mismatch: have %v, want nil", err)
   245  		}
   246  		if !reflect.DeepEqual(m, expectedSubject) {
   247  			t.Errorf("subject mismatch: have %v, want %v", m, expectedSubject)
   248  		}
   249  	}
   250  }
   251  
   252  // round is not checked for now
   253  func TestVerifyPrepare(t *testing.T) {
   254  	// for log purpose
   255  	privateKey, _ := crypto.GenerateKey()
   256  	peer := validator.New(getPublicKeyAddress(privateKey))
   257  	valSet := validator.NewSet([]common.Address{peer.Address()}, podc.RoundRobin)
   258  
   259  	sys := NewTestSystemWithBackend(uint64(1), uint64(0))
   260  
   261  	testCases := []struct {
   262  		expected error
   263  
   264  		prepare    *podc.Subject
   265  		roundState *roundState
   266  	}{
   267  		{
   268  			// normal case
   269  			expected: nil,
   270  			prepare: &podc.Subject{
   271  				View:   &podc.View{Round: big.NewInt(0), Sequence: big.NewInt(0)},
   272  				Digest: newTestProposal().Hash(),
   273  			},
   274  			roundState: newTestRoundState(
   275  				&podc.View{Round: big.NewInt(0), Sequence: big.NewInt(0)},
   276  				valSet,
   277  			),
   278  		},
   279  		{
   280  			// old message
   281  			expected: errInconsistentSubject,
   282  			prepare: &podc.Subject{
   283  				View:   &podc.View{Round: big.NewInt(0), Sequence: big.NewInt(0)},
   284  				Digest: newTestProposal().Hash(),
   285  			},
   286  			roundState: newTestRoundState(
   287  				&podc.View{Round: big.NewInt(1), Sequence: big.NewInt(1)},
   288  				valSet,
   289  			),
   290  		},
   291  		{
   292  			// different digest
   293  			expected: errInconsistentSubject,
   294  			prepare: &podc.Subject{
   295  				View:   &podc.View{Round: big.NewInt(0), Sequence: big.NewInt(0)},
   296  				Digest: common.StringToHash("1234567890"),
   297  			},
   298  			roundState: newTestRoundState(
   299  				&podc.View{Round: big.NewInt(1), Sequence: big.NewInt(1)},
   300  				valSet,
   301  			),
   302  		},
   303  		{
   304  			// malicious package(lack of sequence)
   305  			expected: errInconsistentSubject,
   306  			prepare: &podc.Subject{
   307  				View:   &podc.View{Round: big.NewInt(0), Sequence: nil},
   308  				Digest: newTestProposal().Hash(),
   309  			},
   310  			roundState: newTestRoundState(
   311  				&podc.View{Round: big.NewInt(1), Sequence: big.NewInt(1)},
   312  				valSet,
   313  			),
   314  		},
   315  		{
   316  			// wrong prepare message with same sequence but different round
   317  			expected: errInconsistentSubject,
   318  			prepare: &podc.Subject{
   319  				View:   &podc.View{Round: big.NewInt(1), Sequence: big.NewInt(0)},
   320  				Digest: newTestProposal().Hash(),
   321  			},
   322  			roundState: newTestRoundState(
   323  				&podc.View{Round: big.NewInt(0), Sequence: big.NewInt(0)},
   324  				valSet,
   325  			),
   326  		},
   327  		{
   328  			// wrong prepare message with same round but different sequence
   329  			expected: errInconsistentSubject,
   330  			prepare: &podc.Subject{
   331  				View:   &podc.View{Round: big.NewInt(0), Sequence: big.NewInt(1)},
   332  				Digest: newTestProposal().Hash(),
   333  			},
   334  			roundState: newTestRoundState(
   335  				&podc.View{Round: big.NewInt(0), Sequence: big.NewInt(0)},
   336  				valSet,
   337  			),
   338  		},
   339  	}
   340  	for i, test := range testCases {
   341  		c := sys.backends[0].engine.(*core)
   342  		c.current = test.roundState
   343  
   344  		if err := c.verifyDCommit(test.prepare, peer); err != nil {  //verifyPrepare
   345  			if err != test.expected {
   346  				t.Errorf("result %d: error mismatch: have %v, want %v", i, err, test.expected)
   347  			}
   348  		}
   349  	}
   350  }