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