go_study/fabric-main/internal/pkg/gateway/endorsement_test.go

286 lines
11 KiB
Go

/*
Copyright 2021 IBM All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/
package gateway
import (
"testing"
"github.com/hyperledger/fabric-protos-go/peer"
"github.com/stretchr/testify/require"
)
func TestSingleLayoutPlan(t *testing.T) {
layouts := []*layout{
{required: map[string]int{"g1": 1, "g2": 2}},
}
groupEndorsers := map[string][]*endorser{
"g1": {peer1Mock},
"g2": {peer2Mock, peer3Mock},
}
plan := newPlan(layouts, groupEndorsers)
require.Equal(t, plan.size, 3) // total number of endorsers in all layouts
endorsers := plan.endorsers()
require.Len(t, endorsers, 3)
require.ElementsMatch(t, endorsers, []*endorser{peer1Mock, peer2Mock, peer3Mock})
response1 := &peer.ProposalResponse{Payload: []byte("p"), Endorsement: &peer.Endorsement{Endorser: []byte("e1")}}
response2 := &peer.ProposalResponse{Payload: []byte("p"), Endorsement: &peer.Endorsement{Endorser: []byte("e2")}}
response3 := &peer.ProposalResponse{Payload: []byte("p"), Endorsement: &peer.Endorsement{Endorser: []byte("e3")}}
success := plan.processEndorsement(peer1Mock, response1)
require.True(t, success)
require.Nil(t, plan.completedLayout)
success = plan.processEndorsement(peer2Mock, response2)
require.True(t, success)
require.Nil(t, plan.completedLayout)
success = plan.processEndorsement(peer3Mock, response3)
require.True(t, success)
require.Equal(t, plan.responsePayload, response1.Payload)
require.Len(t, plan.completedLayout.endorsements, 3)
require.ElementsMatch(t, plan.completedLayout.endorsements, []*peer.Endorsement{response1.Endorsement, response2.Endorsement, response3.Endorsement})
}
func TestSingleLayoutRetry(t *testing.T) {
layouts := []*layout{
{required: map[string]int{"g1": 1, "g2": 2}},
}
groupEndorsers := map[string][]*endorser{
"g1": {localhostMock, peer1Mock},
"g2": {peer2Mock, peer3Mock, peer4Mock},
}
plan := newPlan(layouts, groupEndorsers)
require.Equal(t, plan.size, 5) // total number of endorsers in all layouts
endorsers := plan.endorsers()
require.Len(t, endorsers, 3)
require.ElementsMatch(t, endorsers, []*endorser{localhostMock, peer2Mock, peer3Mock})
response1 := &peer.ProposalResponse{Payload: []byte("p"), Endorsement: &peer.Endorsement{Endorser: []byte("e1")}}
response2 := &peer.ProposalResponse{Payload: []byte("p"), Endorsement: &peer.Endorsement{Endorser: []byte("e2")}}
response3 := &peer.ProposalResponse{Payload: []byte("p"), Endorsement: &peer.Endorsement{Endorser: []byte("e3")}}
retry := plan.nextPeerInGroup(localhostMock)
require.Equal(t, peer1Mock, retry)
success := plan.processEndorsement(retry, response1)
require.True(t, success)
require.Nil(t, plan.completedLayout)
success = plan.processEndorsement(peer2Mock, response2)
require.True(t, success)
require.Nil(t, plan.completedLayout)
retry = plan.nextPeerInGroup(peer3Mock)
require.Equal(t, peer4Mock, retry)
success = plan.processEndorsement(retry, response3)
require.True(t, success)
require.Equal(t, plan.responsePayload, response1.Payload)
require.Len(t, plan.completedLayout.endorsements, 3)
require.ElementsMatch(t, plan.completedLayout.endorsements, []*peer.Endorsement{response1.Endorsement, response2.Endorsement, response3.Endorsement})
}
func TestMultiLayoutRetry(t *testing.T) {
layouts := []*layout{
{required: map[string]int{"g1": 1, "g2": 1}},
{required: map[string]int{"g1": 1, "g3": 1}},
{required: map[string]int{"g2": 1, "g3": 1}},
}
groupEndorsers := map[string][]*endorser{
"g1": {localhostMock, peer1Mock},
"g2": {peer2Mock, peer3Mock},
"g3": {peer4Mock},
}
plan := newPlan(layouts, groupEndorsers)
require.Equal(t, plan.size, 5) // total number of endorsers in all layouts
endorsers := plan.endorsers()
require.Len(t, endorsers, 2)
require.ElementsMatch(t, endorsers, []*endorser{localhostMock, peer2Mock})
response1 := &peer.ProposalResponse{Payload: []byte("p"), Endorsement: &peer.Endorsement{Endorser: []byte("e1")}}
response2 := &peer.ProposalResponse{Payload: []byte("p"), Endorsement: &peer.Endorsement{Endorser: []byte("e2")}}
// localhost (g1) fails, returns peer1 to retry
retry := plan.nextPeerInGroup(localhostMock)
require.Equal(t, peer1Mock, retry)
// peer2 (g2) succeeds
success := plan.processEndorsement(peer2Mock, response1)
require.True(t, success)
// peer1 (g1) also fails - returns nil, since no more peers in g1
retry = plan.nextPeerInGroup(retry)
require.Nil(t, retry)
// get endorsers for next layout - should be layout 3 because second layout also required g1
endorsers = plan.endorsers()
// layout 3 requires endorsement from g2 & g3, but we already have one for g2, so just need g3 peer
require.Len(t, endorsers, 1)
require.Equal(t, peer4Mock, endorsers[0])
success = plan.processEndorsement(peer4Mock, response2)
require.True(t, success)
require.Equal(t, plan.responsePayload, response1.Payload)
require.Len(t, plan.completedLayout.endorsements, 2)
require.ElementsMatch(t, plan.completedLayout.endorsements, []*peer.Endorsement{response1.Endorsement, response2.Endorsement})
}
func TestMultiLayoutFailures(t *testing.T) {
layouts := []*layout{
{required: map[string]int{"g1": 1, "g2": 1, "g3": 1}},
{required: map[string]int{"g1": 1, "g2": 2}},
{required: map[string]int{"g1": 2, "g2": 1}},
}
groupEndorsers := map[string][]*endorser{
"g1": {localhostMock, peer1Mock},
"g2": {peer2Mock, peer3Mock},
"g3": {peer4Mock},
}
plan := newPlan(layouts, groupEndorsers)
require.Equal(t, plan.size, 5) // total number of endorsers in all layouts
endorsers := plan.endorsers() // first layout
require.Len(t, endorsers, 3)
require.ElementsMatch(t, endorsers, []*endorser{localhostMock, peer2Mock, peer4Mock})
response1 := &peer.ProposalResponse{Payload: []byte("p"), Endorsement: &peer.Endorsement{Endorser: []byte("e1")}}
response2 := &peer.ProposalResponse{Payload: []byte("p"), Endorsement: &peer.Endorsement{Endorser: []byte("e2")}}
// localhost (g1) succeeds
success := plan.processEndorsement(localhostMock, response1)
require.True(t, success)
// peer2 (g2) fails - returns peer3 to retry
retry := plan.nextPeerInGroup(peer2Mock)
require.Equal(t, peer3Mock, retry)
// peer4 (g3) also fails - returns nil, since no more peers in g3
g3retry := plan.nextPeerInGroup(peer4Mock)
require.Nil(t, g3retry)
// retry g2 - succeeds
success = plan.processEndorsement(retry, response2)
require.True(t, success)
// nothing more to try in this layout - get endorsers for next layout
// layout 2 requires a second endorsement from g2, but all g2 peers have been tried - only 1 succeeded
// should return layout 3 which requires a second endorsement from g1
endorsers = plan.endorsers()
require.Len(t, endorsers, 1)
require.Equal(t, peer1Mock, endorsers[0])
// this one fails too
retry = plan.nextPeerInGroup(peer1Mock)
// no more in this group
require.Nil(t, retry)
endorsers = plan.endorsers()
// we've run out of layouts - failed to endorse!
require.Nil(t, endorsers)
}
func TestMultiPlan(t *testing.T) {
layouts1 := []*layout{
{required: map[string]int{"g1": 1}},
}
groupEndorsers1 := map[string][]*endorser{
"g1": {localhostMock, peer1Mock},
}
// plan 1 is used to determine the first endorser
plan1 := newPlan(layouts1, groupEndorsers1)
require.Equal(t, plan1.size, 2)
layouts2 := []*layout{
{required: map[string]int{"g1": 1, "g2": 1}},
{required: map[string]int{"g1": 1, "g3": 1}},
{required: map[string]int{"g2": 1, "g3": 1}},
}
groupEndorsers2 := map[string][]*endorser{
"g1": {localhostMock, peer1Mock},
"g2": {peer2Mock, peer3Mock},
"g3": {peer4Mock},
}
// plan 2 is derived from the chaincode interest from the first endorsement
plan2 := newPlan(layouts2, groupEndorsers2)
require.Equal(t, plan2.size, 5)
endorsers := plan1.endorsers()
require.Len(t, endorsers, 1)
require.ElementsMatch(t, endorsers, []*endorser{localhostMock})
response1 := &peer.ProposalResponse{Payload: []byte("p"), Endorsement: &peer.Endorsement{Endorser: []byte("e1")}}
response2 := &peer.ProposalResponse{Payload: []byte("p"), Endorsement: &peer.Endorsement{Endorser: []byte("e2")}}
// localhost succeeds, remove from plan 2
endorsers = plan2.endorsers()
require.Len(t, endorsers, 2)
success := plan2.processEndorsement(localhostMock, response1)
require.True(t, success)
// peer2 (g2) succeeds
success = plan2.processEndorsement(peer2Mock, response2)
require.True(t, success)
require.Equal(t, plan2.responsePayload, response1.Payload)
require.Len(t, plan2.completedLayout.endorsements, 2)
require.ElementsMatch(t, plan2.completedLayout.endorsements, []*peer.Endorsement{response1.Endorsement, response2.Endorsement})
}
func TestMultiPlanNoOverlap(t *testing.T) {
layouts1 := []*layout{
{required: map[string]int{"g1": 1}},
}
groupEndorsers1 := map[string][]*endorser{
"g1": {localhostMock, peer1Mock},
}
// plan 1 is used to determine the first endorser
plan1 := newPlan(layouts1, groupEndorsers1)
require.Equal(t, plan1.size, 2)
layouts2 := []*layout{
{required: map[string]int{"g2": 1, "g3": 1}},
}
groupEndorsers2 := map[string][]*endorser{
"g2": {peer2Mock, peer3Mock},
"g3": {peer4Mock},
}
// plan 2 is derived from the chaincode interest from the first endorsement, but doesn't include first endorser
plan2 := newPlan(layouts2, groupEndorsers2)
require.Equal(t, plan2.size, 3)
endorsers := plan1.endorsers()
require.Len(t, endorsers, 1)
require.ElementsMatch(t, endorsers, []*endorser{localhostMock})
response1 := &peer.ProposalResponse{Payload: []byte("p"), Endorsement: &peer.Endorsement{Endorser: []byte("e1")}}
response2 := &peer.ProposalResponse{Payload: []byte("p"), Endorsement: &peer.Endorsement{Endorser: []byte("e2")}}
response3 := &peer.ProposalResponse{Payload: []byte("p"), Endorsement: &peer.Endorsement{Endorser: []byte("e3")}}
// localhost succeeds, try to remove from plan 2 (should be no-op)
endorsers = plan2.endorsers()
require.Len(t, endorsers, 2)
success := plan2.processEndorsement(localhostMock, response1)
require.True(t, success)
// peer2 (g2) succeeds
success = plan2.processEndorsement(peer2Mock, response2)
require.True(t, success)
// peer4 (g3) succeeds
success = plan2.processEndorsement(peer4Mock, response3)
require.True(t, success)
require.Equal(t, plan2.responsePayload, response1.Payload)
require.Len(t, plan2.completedLayout.endorsements, 2)
require.ElementsMatch(t, plan2.completedLayout.endorsements, []*peer.Endorsement{response2.Endorsement, response3.Endorsement})
}
func TestUniqueEndorsements(t *testing.T) {
e1 := &peer.Endorsement{Endorser: []byte("endorserA")}
e2 := &peer.Endorsement{Endorser: []byte("endorserB")}
e3 := &peer.Endorsement{Endorser: []byte("endorserA")}
e4 := &peer.Endorsement{Endorser: []byte("endorserC")}
unique := uniqueEndorsements([]*peer.Endorsement{e1, e2, e3, e4})
require.Len(t, unique, 3)
require.ElementsMatch(t, unique, []*peer.Endorsement{e1, e2, e4})
}