github.com/kaituanwang/hyperledger@v2.0.1+incompatible/orderer/consensus/etcdraft/membership_test.go (about)

     1  /*
     2  Copyright IBM Corp. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package etcdraft_test
     8  
     9  import (
    10  	"testing"
    11  
    12  	etcdraftproto "github.com/hyperledger/fabric-protos-go/orderer/etcdraft"
    13  	"github.com/hyperledger/fabric/orderer/consensus/etcdraft"
    14  	"github.com/stretchr/testify/require"
    15  	"go.etcd.io/etcd/raft/raftpb"
    16  )
    17  
    18  func TestQuorumCheck(t *testing.T) {
    19  	tests := []struct {
    20  		Name          string
    21  		NewConsenters map[uint64]*etcdraftproto.Consenter
    22  		ConfChange    *raftpb.ConfChange
    23  		RotateNode    uint64
    24  		ActiveNodes   []uint64
    25  		QuorumLoss    bool
    26  	}{
    27  		// Notations:
    28  		//  1     - node 1 is alive
    29  		// (1)    - node 1 is dead
    30  		//  1'    - node 1's cert is being rotated. Node is considered to be dead in new set
    31  
    32  		// Add
    33  		{
    34  			Name:          "[1]->[1,(2)]",
    35  			NewConsenters: map[uint64]*etcdraftproto.Consenter{1: nil, 2: nil},
    36  			ConfChange:    &raftpb.ConfChange{NodeID: 2, Type: raftpb.ConfChangeAddNode},
    37  			ActiveNodes:   []uint64{1},
    38  			QuorumLoss:    false,
    39  		},
    40  		{
    41  			Name:          "[1,2]->[1,2,(3)]",
    42  			NewConsenters: map[uint64]*etcdraftproto.Consenter{1: nil, 2: nil, 3: nil},
    43  			ConfChange:    &raftpb.ConfChange{NodeID: 3, Type: raftpb.ConfChangeAddNode},
    44  			ActiveNodes:   []uint64{1, 2},
    45  			QuorumLoss:    false,
    46  		},
    47  		{
    48  			Name:          "[1,2,(3)]->[1,2,(3),(4)]",
    49  			NewConsenters: map[uint64]*etcdraftproto.Consenter{1: nil, 2: nil, 3: nil, 4: nil},
    50  			ConfChange:    &raftpb.ConfChange{NodeID: 4, Type: raftpb.ConfChangeAddNode},
    51  			ActiveNodes:   []uint64{1, 2},
    52  			QuorumLoss:    true,
    53  		},
    54  		{
    55  			Name:          "[1,2,3,(4)]->[1,2,3,(4),(5)]",
    56  			NewConsenters: map[uint64]*etcdraftproto.Consenter{1: nil, 2: nil, 3: nil, 4: nil, 5: nil},
    57  			ConfChange:    &raftpb.ConfChange{NodeID: 5, Type: raftpb.ConfChangeAddNode},
    58  			ActiveNodes:   []uint64{1, 2, 3},
    59  			QuorumLoss:    false,
    60  		},
    61  		// Rotate
    62  		{
    63  			Name:          "[1]->[1']",
    64  			NewConsenters: map[uint64]*etcdraftproto.Consenter{1: nil},
    65  			RotateNode:    1,
    66  			ActiveNodes:   []uint64{1},
    67  			QuorumLoss:    false,
    68  		},
    69  		{
    70  			Name:          "[1,2]->[1,2']",
    71  			NewConsenters: map[uint64]*etcdraftproto.Consenter{1: nil, 2: nil},
    72  			RotateNode:    2,
    73  			ActiveNodes:   []uint64{1, 2},
    74  			QuorumLoss:    false,
    75  		},
    76  		{
    77  			Name:          "[1,2,(3)]->[1,2',(3)]",
    78  			NewConsenters: map[uint64]*etcdraftproto.Consenter{1: nil, 2: nil, 3: nil},
    79  			RotateNode:    2,
    80  			ActiveNodes:   []uint64{1, 2},
    81  			QuorumLoss:    true,
    82  		},
    83  		{
    84  			Name:          "[1,2,(3)]->[1,2,(3')]",
    85  			NewConsenters: map[uint64]*etcdraftproto.Consenter{1: nil, 2: nil, 3: nil},
    86  			RotateNode:    3,
    87  			ActiveNodes:   []uint64{1, 2},
    88  			QuorumLoss:    false,
    89  		},
    90  		// Remove
    91  		{
    92  			Name:          "[1,2,(3)]->[1,2]",
    93  			NewConsenters: map[uint64]*etcdraftproto.Consenter{1: nil, 2: nil},
    94  			ConfChange:    &raftpb.ConfChange{NodeID: 3, Type: raftpb.ConfChangeRemoveNode},
    95  			ActiveNodes:   []uint64{1, 2},
    96  			QuorumLoss:    false,
    97  		},
    98  		{
    99  			Name:          "[1,2,(3)]->[1,(3)]",
   100  			NewConsenters: map[uint64]*etcdraftproto.Consenter{1: nil, 3: nil},
   101  			ConfChange:    &raftpb.ConfChange{NodeID: 2, Type: raftpb.ConfChangeRemoveNode},
   102  			ActiveNodes:   []uint64{1, 2},
   103  			QuorumLoss:    true,
   104  		},
   105  		{
   106  			Name:          "[1,2]->[1]",
   107  			NewConsenters: map[uint64]*etcdraftproto.Consenter{1: nil},
   108  			ConfChange:    &raftpb.ConfChange{NodeID: 2, Type: raftpb.ConfChangeRemoveNode},
   109  			ActiveNodes:   []uint64{1, 2},
   110  			QuorumLoss:    false,
   111  		},
   112  	}
   113  
   114  	for _, test := range tests {
   115  		t.Run(test.Name, func(t *testing.T) {
   116  			changes := &etcdraft.MembershipChanges{
   117  				NewConsenters: test.NewConsenters,
   118  				ConfChange:    test.ConfChange,
   119  				RotatedNode:   test.RotateNode,
   120  			}
   121  
   122  			require.Equal(t, test.QuorumLoss, changes.UnacceptableQuorumLoss(test.ActiveNodes))
   123  		})
   124  	}
   125  }
   126  
   127  func TestMembershipChanges(t *testing.T) {
   128  	blockMetadata := &etcdraftproto.BlockMetadata{
   129  		ConsenterIds:    []uint64{1, 2},
   130  		NextConsenterId: 3,
   131  	}
   132  	c := []*etcdraftproto.Consenter{
   133  		{ClientTlsCert: []byte("first")},
   134  		{ClientTlsCert: []byte("second")},
   135  		{ClientTlsCert: []byte("third")},
   136  		{ClientTlsCert: []byte("4th")},
   137  	}
   138  	tests := []struct {
   139  		Name                        string
   140  		OldConsenters               map[uint64]*etcdraftproto.Consenter
   141  		NewConsenters               []*etcdraftproto.Consenter
   142  		Changes                     *etcdraft.MembershipChanges
   143  		HaveError, Changed, Rotated bool
   144  	}{
   145  		{
   146  			Name:          "Add a node",
   147  			OldConsenters: map[uint64]*etcdraftproto.Consenter{1: c[0], 2: c[1]},
   148  			NewConsenters: []*etcdraftproto.Consenter{c[0], c[1], c[2]},
   149  			Changes: &etcdraft.MembershipChanges{
   150  				NewBlockMetadata: &etcdraftproto.BlockMetadata{
   151  					ConsenterIds:    []uint64{1, 2, 3},
   152  					NextConsenterId: 4,
   153  				},
   154  				NewConsenters: map[uint64]*etcdraftproto.Consenter{1: c[0], 2: c[1], 3: c[2]},
   155  				AddedNodes:    []*etcdraftproto.Consenter{c[2]},
   156  				RemovedNodes:  []*etcdraftproto.Consenter{},
   157  				ConfChange: &raftpb.ConfChange{
   158  					NodeID: 3,
   159  					Type:   raftpb.ConfChangeAddNode,
   160  				},
   161  			},
   162  			HaveError: false,
   163  			Changed:   true,
   164  			Rotated:   false,
   165  		},
   166  		{
   167  			Name:          "Remove a node",
   168  			OldConsenters: map[uint64]*etcdraftproto.Consenter{1: c[0], 2: c[1]},
   169  			NewConsenters: []*etcdraftproto.Consenter{c[1]},
   170  			Changes: &etcdraft.MembershipChanges{
   171  				NewBlockMetadata: &etcdraftproto.BlockMetadata{
   172  					ConsenterIds:    []uint64{2},
   173  					NextConsenterId: 3,
   174  				},
   175  				NewConsenters: map[uint64]*etcdraftproto.Consenter{2: c[1]},
   176  				AddedNodes:    []*etcdraftproto.Consenter{},
   177  				RemovedNodes:  []*etcdraftproto.Consenter{c[0]},
   178  				ConfChange: &raftpb.ConfChange{
   179  					NodeID: 1,
   180  					Type:   raftpb.ConfChangeRemoveNode,
   181  				},
   182  			},
   183  			HaveError: false,
   184  			Changed:   true,
   185  			Rotated:   false,
   186  		},
   187  		{
   188  			Name:          "Rotate a certificate",
   189  			OldConsenters: map[uint64]*etcdraftproto.Consenter{1: c[0], 2: c[1]},
   190  			NewConsenters: []*etcdraftproto.Consenter{c[0], c[2]},
   191  			Changes: &etcdraft.MembershipChanges{
   192  				NewBlockMetadata: &etcdraftproto.BlockMetadata{
   193  					ConsenterIds:    []uint64{1, 2},
   194  					NextConsenterId: 3,
   195  				},
   196  				NewConsenters: map[uint64]*etcdraftproto.Consenter{1: c[0], 2: c[2]},
   197  				AddedNodes:    []*etcdraftproto.Consenter{c[2]},
   198  				RemovedNodes:  []*etcdraftproto.Consenter{c[1]},
   199  				RotatedNode:   2,
   200  			},
   201  			HaveError: false,
   202  			Changed:   true,
   203  			Rotated:   true,
   204  		},
   205  		{
   206  			Name:          "No change",
   207  			OldConsenters: map[uint64]*etcdraftproto.Consenter{1: c[0], 2: c[1]},
   208  			NewConsenters: []*etcdraftproto.Consenter{c[0], c[1]},
   209  			Changes: &etcdraft.MembershipChanges{
   210  				NewBlockMetadata: &etcdraftproto.BlockMetadata{
   211  					ConsenterIds:    []uint64{1, 2},
   212  					NextConsenterId: 3,
   213  				},
   214  				NewConsenters: map[uint64]*etcdraftproto.Consenter{1: c[0], 2: c[1]},
   215  				AddedNodes:    []*etcdraftproto.Consenter{},
   216  				RemovedNodes:  []*etcdraftproto.Consenter{},
   217  			},
   218  			HaveError: false,
   219  			Changed:   false,
   220  			Rotated:   false,
   221  		},
   222  		{
   223  			Name:          "Too many adds",
   224  			OldConsenters: map[uint64]*etcdraftproto.Consenter{1: c[0], 2: c[1]},
   225  			NewConsenters: []*etcdraftproto.Consenter{c[0], c[1], c[2], c[3]},
   226  			Changes:       nil,
   227  			HaveError:     true,
   228  		},
   229  		{
   230  			Name:          "Too many removes",
   231  			OldConsenters: map[uint64]*etcdraftproto.Consenter{1: c[0], 2: c[1]},
   232  			NewConsenters: []*etcdraftproto.Consenter{c[2]},
   233  			Changes:       nil,
   234  			HaveError:     true,
   235  		},
   236  	}
   237  
   238  	for _, test := range tests {
   239  		t.Run(test.Name, func(t *testing.T) {
   240  			changes, err := etcdraft.ComputeMembershipChanges(blockMetadata, test.OldConsenters, test.NewConsenters)
   241  
   242  			if test.HaveError {
   243  				require.NotNil(t, err)
   244  			} else {
   245  				require.NoError(t, err)
   246  				require.Equal(t, changes, test.Changes)
   247  				require.Equal(t, changes.Changed(), test.Changed)
   248  				require.Equal(t, changes.Rotated(), test.Rotated)
   249  			}
   250  		})
   251  	}
   252  }