github.com/hechain20/hechain@v0.0.0-20220316014945-b544036ba106/orderer/consensus/etcdraft/membership_test.go (about) 1 /* 2 Copyright hechain. All Rights Reserved. 3 4 SPDX-License-Identifier: Apache-2.0 5 */ 6 7 package etcdraft_test 8 9 import ( 10 "testing" 11 12 "github.com/hechain20/hechain/common/channelconfig" 13 "github.com/hechain20/hechain/common/crypto/tlsgen" 14 "github.com/hechain20/hechain/orderer/consensus/etcdraft" 15 "github.com/hechain20/hechain/orderer/consensus/etcdraft/mocks" 16 etcdraftproto "github.com/hyperledger/fabric-protos-go/orderer/etcdraft" 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 }