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  }