Skip to content
This repository was archived by the owner on Apr 25, 2025. It is now read-only.

Commit 5c3f546

Browse files
Get proposal response payload from chaincode action (#206)
This is the correct chaincode transaction response payload location. The response payload field is intended to contain metadata but the peer currently copies the transaction response payload here for convenience. This causes failures due to gRPC message size limits for large response payloads and so may be removed in the future. Also changed a few build scripts to honour the GO_CMD environment variable. Signed-off-by: Mark S. Lewis <[email protected]>
1 parent 4f34271 commit 5c3f546

File tree

9 files changed

+223
-94
lines changed

9 files changed

+223
-94
lines changed

pkg/client/channel/chclient_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ import (
1515
"github.com/pkg/errors"
1616
"github.com/stretchr/testify/assert"
1717

18+
"github.com/hyperledger/fabric-protos-go/common"
19+
pb "github.com/hyperledger/fabric-protos-go/peer"
1820
"github.com/hyperledger/fabric-sdk-go/pkg/client/channel/invoke"
1921
txnmocks "github.com/hyperledger/fabric-sdk-go/pkg/client/common/mocks"
2022
"github.com/hyperledger/fabric-sdk-go/pkg/client/common/selection/staticselection"
@@ -27,8 +29,6 @@ import (
2729
"github.com/hyperledger/fabric-sdk-go/pkg/fab/peer"
2830
"github.com/hyperledger/fabric-sdk-go/pkg/fab/txn"
2931
mspmocks "github.com/hyperledger/fabric-sdk-go/pkg/msp/test/mockmsp"
30-
"github.com/hyperledger/fabric-protos-go/common"
31-
pb "github.com/hyperledger/fabric-protos-go/peer"
3232
)
3333

3434
const (

pkg/client/channel/invoke/txnhandler.go

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"bytes"
1111
"strings"
1212

13+
"github.com/golang/protobuf/proto"
1314
"github.com/hyperledger/fabric-sdk-go/pkg/common/errors/status"
1415
"github.com/hyperledger/fabric-sdk-go/pkg/common/options"
1516
"github.com/pkg/errors"
@@ -63,7 +64,13 @@ func (e *EndorsementHandler) Handle(requestContext *RequestContext, clientContex
6364

6465
requestContext.Response.Responses = transactionProposalResponses
6566
if len(transactionProposalResponses) > 0 {
66-
requestContext.Response.Payload = transactionProposalResponses[0].ProposalResponse.GetResponse().Payload
67+
responsePayload, err := getResultFromProposalResponse(transactionProposalResponses[0].ProposalResponse)
68+
if err != nil {
69+
requestContext.Error = err
70+
return
71+
}
72+
73+
requestContext.Response.Payload = responsePayload
6774
requestContext.Response.ChaincodeStatus = transactionProposalResponses[0].ChaincodeStatus
6875
}
6976

@@ -73,6 +80,24 @@ func (e *EndorsementHandler) Handle(requestContext *RequestContext, clientContex
7380
}
7481
}
7582

83+
func getResultFromProposalResponse(proposalResponse *pb.ProposalResponse) ([]byte, error) {
84+
responsePayload := &pb.ProposalResponsePayload{}
85+
if err := proto.Unmarshal(proposalResponse.GetPayload(), responsePayload); err != nil {
86+
return nil, errors.Wrap(err, "failed to deserialize proposal response payload")
87+
}
88+
89+
return getResultFromProposalResponsePayload(responsePayload)
90+
}
91+
92+
func getResultFromProposalResponsePayload(responsePayload *pb.ProposalResponsePayload) ([]byte, error) {
93+
chaincodeAction := &pb.ChaincodeAction{}
94+
if err := proto.Unmarshal(responsePayload.GetExtension(), chaincodeAction); err != nil {
95+
return nil, errors.Wrap(err, "failed to deserialize chaincode action")
96+
}
97+
98+
return chaincodeAction.GetResponse().GetPayload(), nil
99+
}
100+
76101
//ProposalProcessorHandler for selecting proposal processors
77102
type ProposalProcessorHandler struct {
78103
next Handler

pkg/client/channel/invoke/txnhandler_test.go

Lines changed: 94 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import (
1414
"testing"
1515
"time"
1616

17+
"github.com/golang/protobuf/proto"
1718
pb "github.com/hyperledger/fabric-protos-go/peer"
1819
"github.com/pkg/errors"
1920
"github.com/stretchr/testify/assert"
@@ -162,47 +163,104 @@ func TestExecuteTxHandlerErrors(t *testing.T) {
162163
func TestEndorsementHandler(t *testing.T) {
163164
request := Request{ChaincodeID: "test", Fcn: "invoke", Args: [][]byte{[]byte("move"), []byte("a"), []byte("b"), []byte("1")}}
164165

165-
clientContext := setupChannelClientContext(nil, nil, nil, t)
166-
requestContext := prepareRequestContext(request, Opts{Targets: nil}, t)
166+
t.Run("no targets, produces an error", func(t *testing.T) {
167+
clientContext := setupChannelClientContext(nil, nil, nil, t)
168+
requestContext := prepareRequestContext(request, Opts{Targets: nil}, t)
169+
handler := NewEndorsementHandler()
167170

168-
handler := NewEndorsementHandler()
169-
handler.Handle(requestContext, clientContext)
170-
assert.NotNil(t, requestContext.Error)
171+
handler.Handle(requestContext, clientContext)
171172

172-
requestContext = prepareRequestContext(request, Opts{Targets: []fab.Peer{fcmocks.NewMockPeer("p2", "")}}, t)
173+
require.Error(t, requestContext.Error)
174+
})
173175

174-
handler = NewEndorsementHandler()
175-
handler.Handle(requestContext, clientContext)
176-
assert.Nil(t, requestContext.Error)
176+
t.Run("with a target, runs without error", func(t *testing.T) {
177+
clientContext := setupChannelClientContext(nil, nil, nil, t)
178+
requestContext := prepareRequestContext(request, Opts{Targets: []fab.Peer{fcmocks.NewMockPeer("p2", "")}}, t)
179+
handler := NewEndorsementHandler()
177180

178-
optsProviderCalled := false
179-
optsProvider := func() []fab.TxnHeaderOpt {
180-
optsProviderCalled = true
181-
var opts []fab.TxnHeaderOpt
182-
opts = append(opts, fab.WithCreator([]byte("somecreator")))
183-
opts = append(opts, fab.WithNonce([]byte("somenonce")))
184-
return opts
185-
}
181+
handler.Handle(requestContext, clientContext)
186182

187-
handler = NewEndorsementHandlerWithOpts(nil, optsProvider)
188-
handler.Handle(requestContext, clientContext)
189-
assert.Nil(t, requestContext.Error)
190-
assert.Truef(t, optsProviderCalled, "expecting opts provider to be called")
191-
192-
errExpected := fmt.Errorf("error in simulation: failed to distribute private collection, txID 695560b")
193-
clientContext.Transactor.(*txnmocks.MockTransactor).Err = errExpected
194-
handler.Handle(requestContext, clientContext)
195-
s, ok := requestContext.Error.(*status.Status)
196-
require.True(t, ok)
197-
require.Equal(t, status.EndorserServerStatus, s.Group)
198-
require.Equal(t, status.PvtDataDisseminationFailed.ToInt32(), s.Code)
199-
200-
errExpected = fmt.Errorf("error in simulation")
201-
clientContext.Transactor.(*txnmocks.MockTransactor).Err = errExpected
202-
handler.Handle(requestContext, clientContext)
203-
s, ok = requestContext.Error.(*status.Status)
204-
require.False(t, ok)
205-
require.EqualError(t, requestContext.Error, errExpected.Error())
183+
require.NoError(t, requestContext.Error)
184+
})
185+
186+
t.Run("calls opts provider", func(t *testing.T) {
187+
clientContext := setupChannelClientContext(nil, nil, nil, t)
188+
requestContext := prepareRequestContext(request, Opts{Targets: []fab.Peer{fcmocks.NewMockPeer("p2", "")}}, t)
189+
optsProviderCalled := false
190+
optsProvider := func() []fab.TxnHeaderOpt {
191+
optsProviderCalled = true
192+
return []fab.TxnHeaderOpt{
193+
fab.WithCreator([]byte("somecreator")),
194+
fab.WithNonce([]byte("somenonce")),
195+
}
196+
}
197+
handler := NewEndorsementHandlerWithOpts(nil, optsProvider)
198+
199+
handler.Handle(requestContext, clientContext)
200+
require.NoError(t, requestContext.Error)
201+
202+
require.True(t, optsProviderCalled, "expecting opts provider to be called")
203+
})
204+
205+
t.Run("returns EndorserServerStatus error from Transactor", func(t *testing.T) {
206+
clientContext := setupChannelClientContext(nil, nil, nil, t)
207+
requestContext := prepareRequestContext(request, Opts{Targets: []fab.Peer{fcmocks.NewMockPeer("p2", "")}}, t)
208+
handler := NewEndorsementHandler()
209+
clientContext.Transactor.(*txnmocks.MockTransactor).Err = fmt.Errorf("error in simulation: failed to distribute private collection, txID 695560b")
210+
211+
handler.Handle(requestContext, clientContext)
212+
213+
s, ok := requestContext.Error.(*status.Status)
214+
require.True(t, ok)
215+
require.Equal(t, status.EndorserServerStatus, s.Group)
216+
require.Equal(t, status.PvtDataDisseminationFailed.ToInt32(), s.Code)
217+
})
218+
219+
t.Run("returns error from simulation", func(t *testing.T) {
220+
clientContext := setupChannelClientContext(nil, nil, nil, t)
221+
errExpected := fmt.Errorf("error in simulation")
222+
requestContext := prepareRequestContext(request, Opts{Targets: []fab.Peer{fcmocks.NewMockPeer("p2", "")}}, t)
223+
handler := NewEndorsementHandler()
224+
clientContext.Transactor.(*txnmocks.MockTransactor).Err = errExpected
225+
226+
handler.Handle(requestContext, clientContext)
227+
228+
_, ok := requestContext.Error.(*status.Status)
229+
require.False(t, ok)
230+
require.EqualError(t, requestContext.Error, errExpected.Error())
231+
})
232+
233+
t.Run("returns error deserializing proposal response payload", func(t *testing.T) {
234+
clientContext := setupChannelClientContext(nil, nil, nil, t)
235+
mockPeer := fcmocks.NewMockPeer("p2", "")
236+
mockPeer.ProposalResponsePayload = []byte("invalid serialized protobuf message")
237+
requestContext := prepareRequestContext(request, Opts{Targets: []fab.Peer{mockPeer}}, t)
238+
handler := NewEndorsementHandler()
239+
240+
handler.Handle(requestContext, clientContext)
241+
242+
require.Error(t, requestContext.Error)
243+
require.Contains(t, requestContext.Error.Error(), "failed to deserialize proposal response payload")
244+
})
245+
246+
t.Run("returns error deserializing chaincode action", func(t *testing.T) {
247+
proposalResponsePayload := &pb.ProposalResponsePayload{
248+
Extension: []byte("invalid serialized protobuf message"),
249+
}
250+
proposalResponsePayloadBytes, err := proto.Marshal(proposalResponsePayload)
251+
require.NoError(t, err)
252+
253+
clientContext := setupChannelClientContext(nil, nil, nil, t)
254+
mockPeer := fcmocks.NewMockPeer("p2", "")
255+
mockPeer.ProposalResponsePayload = proposalResponsePayloadBytes
256+
requestContext := prepareRequestContext(request, Opts{Targets: []fab.Peer{mockPeer}}, t)
257+
handler := NewEndorsementHandler()
258+
259+
handler.Handle(requestContext, clientContext)
260+
261+
require.Error(t, requestContext.Error)
262+
require.Contains(t, requestContext.Error.Error(), "failed to deserialize chaincode action")
263+
})
206264
}
207265

208266
// Target filter

pkg/fab/mocks/mockpeer.go

Lines changed: 63 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -21,21 +21,22 @@ import (
2121

2222
// MockPeer is a mock fabricsdk.Peer.
2323
type MockPeer struct {
24-
RWLock *sync.RWMutex
25-
Error error
26-
MockName string
27-
MockURL string
28-
MockRoles []string
29-
MockCert *pem.Block
30-
Payload []byte
31-
ResponseMessage string
32-
MockMSP string
33-
Status int32
34-
ProcessProposalCalls int
35-
Endorser []byte
36-
ChaincodeID string
37-
RwSets []*rwsetutil.NsRwSet
38-
properties fab.Properties
24+
RWLock *sync.RWMutex
25+
Error error
26+
MockName string
27+
MockURL string
28+
MockRoles []string
29+
MockCert *pem.Block
30+
Payload []byte
31+
ResponseMessage string
32+
ProposalResponsePayload []byte // Overrides proposal response payload generated from other values
33+
MockMSP string
34+
Status int32
35+
ProcessProposalCalls int
36+
Endorser []byte
37+
ChaincodeID string
38+
RwSets []*rwsetutil.NsRwSet
39+
properties fab.Properties
3940
}
4041

4142
// NewMockPeer creates basic mock peer
@@ -146,43 +147,61 @@ func (p *MockPeer) getProposalResponsePayload() []byte {
146147
p.ChaincodeID = p.RwSets[0].NameSpace
147148
}
148149

149-
var err error
150-
var resultBytes []byte
151-
if len(p.RwSets) > 0 {
152-
txRWSet := &rwsetutil.TxRwSet{
153-
NsRwSets: p.RwSets,
154-
}
155-
resultBytes, err = txRWSet.ToProtoBytes()
156-
if err != nil {
157-
panic(err)
158-
}
150+
if len(p.ProposalResponsePayload) > 0 {
151+
return p.ProposalResponsePayload
159152
}
160153

161-
var chaincodeActionBytes []byte
162-
if p.ChaincodeID != "" {
163-
chaincodeAction := &pb.ChaincodeAction{
164-
ChaincodeId: &pb.ChaincodeID{Name: p.ChaincodeID},
165-
Events: nil,
166-
Response: &pb.Response{
167-
Message: p.ResponseMessage,
168-
Status: p.Status,
169-
Payload: p.Payload,
170-
},
171-
Results: resultBytes,
172-
}
173-
chaincodeActionBytes, err = proto.Marshal(chaincodeAction)
174-
if err != nil {
175-
panic(err)
176-
}
154+
payload := p.newProposalResponsePayload()
155+
payloadBytes, err := proto.Marshal(payload)
156+
if err != nil {
157+
panic(err)
158+
}
159+
160+
return payloadBytes
161+
}
162+
163+
func (p *MockPeer) newProposalResponsePayload() *pb.ProposalResponsePayload {
164+
chaincodeAction := p.newChaincodeAction()
165+
chaincodeActionBytes, err := proto.Marshal(chaincodeAction)
166+
if err != nil {
167+
panic(err)
177168
}
178169

179-
prp := &pb.ProposalResponsePayload{
170+
return &pb.ProposalResponsePayload{
180171
Extension: chaincodeActionBytes,
181172
}
182-
payloadBytes, err := proto.Marshal(prp)
173+
}
174+
175+
func (p *MockPeer) newChaincodeAction() *pb.ChaincodeAction {
176+
chaincodeAction := &pb.ChaincodeAction{
177+
Events: nil,
178+
Response: &pb.Response{
179+
Message: p.ResponseMessage,
180+
Status: p.Status,
181+
Payload: p.Payload,
182+
},
183+
Results: p.getRWSet(),
184+
}
185+
186+
if p.ChaincodeID != "" {
187+
chaincodeAction.ChaincodeId = &pb.ChaincodeID{Name: p.ChaincodeID}
188+
}
189+
190+
return chaincodeAction
191+
}
192+
193+
func (p *MockPeer) getRWSet() []byte {
194+
if len(p.RwSets) == 0 {
195+
return nil
196+
}
197+
198+
txRWSet := &rwsetutil.TxRwSet{
199+
NsRwSets: p.RwSets,
200+
}
201+
txRWSetBytes, err := txRWSet.ToProtoBytes()
183202
if err != nil {
184203
panic(err)
185204
}
186205

187-
return payloadBytes
206+
return txRWSetBytes
188207
}

pkg/fab/mocks/mocktransactor.go

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
reqContex "context"
1111
"net/http"
1212

13+
"github.com/golang/protobuf/proto"
1314
pb "github.com/hyperledger/fabric-protos-go/peer"
1415
"github.com/hyperledger/fabric-sdk-go/pkg/common/providers/fab"
1516
)
@@ -29,9 +30,13 @@ func (t *MockTransactor) CreateTransactionHeader(opts ...fab.TxnHeaderOpt) (fab.
2930
// SendTransactionProposal sends a TransactionProposal to the target peers.
3031
func (t *MockTransactor) SendTransactionProposal(proposal *fab.TransactionProposal, targets []fab.ProposalProcessor) ([]*fab.TransactionProposalResponse, error) {
3132
response := make([]*fab.TransactionProposalResponse, 1)
33+
txResponse := &pb.Response{Message: "success", Payload: []byte("abc"), Status: http.StatusOK}
3234
response[0] = &fab.TransactionProposalResponse{Endorser: "example.com", Status: 200,
33-
ProposalResponse: &pb.ProposalResponse{Response: &pb.Response{Message: "success", Payload: []byte("abc"), Status: http.StatusOK},
34-
Endorsement: &pb.Endorsement{Endorser: []byte("example.com"), Signature: []byte("signature")}},
35+
ProposalResponse: &pb.ProposalResponse{
36+
Response: txResponse,
37+
Payload: getProposalResponsePayloadBytes(txResponse),
38+
Endorsement: &pb.Endorsement{Endorser: []byte("example.com"), Signature: []byte("signature")},
39+
},
3540
}
3641
return response, nil
3742
}
@@ -54,3 +59,23 @@ func (t *MockTransactor) SendTransaction(tx *fab.Transaction) (*fab.TransactionR
5459
}
5560
return response, nil
5661
}
62+
63+
func getProposalResponsePayloadBytes(response *pb.Response) []byte {
64+
chaincodeAction := &pb.ChaincodeAction{
65+
Response: response,
66+
}
67+
chaincodeActionBytes, err := proto.Marshal(chaincodeAction)
68+
if err != nil {
69+
panic(err)
70+
}
71+
72+
prp := &pb.ProposalResponsePayload{
73+
Extension: chaincodeActionBytes,
74+
}
75+
payloadBytes, err := proto.Marshal(prp)
76+
if err != nil {
77+
panic(err)
78+
}
79+
80+
return payloadBytes
81+
}

0 commit comments

Comments
 (0)