github.com/hechain20/hechain@v0.0.0-20220316014945-b544036ba106/internal/pkg/gateway/endorsement_test.go (about) 1 /* 2 Copyright 2021 IBM All Rights Reserved. 3 4 SPDX-License-Identifier: Apache-2.0 5 */ 6 7 package gateway 8 9 import ( 10 "testing" 11 12 "github.com/hyperledger/fabric-protos-go/peer" 13 "github.com/stretchr/testify/require" 14 ) 15 16 func TestSingleLayoutPlan(t *testing.T) { 17 layouts := []*layout{ 18 {required: map[string]int{"g1": 1, "g2": 2}}, 19 } 20 groupEndorsers := map[string][]*endorser{ 21 "g1": {peer1Mock}, 22 "g2": {peer2Mock, peer3Mock}, 23 } 24 plan := newPlan(layouts, groupEndorsers) 25 require.Equal(t, plan.size, 3) // total number of endorsers in all layouts 26 27 endorsers := plan.endorsers() 28 require.Len(t, endorsers, 3) 29 require.ElementsMatch(t, endorsers, []*endorser{peer1Mock, peer2Mock, peer3Mock}) 30 31 response1 := &peer.ProposalResponse{Payload: []byte("p"), Endorsement: &peer.Endorsement{Endorser: []byte("e1")}} 32 response2 := &peer.ProposalResponse{Payload: []byte("p"), Endorsement: &peer.Endorsement{Endorser: []byte("e2")}} 33 response3 := &peer.ProposalResponse{Payload: []byte("p"), Endorsement: &peer.Endorsement{Endorser: []byte("e3")}} 34 35 success := plan.processEndorsement(peer1Mock, response1) 36 require.True(t, success) 37 require.Nil(t, plan.completedLayout) 38 success = plan.processEndorsement(peer2Mock, response2) 39 require.True(t, success) 40 require.Nil(t, plan.completedLayout) 41 success = plan.processEndorsement(peer3Mock, response3) 42 require.True(t, success) 43 require.Equal(t, plan.responsePayload, response1.Payload) 44 require.Len(t, plan.completedLayout.endorsements, 3) 45 require.ElementsMatch(t, plan.completedLayout.endorsements, []*peer.Endorsement{response1.Endorsement, response2.Endorsement, response3.Endorsement}) 46 } 47 48 func TestSingleLayoutRetry(t *testing.T) { 49 layouts := []*layout{ 50 {required: map[string]int{"g1": 1, "g2": 2}}, 51 } 52 groupEndorsers := map[string][]*endorser{ 53 "g1": {localhostMock, peer1Mock}, 54 "g2": {peer2Mock, peer3Mock, peer4Mock}, 55 } 56 plan := newPlan(layouts, groupEndorsers) 57 require.Equal(t, plan.size, 5) // total number of endorsers in all layouts 58 59 endorsers := plan.endorsers() 60 require.Len(t, endorsers, 3) 61 require.ElementsMatch(t, endorsers, []*endorser{localhostMock, peer2Mock, peer3Mock}) 62 63 response1 := &peer.ProposalResponse{Payload: []byte("p"), Endorsement: &peer.Endorsement{Endorser: []byte("e1")}} 64 response2 := &peer.ProposalResponse{Payload: []byte("p"), Endorsement: &peer.Endorsement{Endorser: []byte("e2")}} 65 response3 := &peer.ProposalResponse{Payload: []byte("p"), Endorsement: &peer.Endorsement{Endorser: []byte("e3")}} 66 67 retry := plan.nextPeerInGroup(localhostMock) 68 require.Equal(t, peer1Mock, retry) 69 success := plan.processEndorsement(retry, response1) 70 require.True(t, success) 71 require.Nil(t, plan.completedLayout) 72 success = plan.processEndorsement(peer2Mock, response2) 73 require.True(t, success) 74 require.Nil(t, plan.completedLayout) 75 retry = plan.nextPeerInGroup(peer3Mock) 76 require.Equal(t, peer4Mock, retry) 77 success = plan.processEndorsement(retry, response3) 78 require.True(t, success) 79 require.Equal(t, plan.responsePayload, response1.Payload) 80 require.Len(t, plan.completedLayout.endorsements, 3) 81 require.ElementsMatch(t, plan.completedLayout.endorsements, []*peer.Endorsement{response1.Endorsement, response2.Endorsement, response3.Endorsement}) 82 } 83 84 func TestMultiLayoutRetry(t *testing.T) { 85 layouts := []*layout{ 86 {required: map[string]int{"g1": 1, "g2": 1}}, 87 {required: map[string]int{"g1": 1, "g3": 1}}, 88 {required: map[string]int{"g2": 1, "g3": 1}}, 89 } 90 groupEndorsers := map[string][]*endorser{ 91 "g1": {localhostMock, peer1Mock}, 92 "g2": {peer2Mock, peer3Mock}, 93 "g3": {peer4Mock}, 94 } 95 plan := newPlan(layouts, groupEndorsers) 96 require.Equal(t, plan.size, 5) // total number of endorsers in all layouts 97 98 endorsers := plan.endorsers() 99 require.Len(t, endorsers, 2) 100 require.ElementsMatch(t, endorsers, []*endorser{localhostMock, peer2Mock}) 101 102 response1 := &peer.ProposalResponse{Payload: []byte("p"), Endorsement: &peer.Endorsement{Endorser: []byte("e1")}} 103 response2 := &peer.ProposalResponse{Payload: []byte("p"), Endorsement: &peer.Endorsement{Endorser: []byte("e2")}} 104 105 // localhost (g1) fails, returns peer1 to retry 106 retry := plan.nextPeerInGroup(localhostMock) 107 require.Equal(t, peer1Mock, retry) 108 109 // peer2 (g2) succeeds 110 success := plan.processEndorsement(peer2Mock, response1) 111 require.True(t, success) 112 113 // peer1 (g1) also fails - returns nil, since no more peers in g1 114 retry = plan.nextPeerInGroup(retry) 115 require.Nil(t, retry) 116 117 // get endorsers for next layout - should be layout 3 because second layout also required g1 118 endorsers = plan.endorsers() 119 // layout 3 requires endorsement from g2 & g3, but we already have one for g2, so just need g3 peer 120 require.Len(t, endorsers, 1) 121 require.Equal(t, peer4Mock, endorsers[0]) 122 123 success = plan.processEndorsement(peer4Mock, response2) 124 require.True(t, success) 125 require.Equal(t, plan.responsePayload, response1.Payload) 126 require.Len(t, plan.completedLayout.endorsements, 2) 127 require.ElementsMatch(t, plan.completedLayout.endorsements, []*peer.Endorsement{response1.Endorsement, response2.Endorsement}) 128 } 129 130 func TestMultiLayoutFailures(t *testing.T) { 131 layouts := []*layout{ 132 {required: map[string]int{"g1": 1, "g2": 1, "g3": 1}}, 133 {required: map[string]int{"g1": 1, "g2": 2}}, 134 {required: map[string]int{"g1": 2, "g2": 1}}, 135 } 136 groupEndorsers := map[string][]*endorser{ 137 "g1": {localhostMock, peer1Mock}, 138 "g2": {peer2Mock, peer3Mock}, 139 "g3": {peer4Mock}, 140 } 141 plan := newPlan(layouts, groupEndorsers) 142 require.Equal(t, plan.size, 5) // total number of endorsers in all layouts 143 144 endorsers := plan.endorsers() // first layout 145 require.Len(t, endorsers, 3) 146 require.ElementsMatch(t, endorsers, []*endorser{localhostMock, peer2Mock, peer4Mock}) 147 148 response1 := &peer.ProposalResponse{Payload: []byte("p"), Endorsement: &peer.Endorsement{Endorser: []byte("e1")}} 149 response2 := &peer.ProposalResponse{Payload: []byte("p"), Endorsement: &peer.Endorsement{Endorser: []byte("e2")}} 150 151 // localhost (g1) succeeds 152 success := plan.processEndorsement(localhostMock, response1) 153 require.True(t, success) 154 155 // peer2 (g2) fails - returns peer3 to retry 156 retry := plan.nextPeerInGroup(peer2Mock) 157 require.Equal(t, peer3Mock, retry) 158 159 // peer4 (g3) also fails - returns nil, since no more peers in g3 160 g3retry := plan.nextPeerInGroup(peer4Mock) 161 require.Nil(t, g3retry) 162 163 // retry g2 - succeeds 164 success = plan.processEndorsement(retry, response2) 165 require.True(t, success) 166 167 // nothing more to try in this layout - get endorsers for next layout 168 // layout 2 requires a second endorsement from g2, but all g2 peers have been tried - only 1 succeeded 169 // should return layout 3 which requires a second endorsement from g1 170 endorsers = plan.endorsers() 171 require.Len(t, endorsers, 1) 172 require.Equal(t, peer1Mock, endorsers[0]) 173 174 // this one fails too 175 retry = plan.nextPeerInGroup(peer1Mock) 176 // no more in this group 177 require.Nil(t, retry) 178 endorsers = plan.endorsers() 179 // we've run out of layouts - failed to endorse! 180 require.Nil(t, endorsers) 181 } 182 183 func TestMultiPlan(t *testing.T) { 184 layouts1 := []*layout{ 185 {required: map[string]int{"g1": 1}}, 186 } 187 groupEndorsers1 := map[string][]*endorser{ 188 "g1": {localhostMock, peer1Mock}, 189 } 190 // plan 1 is used to determine the first endorser 191 plan1 := newPlan(layouts1, groupEndorsers1) 192 require.Equal(t, plan1.size, 2) 193 194 layouts2 := []*layout{ 195 {required: map[string]int{"g1": 1, "g2": 1}}, 196 {required: map[string]int{"g1": 1, "g3": 1}}, 197 {required: map[string]int{"g2": 1, "g3": 1}}, 198 } 199 groupEndorsers2 := map[string][]*endorser{ 200 "g1": {localhostMock, peer1Mock}, 201 "g2": {peer2Mock, peer3Mock}, 202 "g3": {peer4Mock}, 203 } 204 // plan 2 is derived from the chaincode interest from the first endorsement 205 plan2 := newPlan(layouts2, groupEndorsers2) 206 require.Equal(t, plan2.size, 5) 207 208 endorsers := plan1.endorsers() 209 require.Len(t, endorsers, 1) 210 require.ElementsMatch(t, endorsers, []*endorser{localhostMock}) 211 212 response1 := &peer.ProposalResponse{Payload: []byte("p"), Endorsement: &peer.Endorsement{Endorser: []byte("e1")}} 213 response2 := &peer.ProposalResponse{Payload: []byte("p"), Endorsement: &peer.Endorsement{Endorser: []byte("e2")}} 214 215 // localhost succeeds, remove from plan 2 216 endorsers = plan2.endorsers() 217 require.Len(t, endorsers, 2) 218 success := plan2.processEndorsement(localhostMock, response1) 219 require.True(t, success) 220 221 // peer2 (g2) succeeds 222 success = plan2.processEndorsement(peer2Mock, response2) 223 require.True(t, success) 224 require.Equal(t, plan2.responsePayload, response1.Payload) 225 require.Len(t, plan2.completedLayout.endorsements, 2) 226 require.ElementsMatch(t, plan2.completedLayout.endorsements, []*peer.Endorsement{response1.Endorsement, response2.Endorsement}) 227 } 228 229 func TestMultiPlanNoOverlap(t *testing.T) { 230 layouts1 := []*layout{ 231 {required: map[string]int{"g1": 1}}, 232 } 233 groupEndorsers1 := map[string][]*endorser{ 234 "g1": {localhostMock, peer1Mock}, 235 } 236 // plan 1 is used to determine the first endorser 237 plan1 := newPlan(layouts1, groupEndorsers1) 238 require.Equal(t, plan1.size, 2) 239 240 layouts2 := []*layout{ 241 {required: map[string]int{"g2": 1, "g3": 1}}, 242 } 243 groupEndorsers2 := map[string][]*endorser{ 244 "g2": {peer2Mock, peer3Mock}, 245 "g3": {peer4Mock}, 246 } 247 // plan 2 is derived from the chaincode interest from the first endorsement, but doesn't include first endorser 248 plan2 := newPlan(layouts2, groupEndorsers2) 249 require.Equal(t, plan2.size, 3) 250 251 endorsers := plan1.endorsers() 252 require.Len(t, endorsers, 1) 253 require.ElementsMatch(t, endorsers, []*endorser{localhostMock}) 254 255 response1 := &peer.ProposalResponse{Payload: []byte("p"), Endorsement: &peer.Endorsement{Endorser: []byte("e1")}} 256 response2 := &peer.ProposalResponse{Payload: []byte("p"), Endorsement: &peer.Endorsement{Endorser: []byte("e2")}} 257 response3 := &peer.ProposalResponse{Payload: []byte("p"), Endorsement: &peer.Endorsement{Endorser: []byte("e3")}} 258 259 // localhost succeeds, try to remove from plan 2 (should be no-op) 260 endorsers = plan2.endorsers() 261 require.Len(t, endorsers, 2) 262 success := plan2.processEndorsement(localhostMock, response1) 263 require.True(t, success) 264 265 // peer2 (g2) succeeds 266 success = plan2.processEndorsement(peer2Mock, response2) 267 require.True(t, success) 268 269 // peer4 (g3) succeeds 270 success = plan2.processEndorsement(peer4Mock, response3) 271 require.True(t, success) 272 require.Equal(t, plan2.responsePayload, response1.Payload) 273 require.Len(t, plan2.completedLayout.endorsements, 2) 274 require.ElementsMatch(t, plan2.completedLayout.endorsements, []*peer.Endorsement{response2.Endorsement, response3.Endorsement}) 275 } 276 277 func TestUniqueEndorsements(t *testing.T) { 278 e1 := &peer.Endorsement{Endorser: []byte("endorserA")} 279 e2 := &peer.Endorsement{Endorser: []byte("endorserB")} 280 e3 := &peer.Endorsement{Endorser: []byte("endorserA")} 281 e4 := &peer.Endorsement{Endorser: []byte("endorserC")} 282 unique := uniqueEndorsements([]*peer.Endorsement{e1, e2, e3, e4}) 283 require.Len(t, unique, 3) 284 require.ElementsMatch(t, unique, []*peer.Endorsement{e1, e2, e4}) 285 }