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 }