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

Commit b8f2be7

Browse files
committed
[FAB-8039] Split Endorsement Handler
Split the endorsement handler into two handlers: - Proposal Processor Handler - Endorsement Handler This allows for more granular reuse/replacement of handlers. Change-Id: I122ef44fdfb5b7a1de3c62a646f06cd8244269f6 Signed-off-by: Bob Stasyszyn <bob.stasyszyn@securekey.com>
1 parent 913ef17 commit b8f2be7

File tree

8 files changed

+212
-58
lines changed

8 files changed

+212
-58
lines changed

api/apitxn/chclient/chclient.go

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -40,12 +40,6 @@ type Opts struct {
4040
//Option func for each Opts argument
4141
type Option func(opts *Opts) error
4242

43-
// TxProposalResponseFilter allows the user to inspect/modify response before commit
44-
type TxProposalResponseFilter interface {
45-
// process transaction proposal response (there will be no commit if an error is returned)
46-
ProcessTxProposalResponse(txProposalResponse []*apifabclient.TransactionProposalResponse) ([]*apifabclient.TransactionProposalResponse, error)
47-
}
48-
4943
// Registration is a handle that is returned from a successful Register Chaincode Event.
5044
// This handle should be used in Unregister in order to unregister the event.
5145
type Registration interface {

pkg/fabric-client/channel/channel.go

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,6 @@ type Channel struct {
3535
// is enforced by the ordering service and must be unique within the blockchain network.
3636
// client: Provides operational context such as submitting User etc.
3737
func New(ctx fab.Context, cfg fab.ChannelCfg) (*Channel, error) {
38-
if cfg.Name() == "" {
39-
return nil, errors.Errorf("name is required")
40-
}
4138
if ctx == nil {
4239
return nil, errors.Errorf("client is required")
4340
}

pkg/fabric-client/channel/channel_test.go

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -45,11 +45,8 @@ func TestChannelMethods(t *testing.T) {
4545
}
4646

4747
_, err = New(ctx, mocks.NewMockChannelCfg(""))
48-
if err == nil {
49-
t.Fatalf("New didn't return error")
50-
}
51-
if err.Error() != "name is required" {
52-
t.Fatalf("New didn't return right error")
48+
if err != nil {
49+
t.Fatalf("Got error creating channel with empty channel ID: %s", err)
5350
}
5451

5552
_, err = New(nil, mocks.NewMockChannelCfg("testChannel"))
@@ -178,9 +175,13 @@ func isValueInList(value string, list []string) bool {
178175
}
179176

180177
func setupTestChannel() (*Channel, error) {
178+
return setupChannel("testChannel")
179+
}
180+
181+
func setupChannel(channelID string) (*Channel, error) {
181182
user := mocks.NewMockUser("test")
182183
ctx := mocks.NewMockContext(user)
183-
return New(ctx, mocks.NewMockChannelCfg("testChannel"))
184+
return New(ctx, mocks.NewMockChannelCfg(channelID))
184185
}
185186

186187
func setupMassiveTestChannel(numberOfPeers int, numberOfOrderers int) (*Channel, error) {

pkg/fabric-client/channel/query_test.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,24 @@ func TestQueryMethods(t *testing.T) {
5656

5757
}
5858

59+
func TestQueryOnSystemChannel(t *testing.T) {
60+
channel, _ := setupChannel(systemChannel)
61+
peer := mocks.MockPeer{MockName: "Peer1", MockURL: "http://peer1.com", MockRoles: []string{}, MockCert: nil}
62+
err := channel.AddPeer(&peer)
63+
if err != nil {
64+
t.Fatalf("Error adding peer to channel: %s", err)
65+
}
66+
67+
request := fab.ChaincodeInvokeRequest{
68+
ChaincodeID: "ccID",
69+
Fcn: "method",
70+
Args: [][]byte{[]byte("arg")},
71+
}
72+
if _, err := channel.QueryByChaincode(request); err != nil {
73+
t.Fatalf("Error invoking chaincode on system channel: %s", err)
74+
}
75+
}
76+
5977
func TestChannelQueryBlock(t *testing.T) {
6078

6179
channel, _ := setupTestChannel()

pkg/fabric-txn/chclient/chclient_test.go

Lines changed: 65 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,9 @@ import (
2020
"github.com/hyperledger/fabric-sdk-go/pkg/fabric-client/channel"
2121
fcmocks "github.com/hyperledger/fabric-sdk-go/pkg/fabric-client/mocks"
2222
"github.com/hyperledger/fabric-sdk-go/pkg/fabric-client/peer"
23+
"github.com/hyperledger/fabric-sdk-go/pkg/fabric-txn/internal"
2324
txnmocks "github.com/hyperledger/fabric-sdk-go/pkg/fabric-txn/mocks"
25+
"github.com/hyperledger/fabric-sdk-go/pkg/fabric-txn/txnhandler"
2426
pb "github.com/hyperledger/fabric-sdk-go/third_party/github.com/hyperledger/fabric/protos/peer"
2527
"github.com/pkg/errors"
2628
)
@@ -211,6 +213,64 @@ func TestInvokeHandler(t *testing.T) {
211213
}
212214
}
213215

216+
// customEndorsementHandler ignores the channel in the ClientContext
217+
// and instead sends the proposal to the given channel
218+
type customEndorsementHandler struct {
219+
channel apifabclient.Channel
220+
next chclient.Handler
221+
}
222+
223+
func (h *customEndorsementHandler) Handle(requestContext *chclient.RequestContext, clientContext *chclient.ClientContext) {
224+
transactionProposalResponses, txnID, err := internal.CreateAndSendTransactionProposal(h.channel,
225+
requestContext.Request.ChaincodeID, requestContext.Request.Fcn, requestContext.Request.Args, requestContext.Opts.ProposalProcessors, requestContext.Request.TransientMap)
226+
227+
requestContext.Response.TransactionID = txnID
228+
229+
if err != nil {
230+
requestContext.Error = err
231+
return
232+
}
233+
234+
requestContext.Response.Responses = transactionProposalResponses
235+
if len(transactionProposalResponses) > 0 {
236+
requestContext.Response.Payload = transactionProposalResponses[0].ProposalResponse.GetResponse().Payload
237+
}
238+
239+
//Delegate to next step if any
240+
if h.next != nil {
241+
h.next.Handle(requestContext, clientContext)
242+
}
243+
}
244+
245+
func TestQueryWithCustomEndorser(t *testing.T) {
246+
chClient := setupChannelClient(nil, t)
247+
248+
// Use the customEndorsementHandler to send the proposal to
249+
// the system channel instead of the channel in context
250+
251+
systemChannel, err := setupChannel("")
252+
if err != nil {
253+
t.Fatalf("Error getting system channel: %s", err)
254+
}
255+
256+
response, err := chClient.InvokeHandler(
257+
txnhandler.NewProposalProcessorHandler(
258+
&customEndorsementHandler{
259+
channel: systemChannel,
260+
next: txnhandler.NewEndorsementValidationHandler(),
261+
},
262+
),
263+
chclient.Request{ChaincodeID: "testCC", Fcn: "invoke", Args: [][]byte{[]byte("query"), []byte("b")}},
264+
)
265+
if err != nil {
266+
t.Fatalf("Failed to invoke test cc: %s", err)
267+
}
268+
269+
if response.Payload != nil {
270+
t.Fatalf("Expecting nil, got %s", response.Payload)
271+
}
272+
}
273+
214274
func TestExecuteTxDiscoveryError(t *testing.T) {
215275
chClient := setupChannelClientWithError(errors.New("Test Error"), nil, nil, t)
216276

@@ -326,8 +386,12 @@ func TestExecuteTxWithRetries(t *testing.T) {
326386
}
327387

328388
func setupTestChannel() (*channel.Channel, error) {
389+
return setupChannel("testChannel")
390+
}
391+
392+
func setupChannel(channelID string) (*channel.Channel, error) {
329393
ctx := setupTestContext()
330-
return channel.New(ctx, fcmocks.NewMockChannelCfg("testChannel"))
394+
return channel.New(ctx, fcmocks.NewMockChannelCfg(channelID))
331395
}
332396

333397
func setupTestContext() apifabclient.Context {

pkg/fabric-txn/txnhandler/txnhandler.go

Lines changed: 53 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -20,18 +20,45 @@ import (
2020
"github.com/pkg/errors"
2121
)
2222

23-
//EndorseTxHandler for handling endorse transactions
24-
type EndorseTxHandler struct {
23+
//EndorsementHandler for handling endorse transactions
24+
type EndorsementHandler struct {
2525
next chclient.Handler
2626
}
2727

2828
//Handle for endorsing transactions
29-
func (e *EndorseTxHandler) Handle(requestContext *chclient.RequestContext, clientContext *chclient.ClientContext) {
29+
func (e *EndorsementHandler) Handle(requestContext *chclient.RequestContext, clientContext *chclient.ClientContext) {
30+
// Endorse Tx
31+
transactionProposalResponses, txnID, err := internal.CreateAndSendTransactionProposal(clientContext.Channel,
32+
requestContext.Request.ChaincodeID, requestContext.Request.Fcn, requestContext.Request.Args, requestContext.Opts.ProposalProcessors, requestContext.Request.TransientMap)
33+
34+
requestContext.Response.TransactionID = txnID
35+
36+
if err != nil {
37+
requestContext.Error = err
38+
return
39+
}
40+
41+
requestContext.Response.Responses = transactionProposalResponses
42+
if len(transactionProposalResponses) > 0 {
43+
requestContext.Response.Payload = transactionProposalResponses[0].ProposalResponse.GetResponse().Payload
44+
}
45+
46+
//Delegate to next step if any
47+
if e.next != nil {
48+
e.next.Handle(requestContext, clientContext)
49+
}
50+
}
51+
52+
//ProposalProcessorHandler for selecting proposal processors
53+
type ProposalProcessorHandler struct {
54+
next chclient.Handler
55+
}
3056

57+
//Handle selects proposal processors
58+
func (h *ProposalProcessorHandler) Handle(requestContext *chclient.RequestContext, clientContext *chclient.ClientContext) {
3159
//Get proposal processor, if not supplied then use discovery service to get available peers as endorser
3260
//If selection service available then get endorser peers for this chaincode
33-
txProcessors := requestContext.Opts.ProposalProcessors
34-
if len(txProcessors) == 0 {
61+
if len(requestContext.Opts.ProposalProcessors) == 0 {
3562
// Use discovery service to figure out proposal processors
3663
peers, err := clientContext.Discovery.GetPeers()
3764
if err != nil {
@@ -46,28 +73,12 @@ func (e *EndorseTxHandler) Handle(requestContext *chclient.RequestContext, clien
4673
return
4774
}
4875
}
49-
txProcessors = peer.PeersToTxnProcessors(endorsers)
50-
}
51-
52-
// Endorse Tx
53-
transactionProposalResponses, txnID, err := internal.CreateAndSendTransactionProposal(clientContext.Channel,
54-
requestContext.Request.ChaincodeID, requestContext.Request.Fcn, requestContext.Request.Args, txProcessors, requestContext.Request.TransientMap)
55-
56-
requestContext.Response.TransactionID = txnID
57-
58-
if err != nil {
59-
requestContext.Error = err
60-
return
61-
}
62-
63-
requestContext.Response.Responses = transactionProposalResponses
64-
if len(transactionProposalResponses) > 0 {
65-
requestContext.Response.Payload = transactionProposalResponses[0].ProposalResponse.GetResponse().Payload
76+
requestContext.Opts.ProposalProcessors = peer.PeersToTxnProcessors(endorsers)
6677
}
6778

6879
//Delegate to next step if any
69-
if e.next != nil {
70-
e.next.Handle(requestContext, clientContext)
80+
if h.next != nil {
81+
h.next.Handle(requestContext, clientContext)
7182
}
7283
}
7384

@@ -160,17 +171,30 @@ func (c *CommitTxHandler) Handle(requestContext *chclient.RequestContext, client
160171

161172
//NewQueryHandler returns query handler with EndorseTxHandler & EndorsementValidationHandler Chained
162173
func NewQueryHandler(next ...chclient.Handler) chclient.Handler {
163-
return NewEndorseHandler(NewEndorsementValidationHandler(next...))
174+
return NewProposalProcessorHandler(
175+
NewEndorsementHandler(
176+
NewEndorsementValidationHandler(next...),
177+
),
178+
)
164179
}
165180

166181
//NewExecuteHandler returns query handler with EndorseTxHandler, EndorsementValidationHandler & CommitTxHandler Chained
167182
func NewExecuteHandler(next ...chclient.Handler) chclient.Handler {
168-
return NewEndorseHandler(NewEndorsementValidationHandler(NewCommitHandler(next...)))
183+
return NewProposalProcessorHandler(
184+
NewEndorsementHandler(
185+
NewEndorsementValidationHandler(NewCommitHandler(next...)),
186+
),
187+
)
188+
}
189+
190+
//NewProposalProcessorHandler returns a handler that selects proposal processors
191+
func NewProposalProcessorHandler(next ...chclient.Handler) *ProposalProcessorHandler {
192+
return &ProposalProcessorHandler{next: getNext(next)}
169193
}
170194

171-
//NewEndorseHandler returns a handler that endorses a transaction proposal
172-
func NewEndorseHandler(next ...chclient.Handler) *EndorseTxHandler {
173-
return &EndorseTxHandler{next: getNext(next)}
195+
//NewEndorsementHandler returns a handler that endorses a transaction proposal
196+
func NewEndorsementHandler(next ...chclient.Handler) *EndorsementHandler {
197+
return &EndorsementHandler{next: getNext(next)}
174198
}
175199

176200
//NewEndorsementValidationHandler returns a handler that validates an endorsement

pkg/fabric-txn/txnhandler/txnhandler_test.go

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,60 @@ func TestQueryHandlerErrors(t *testing.T) {
104104
}
105105
}
106106

107+
func TestEndorsementHandler(t *testing.T) {
108+
request := chclient.Request{ChaincodeID: "test", Fcn: "invoke", Args: [][]byte{[]byte("move"), []byte("a"), []byte("b"), []byte("1")}}
109+
110+
requestContext := prepareRequestContext(request, chclient.Opts{ProposalProcessors: []apifabclient.ProposalProcessor{fcmocks.NewMockPeer("p2", "")}}, t)
111+
clientContext := setupChannelClientContext(nil, nil, nil, t)
112+
113+
handler := NewEndorsementHandler()
114+
handler.Handle(requestContext, clientContext)
115+
assert.Nil(t, requestContext.Error)
116+
}
117+
118+
func TestProposalProcessorHandler(t *testing.T) {
119+
peer1 := fcmocks.NewMockPeer("p1", "")
120+
peer2 := fcmocks.NewMockPeer("p2", "")
121+
discoveryPeers := []apifabclient.Peer{peer1, peer2}
122+
123+
//Get query handler
124+
handler := NewProposalProcessorHandler()
125+
126+
request := chclient.Request{ChaincodeID: "testCC", Fcn: "invoke", Args: [][]byte{[]byte("query"), []byte("b")}}
127+
128+
selectionErr := errors.New("Some selection error")
129+
requestContext := prepareRequestContext(request, chclient.Opts{}, t)
130+
handler.Handle(requestContext, setupChannelClientContext(nil, selectionErr, discoveryPeers, t))
131+
if requestContext.Error == nil || !strings.Contains(requestContext.Error.Error(), selectionErr.Error()) {
132+
t.Fatal("Expected error: ", selectionErr, ", Received error:", requestContext.Error)
133+
}
134+
135+
requestContext = prepareRequestContext(request, chclient.Opts{}, t)
136+
handler.Handle(requestContext, setupChannelClientContext(nil, nil, discoveryPeers, t))
137+
if requestContext.Error != nil {
138+
t.Fatalf("Got error: %s", requestContext.Error)
139+
}
140+
if len(requestContext.Opts.ProposalProcessors) != len(discoveryPeers) {
141+
t.Fatalf("Expecting %d proposal processors but got %d", len(discoveryPeers), len(requestContext.Opts.ProposalProcessors))
142+
}
143+
if requestContext.Opts.ProposalProcessors[0] != peer1 || requestContext.Opts.ProposalProcessors[1] != peer2 {
144+
t.Fatalf("Didn't get expected peers")
145+
}
146+
147+
// Directly pass in the proposal processors. In this case it should use those directly
148+
requestContext = prepareRequestContext(request, chclient.Opts{ProposalProcessors: []apifabclient.ProposalProcessor{peer2}}, t)
149+
handler.Handle(requestContext, setupChannelClientContext(nil, nil, discoveryPeers, t))
150+
if requestContext.Error != nil {
151+
t.Fatalf("Got error: %s", requestContext.Error)
152+
}
153+
if len(requestContext.Opts.ProposalProcessors) != 1 {
154+
t.Fatalf("Expecting 1 proposal processor but got %d", len(requestContext.Opts.ProposalProcessors))
155+
}
156+
if requestContext.Opts.ProposalProcessors[0] != peer2 {
157+
t.Fatalf("Didn't get expected peers")
158+
}
159+
}
160+
107161
//prepareHandlerContexts prepares context objects for handlers
108162
func prepareRequestContext(request chclient.Request, opts chclient.Opts, t *testing.T) *chclient.RequestContext {
109163

test/integration/sdk/channel_client_test.go

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -176,19 +176,21 @@ func testInvokeHandler(ccID string, chClient chclient.ChannelClient, t *testing.
176176
txValidationCode := pb.TxValidationCode(-1)
177177

178178
response, err := chClient.InvokeHandler(
179-
txnhandler.NewEndorseHandler(
180-
txnhandler.NewEndorsementValidationHandler(
181-
&testHandler{
182-
t: t,
183-
txID: &txID,
184-
endorser: &endorser,
185-
next: txnhandler.NewCommitHandler(
186-
&testHandler{
187-
t: t,
188-
txValidationCode: &txValidationCode,
189-
},
190-
),
191-
},
179+
txnhandler.NewProposalProcessorHandler(
180+
txnhandler.NewEndorsementHandler(
181+
txnhandler.NewEndorsementValidationHandler(
182+
&testHandler{
183+
t: t,
184+
txID: &txID,
185+
endorser: &endorser,
186+
next: txnhandler.NewCommitHandler(
187+
&testHandler{
188+
t: t,
189+
txValidationCode: &txValidationCode,
190+
},
191+
),
192+
},
193+
),
192194
),
193195
),
194196
chclient.Request{

0 commit comments

Comments
 (0)