github.com/osdi23p228/fabric@v0.0.0-20221218062954-77808885f5db/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/osdi23p228/fabric/common/channelconfig"
    14  	"github.com/osdi23p228/fabric/common/crypto/tlsgen"
    15  	"github.com/osdi23p228/fabric/orderer/consensus/etcdraft"
    16  	"github.com/osdi23p228/fabric/orderer/consensus/etcdraft/mocks"
    17  	"github.com/stretchr/testify/require"
    18  	"go.etcd.io/etcd/raft/raftpb"
    19  )
    20  
    21  func TestQuorumCheck(t *testing.T) {
    22  	tests := []struct {
    23  		Name          string
    24  		NewConsenters map[uint64]*etcdraftproto.Consenter
    25  		ConfChange    *raftpb.ConfChange
    26  		RotateNode    uint64
    27  		ActiveNodes   []uint64
    28  		QuorumLoss    bool
    29  	}{
    30  		// Notations:
    31  		//  1     - node 1 is alive
    32  		// (1)    - node 1 is dead
    33  		//  1'    - node 1's cert is being rotated. Node is considered to be dead in new set
    34  
    35  		// Add
    36  		{
    37  			Name:          "[1]->[1,(2)]",
    38  			NewConsenters: map[uint64]*etcdraftproto.Consenter{1: nil, 2: nil},
    39  			ConfChange:    &raftpb.ConfChange{NodeID: 2, Type: raftpb.ConfChangeAddNode},
    40  			ActiveNodes:   []uint64{1},
    41  			QuorumLoss:    false,
    42  		},
    43  		{
    44  			Name:          "[1,2]->[1,2,(3)]",
    45  			NewConsenters: map[uint64]*etcdraftproto.Consenter{1: nil, 2: nil, 3: nil},
    46  			ConfChange:    &raftpb.ConfChange{NodeID: 3, Type: raftpb.ConfChangeAddNode},
    47  			ActiveNodes:   []uint64{1, 2},
    48  			QuorumLoss:    false,
    49  		},
    50  		{
    51  			Name:          "[1,2,(3)]->[1,2,(3),(4)]",
    52  			NewConsenters: map[uint64]*etcdraftproto.Consenter{1: nil, 2: nil, 3: nil, 4: nil},
    53  			ConfChange:    &raftpb.ConfChange{NodeID: 4, Type: raftpb.ConfChangeAddNode},
    54  			ActiveNodes:   []uint64{1, 2},
    55  			QuorumLoss:    true,
    56  		},
    57  		{
    58  			Name:          "[1,2,3,(4)]->[1,2,3,(4),(5)]",
    59  			NewConsenters: map[uint64]*etcdraftproto.Consenter{1: nil, 2: nil, 3: nil, 4: nil, 5: nil},
    60  			ConfChange:    &raftpb.ConfChange{NodeID: 5, Type: raftpb.ConfChangeAddNode},
    61  			ActiveNodes:   []uint64{1, 2, 3},
    62  			QuorumLoss:    false,
    63  		},
    64  		// Rotate
    65  		{
    66  			Name:          "[1]->[1']",
    67  			NewConsenters: map[uint64]*etcdraftproto.Consenter{1: nil},
    68  			RotateNode:    1,
    69  			ActiveNodes:   []uint64{1},
    70  			QuorumLoss:    false,
    71  		},
    72  		{
    73  			Name:          "[1,2]->[1,2']",
    74  			NewConsenters: map[uint64]*etcdraftproto.Consenter{1: nil, 2: nil},
    75  			RotateNode:    2,
    76  			ActiveNodes:   []uint64{1, 2},
    77  			QuorumLoss:    false,
    78  		},
    79  		{
    80  			Name:          "[1,2,(3)]->[1,2',(3)]",
    81  			NewConsenters: map[uint64]*etcdraftproto.Consenter{1: nil, 2: nil, 3: nil},
    82  			RotateNode:    2,
    83  			ActiveNodes:   []uint64{1, 2},
    84  			QuorumLoss:    true,
    85  		},
    86  		{
    87  			Name:          "[1,2,(3)]->[1,2,(3')]",
    88  			NewConsenters: map[uint64]*etcdraftproto.Consenter{1: nil, 2: nil, 3: nil},
    89  			RotateNode:    3,
    90  			ActiveNodes:   []uint64{1, 2},
    91  			QuorumLoss:    false,
    92  		},
    93  		// Remove
    94  		{
    95  			Name:          "[1,2,(3)]->[1,2]",
    96  			NewConsenters: map[uint64]*etcdraftproto.Consenter{1: nil, 2: nil},
    97  			ConfChange:    &raftpb.ConfChange{NodeID: 3, Type: raftpb.ConfChangeRemoveNode},
    98  			ActiveNodes:   []uint64{1, 2},
    99  			QuorumLoss:    false,
   100  		},
   101  		{
   102  			Name:          "[1,2,(3)]->[1,(3)]",
   103  			NewConsenters: map[uint64]*etcdraftproto.Consenter{1: nil, 3: nil},
   104  			ConfChange:    &raftpb.ConfChange{NodeID: 2, Type: raftpb.ConfChangeRemoveNode},
   105  			ActiveNodes:   []uint64{1, 2},
   106  			QuorumLoss:    true,
   107  		},
   108  		{
   109  			Name:          "[1,2]->[1]",
   110  			NewConsenters: map[uint64]*etcdraftproto.Consenter{1: nil},
   111  			ConfChange:    &raftpb.ConfChange{NodeID: 2, Type: raftpb.ConfChangeRemoveNode},
   112  			ActiveNodes:   []uint64{1, 2},
   113  			QuorumLoss:    false,
   114  		},
   115  	}
   116  
   117  	for _, test := range tests {
   118  		t.Run(test.Name, func(t *testing.T) {
   119  			changes := &etcdraft.MembershipChanges{
   120  				NewConsenters: test.NewConsenters,
   121  				ConfChange:    test.ConfChange,
   122  				RotatedNode:   test.RotateNode,
   123  			}
   124  
   125  			require.Equal(t, test.QuorumLoss, changes.UnacceptableQuorumLoss(test.ActiveNodes))
   126  		})
   127  	}
   128  }
   129  
   130  func TestMembershipChanges(t *testing.T) {
   131  	blockMetadata := &etcdraftproto.BlockMetadata{
   132  		ConsenterIds:    []uint64{1, 2},
   133  		NextConsenterId: 3,
   134  	}
   135  
   136  	// generate certs for adding a new consenter
   137  	// certs for fake-org
   138  	tlsCA, err := tlsgen.NewCA()
   139  	require.NoError(t, err)
   140  	client1, err := tlsCA.NewClientCertKeyPair()
   141  	require.NoError(t, err)
   142  	tlsIntermediateCA, err := tlsCA.NewIntermediateCA()
   143  	require.NoError(t, err)
   144  	client2, err := tlsIntermediateCA.NewClientCertKeyPair()
   145  	require.NoError(t, err)
   146  
   147  	// certs for fake-org2
   148  	tlsCA2, err := tlsgen.NewCA()
   149  	require.NoError(t, err)
   150  	client3, err := tlsCA2.NewClientCertKeyPair()
   151  	require.NoError(t, err)
   152  	tlsIntermediateCA2, err := tlsCA2.NewIntermediateCA()
   153  	require.NoError(t, err)
   154  	client4, err := tlsIntermediateCA2.NewClientCertKeyPair()
   155  	require.NoError(t, err)
   156  
   157  	require.NoError(t, err)
   158  
   159  	c := []*etcdraftproto.Consenter{
   160  		{ClientTlsCert: client1.Cert, ServerTlsCert: client1.Cert},
   161  		{ClientTlsCert: client2.Cert, ServerTlsCert: client2.Cert},
   162  		{ClientTlsCert: client3.Cert, ServerTlsCert: client3.Cert},
   163  		{ClientTlsCert: client4.Cert, ServerTlsCert: client4.Cert},
   164  	}
   165  
   166  	mockOrdererConfig := &mocks.OrdererConfig{}
   167  	mockOrg := &mocks.OrdererOrg{}
   168  	mockMSP := &mocks.MSP{}
   169  	mockMSP.GetTLSRootCertsReturns([][]byte{
   170  		tlsCA.CertBytes(),
   171  	})
   172  	mockMSP.GetTLSIntermediateCertsReturns([][]byte{
   173  		tlsIntermediateCA.CertBytes(),
   174  	})
   175  	mockOrg.MSPReturns(mockMSP)
   176  
   177  	mockOrg2 := &mocks.OrdererOrg{}
   178  	mockMSP2 := &mocks.MSP{}
   179  	mockMSP2.GetTLSRootCertsReturns([][]byte{
   180  		tlsCA2.CertBytes(),
   181  	})
   182  	mockMSP2.GetTLSIntermediateCertsReturns([][]byte{
   183  		tlsIntermediateCA2.CertBytes(),
   184  	})
   185  
   186  	mockOrg2.MSPReturns(mockMSP2)
   187  
   188  	mockOrdererConfig.OrganizationsReturns(map[string]channelconfig.OrdererOrg{
   189  		"fake-org":  mockOrg,
   190  		"fake-org2": mockOrg2,
   191  	})
   192  
   193  	tests := []struct {
   194  		Name             string
   195  		OldConsenters    map[uint64]*etcdraftproto.Consenter
   196  		NewConsenters    []*etcdraftproto.Consenter
   197  		Changes          *etcdraft.MembershipChanges
   198  		Changed, Rotated bool
   199  		ExpectedErr      string
   200  	}{
   201  		{
   202  			Name: "Add a node",
   203  			OldConsenters: map[uint64]*etcdraftproto.Consenter{
   204  				1: c[0],
   205  				2: c[1],
   206  			},
   207  			NewConsenters: []*etcdraftproto.Consenter{
   208  				c[0],
   209  				c[1],
   210  				c[2],
   211  			},
   212  			Changes: &etcdraft.MembershipChanges{
   213  				NewBlockMetadata: &etcdraftproto.BlockMetadata{
   214  					ConsenterIds:    []uint64{1, 2, 3},
   215  					NextConsenterId: 4,
   216  				},
   217  				NewConsenters: map[uint64]*etcdraftproto.Consenter{1: c[0], 2: c[1], 3: c[2]},
   218  				AddedNodes:    []*etcdraftproto.Consenter{c[2]},
   219  				RemovedNodes:  []*etcdraftproto.Consenter{},
   220  				ConfChange: &raftpb.ConfChange{
   221  					NodeID: 3,
   222  					Type:   raftpb.ConfChangeAddNode,
   223  				},
   224  			},
   225  			Changed:     true,
   226  			Rotated:     false,
   227  			ExpectedErr: "",
   228  		},
   229  		{
   230  			Name: "Remove a node",
   231  			OldConsenters: map[uint64]*etcdraftproto.Consenter{
   232  				1: c[0],
   233  				2: c[1],
   234  			},
   235  			NewConsenters: []*etcdraftproto.Consenter{
   236  				c[1],
   237  			},
   238  			Changes: &etcdraft.MembershipChanges{
   239  				NewBlockMetadata: &etcdraftproto.BlockMetadata{
   240  					ConsenterIds:    []uint64{2},
   241  					NextConsenterId: 3,
   242  				},
   243  				NewConsenters: map[uint64]*etcdraftproto.Consenter{2: c[1]},
   244  				AddedNodes:    []*etcdraftproto.Consenter{},
   245  				RemovedNodes:  []*etcdraftproto.Consenter{c[0]},
   246  				ConfChange: &raftpb.ConfChange{
   247  					NodeID: 1,
   248  					Type:   raftpb.ConfChangeRemoveNode,
   249  				},
   250  			},
   251  			Changed:     true,
   252  			Rotated:     false,
   253  			ExpectedErr: "",
   254  		},
   255  		{
   256  			Name: "Rotate a certificate",
   257  			OldConsenters: map[uint64]*etcdraftproto.Consenter{
   258  				1: c[0],
   259  				2: c[1],
   260  			},
   261  			NewConsenters: []*etcdraftproto.Consenter{
   262  				c[0],
   263  				c[2],
   264  			},
   265  			Changes: &etcdraft.MembershipChanges{
   266  				NewBlockMetadata: &etcdraftproto.BlockMetadata{
   267  					ConsenterIds:    []uint64{1, 2},
   268  					NextConsenterId: 3,
   269  				},
   270  				NewConsenters: map[uint64]*etcdraftproto.Consenter{1: c[0], 2: c[2]},
   271  				AddedNodes:    []*etcdraftproto.Consenter{c[2]},
   272  				RemovedNodes:  []*etcdraftproto.Consenter{c[1]},
   273  				RotatedNode:   2,
   274  			},
   275  			Changed:     true,
   276  			Rotated:     true,
   277  			ExpectedErr: "",
   278  		},
   279  		{
   280  			Name: "No change",
   281  			OldConsenters: map[uint64]*etcdraftproto.Consenter{
   282  				1: c[0],
   283  				2: c[1],
   284  			},
   285  			NewConsenters: []*etcdraftproto.Consenter{
   286  				c[0],
   287  				c[1],
   288  			},
   289  			Changes: &etcdraft.MembershipChanges{
   290  				NewBlockMetadata: &etcdraftproto.BlockMetadata{
   291  					ConsenterIds:    []uint64{1, 2},
   292  					NextConsenterId: 3,
   293  				},
   294  				NewConsenters: map[uint64]*etcdraftproto.Consenter{1: c[0], 2: c[1]},
   295  				AddedNodes:    []*etcdraftproto.Consenter{},
   296  				RemovedNodes:  []*etcdraftproto.Consenter{},
   297  			},
   298  			Changed:     false,
   299  			Rotated:     false,
   300  			ExpectedErr: "",
   301  		},
   302  		{
   303  			Name: "More than one consenter added",
   304  			OldConsenters: map[uint64]*etcdraftproto.Consenter{
   305  				1: c[0],
   306  				2: c[1],
   307  			},
   308  			NewConsenters: []*etcdraftproto.Consenter{
   309  				c[0],
   310  				c[1],
   311  				c[2],
   312  				c[3],
   313  			},
   314  			Changes:     nil,
   315  			ExpectedErr: "update of more than one consenter at a time is not supported, requested changes: add 2 node(s), remove 0 node(s)",
   316  		},
   317  		{
   318  			Name: "More than one consenter removed",
   319  			OldConsenters: map[uint64]*etcdraftproto.Consenter{
   320  				1: c[0],
   321  				2: c[1],
   322  			},
   323  			NewConsenters: []*etcdraftproto.Consenter{
   324  				c[2],
   325  			},
   326  			Changes:     nil,
   327  			ExpectedErr: "update of more than one consenter at a time is not supported, requested changes: add 1 node(s), remove 2 node(s)",
   328  		},
   329  	}
   330  
   331  	for _, test := range tests {
   332  		t.Run(test.Name, func(t *testing.T) {
   333  			changes, err := etcdraft.ComputeMembershipChanges(blockMetadata, test.OldConsenters, test.NewConsenters)
   334  
   335  			if test.ExpectedErr != "" {
   336  				require.EqualError(t, err, test.ExpectedErr)
   337  			} else {
   338  				require.NoError(t, err)
   339  				require.Equal(t, test.Changes, changes)
   340  				require.Equal(t, test.Changed, changes.Changed())
   341  				require.Equal(t, test.Rotated, changes.Rotated())
   342  			}
   343  		})
   344  	}
   345  }