Skip to content

Commit 4b5b4d1

Browse files
committed
introduce query policy
Signed-off-by: Angelo De Caro <[email protected]>
1 parent 2bf32e0 commit 4b5b4d1

File tree

7 files changed

+136
-22
lines changed

7 files changed

+136
-22
lines changed

Diff for: integration/fabric/atsa/chaincode/views/assets.go

+7-3
Original file line numberDiff line numberDiff line change
@@ -60,9 +60,13 @@ type ReadAssetView struct {
6060

6161
func (r *ReadAssetView) Call(context view.Context) (interface{}, error) {
6262
res, err := context.RunView(
63-
chaincode.NewQueryView(
64-
"asset_transfer", "ReadAsset", r.ID,
65-
).WithEndorsersFromMyOrg().WithNumRetries(2).WithRetrySleep(2 * time.Second).WithMatchEndorsementPolicy())
63+
chaincode.NewQueryView("asset_transfer", "ReadAsset", r.ID).
64+
WithEndorsersFromMyOrg().
65+
WithNumRetries(2).
66+
WithRetrySleep(2 * time.Second).
67+
WithMatchEndorsementPolicy().
68+
WithQueryPolicy(chaincode.QueryOne),
69+
)
6670
assert.NoError(err, "failed reading asset")
6771
return res, nil
6872
}

Diff for: platform/fabric/chaincode.go

+18
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,18 @@ import (
1515
"github.com/hyperledger-labs/fabric-smart-client/platform/view/view"
1616
)
1717

18+
// QueryPolicy defines the policy to use to decide if a query is successful
19+
type QueryPolicy int
20+
21+
const (
22+
// QueryAll requires an answer from all selected peers
23+
QueryAll QueryPolicy = iota
24+
// QueryMajority requires an answer from the majority of the selected peers
25+
QueryMajority
26+
// QueryOne requires an answer from at least one of the selected peers
27+
QueryOne
28+
)
29+
1830
type Envelope struct {
1931
e driver.Envelope
2032
}
@@ -249,6 +261,12 @@ func (i *ChaincodeQuery) WithRetrySleep(duration time.Duration) *ChaincodeQuery
249261
return i
250262
}
251263

264+
// WithQueryPolicy sets the query policy to use
265+
func (i *ChaincodeQuery) WithQueryPolicy(policy QueryPolicy) *ChaincodeQuery {
266+
i.ChaincodeInvocation.WithQueryPolicy(driver.QueryPolicy(policy))
267+
return i
268+
}
269+
252270
type ChaincodeEndorse struct {
253271
ChaincodeInvocation driver.ChaincodeInvocation
254272
}

Diff for: platform/fabric/core/generic/chaincode/chaincode.go

+3-1
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,9 @@ func NewChaincode(
8888
}
8989

9090
func (c *Chaincode) NewInvocation(function string, args ...interface{}) driver.ChaincodeInvocation {
91-
return NewInvoke(c, function, args...)
91+
return NewInvoke(c, func(chaincode *Chaincode) driver.ChaincodeDiscover {
92+
return NewDiscovery(chaincode)
93+
}, function, args...)
9294
}
9395

9496
func (c *Chaincode) NewDiscover() driver.ChaincodeDiscover {

Diff for: platform/fabric/core/generic/chaincode/invoke.go

+63-17
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"bytes"
1111
"context"
1212
"encoding/base64"
13+
"fmt"
1314
"strconv"
1415
"strings"
1516
"sync"
@@ -27,6 +28,8 @@ import (
2728
"go.uber.org/zap/zapcore"
2829
)
2930

31+
type NewChaincodeDiscoverFunc = func(chaincode *Chaincode) driver.ChaincodeDiscover
32+
3033
type Invoke struct {
3134
Chaincode *Chaincode
3235
TxID driver.TxID
@@ -46,16 +49,20 @@ type Invoke struct {
4649
NumRetries int
4750
RetrySleep time.Duration
4851
Context context.Context
52+
QueryPolicy driver.QueryPolicy
53+
NewChaincodeDiscover NewChaincodeDiscoverFunc
4954
}
5055

51-
func NewInvoke(chaincode *Chaincode, function string, args ...interface{}) *Invoke {
56+
func NewInvoke(chaincode *Chaincode, newChaincodeDiscover NewChaincodeDiscoverFunc, function string, args ...interface{}) *Invoke {
5257
return &Invoke{
53-
Chaincode: chaincode,
54-
ChaincodeName: chaincode.name,
55-
Function: function,
56-
Args: args,
57-
NumRetries: int(chaincode.NumRetries),
58-
RetrySleep: chaincode.RetrySleep,
58+
Chaincode: chaincode,
59+
60+
ChaincodeName: chaincode.name,
61+
Function: function,
62+
Args: args,
63+
NumRetries: int(chaincode.NumRetries),
64+
RetrySleep: chaincode.RetrySleep,
65+
NewChaincodeDiscover: newChaincodeDiscover,
5966
}
6067
}
6168

@@ -257,6 +264,11 @@ func (i *Invoke) WithRetrySleep(duration time.Duration) driver.ChaincodeInvocati
257264
return i
258265
}
259266

267+
func (i *Invoke) WithQueryPolicy(policy driver.QueryPolicy) driver.ChaincodeInvocation {
268+
i.QueryPolicy = policy
269+
return i
270+
}
271+
260272
func (i *Invoke) prepare(query bool) (string, *pb.Proposal, []*pb.ProposalResponse, driver.SigningIdentity, error) {
261273
var peerClients []services.PeerClient
262274
defer func() {
@@ -297,7 +309,7 @@ func (i *Invoke) prepare(query bool) (string, *pb.Proposal, []*pb.ProposalRespon
297309

298310
// discover
299311
var err error
300-
discovery := NewDiscovery(
312+
discovery := i.NewChaincodeDiscover(
301313
i.Chaincode,
302314
)
303315
discovery.WithFilterByMSPIDs(
@@ -327,7 +339,9 @@ func (i *Invoke) prepare(query bool) (string, *pb.Proposal, []*pb.ProposalRespon
327339
}
328340
}
329341

330-
// get a peer client for all discovered peers
342+
n := len(discoveredPeers)
343+
// get a peer client for all discovered peers and collect the errors
344+
var errs []error
331345
for _, peer := range discoveredPeers {
332346
peerClient, err := i.Chaincode.Services.NewPeerClient(grpc.ConnectionConfig{
333347
Address: peer.Endpoint,
@@ -336,19 +350,28 @@ func (i *Invoke) prepare(query bool) (string, *pb.Proposal, []*pb.ProposalRespon
336350
TLSRootCertBytes: peer.TLSRootCerts,
337351
})
338352
if err != nil {
339-
return "", nil, nil, nil, errors.WithMessagef(err, "error getting endorser client for %s", peer.Endpoint)
353+
errs = append(errs, errors.WithMessagef(err, "error getting endorser client for %s", peer.Endpoint))
354+
continue
340355
}
341356
peerClients = append(peerClients, peerClient)
342357
}
358+
if err := i.checkQueryPolicy(errs, len(peerClients), n); err != nil {
359+
return "", nil, nil, nil, errors.WithMessagef(err, "cannot match query policy with the given discovered peers")
360+
}
343361

344362
// get endorser clients
363+
errs = nil
345364
for _, client := range peerClients {
346365
endorserClient, err := client.EndorserClient()
347366
if err != nil {
348-
return "", nil, nil, nil, errors.WithMessagef(err, "error getting endorser client for %s", client.Address())
367+
errs = append(errs, errors.WithMessagef(err, "error getting endorser client for %s", client.Address()))
368+
continue
349369
}
350370
endorserClients = append(endorserClients, endorserClient)
351371
}
372+
if err := i.checkQueryPolicy(errs, len(endorserClients), n); err != nil {
373+
return "", nil, nil, nil, errors.WithMessagef(err, "cannot match query policy with the given peer clients")
374+
}
352375
if len(endorserClients) == 0 {
353376
return "", nil, nil, nil, errors.New("no endorser clients retrieved with the current filters")
354377
}
@@ -366,15 +389,17 @@ func (i *Invoke) prepare(query bool) (string, *pb.Proposal, []*pb.ProposalRespon
366389
}
367390

368391
// collect responses
369-
responses, err := i.collectResponses(endorserClients, signedProp)
392+
responses, errs := i.collectResponses(endorserClients, signedProp)
370393
if err != nil {
371394
return "", nil, nil, nil, errors.Wrapf(err, "failed collecting proposal responses")
372395
}
373-
374396
if len(responses) == 0 {
375397
// this should only happen if some new code has introduced a bug
376398
return "", nil, nil, nil, errors.New("no proposal responses received - this might indicate a bug")
377399
}
400+
if err := i.checkQueryPolicy(errs, len(responses), n); err != nil {
401+
return "", nil, nil, nil, errors.WithMessagef(err, "cannot match query policy with the given peer clients")
402+
}
378403

379404
return txID, prop, responses, signer, nil
380405
}
@@ -440,12 +465,12 @@ func (i *Invoke) createChaincodeProposalWithTxIDAndTransient(typ common.HeaderTy
440465
}
441466

442467
// collectResponses sends a signed proposal to a set of peers, and gathers all the responses.
443-
func (i *Invoke) collectResponses(endorserClients []pb.EndorserClient, signedProposal *pb.SignedProposal) ([]*pb.ProposalResponse, error) {
468+
func (i *Invoke) collectResponses(endorserClients []pb.EndorserClient, signedProposal *pb.SignedProposal) ([]*pb.ProposalResponse, []error) {
444469
responsesCh := make(chan *pb.ProposalResponse, len(endorserClients))
445470
errorCh := make(chan error, len(endorserClients))
446471
wg := sync.WaitGroup{}
472+
wg.Add(len(endorserClients))
447473
for _, endorser := range endorserClients {
448-
wg.Add(1)
449474
go func(endorser pb.EndorserClient) {
450475
defer wg.Done()
451476
proposalResp, err := endorser.ProcessProposal(context.Background(), signedProposal)
@@ -459,14 +484,15 @@ func (i *Invoke) collectResponses(endorserClients []pb.EndorserClient, signedPro
459484
wg.Wait()
460485
close(responsesCh)
461486
close(errorCh)
487+
var errs []error
462488
for err := range errorCh {
463-
return nil, err
489+
errs = append(errs, err)
464490
}
465491
var responses []*pb.ProposalResponse
466492
for response := range responsesCh {
467493
responses = append(responses, response)
468494
}
469-
return responses, nil
495+
return responses, errs
470496
}
471497

472498
// getChaincodeSpec get chaincode spec
@@ -529,3 +555,23 @@ func (i *Invoke) broadcast(txID string, env *common.Envelope) error {
529555
}
530556
return i.Chaincode.Finality.IsFinal(context.Background(), txID)
531557
}
558+
559+
func (i *Invoke) checkQueryPolicy(errs []error, successes int, n int) error {
560+
switch i.QueryPolicy {
561+
case driver.QueryAll:
562+
if len(errs) != 0 {
563+
return errors.Errorf("query all policy, no errors expected [%v]", errs)
564+
}
565+
case driver.QueryOne:
566+
if successes == 0 {
567+
return errors.Errorf("query one policy, errors occurred [%v]", errs)
568+
}
569+
case driver.QueryMajority:
570+
if successes <= n/2 {
571+
return errors.Errorf("query majority policy, no majority reached [%v]", errs)
572+
}
573+
default:
574+
panic(fmt.Sprintf("programming error, policy [%d] is not valid", i.QueryPolicy))
575+
}
576+
return nil
577+
}

Diff for: platform/fabric/driver/chaincode.go

+16
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,18 @@ type TxID struct {
1919
Creator []byte
2020
}
2121

22+
// QueryPolicy defines the policy to use to decide if a query is successful
23+
type QueryPolicy int
24+
25+
const (
26+
// QueryAll requires an answer from all selected peers
27+
QueryAll QueryPolicy = iota
28+
// QueryMajority requires an answer from the majority of the selected peers
29+
QueryMajority
30+
// QueryOne requires an answer from at least one of the selected peers
31+
QueryOne
32+
)
33+
2234
// ChaincodeInvocation models a client-side chaincode invocation
2335
type ChaincodeInvocation interface {
2436
Endorse() (Envelope, error)
@@ -56,6 +68,8 @@ type ChaincodeInvocation interface {
5668
WithRetrySleep(duration time.Duration) ChaincodeInvocation
5769

5870
WithContext(context context.Context) ChaincodeInvocation
71+
72+
WithQueryPolicy(policy QueryPolicy) ChaincodeInvocation
5973
}
6074

6175
// DiscoveredPeer contains the information of a discovered peer
@@ -76,6 +90,8 @@ type ChaincodeDiscover interface {
7690
Call() ([]DiscoveredPeer, error)
7791
WithFilterByMSPIDs(mspIDs ...string) ChaincodeDiscover
7892
WithImplicitCollections(mspIDs ...string) ChaincodeDiscover
93+
WithForQuery() ChaincodeDiscover
94+
ChaincodeVersion() (string, error)
7995
}
8096

8197
// Chaincode exposes chaincode-related functions

Diff for: platform/fabric/services/chaincode/chaincode.go

+7
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"time"
1111

1212
"github.com/hyperledger-labs/fabric-smart-client/platform/fabric"
13+
"github.com/hyperledger-labs/fabric-smart-client/platform/fabric/driver"
1314
"github.com/hyperledger-labs/fabric-smart-client/platform/view/view"
1415
)
1516

@@ -33,6 +34,7 @@ type Query interface {
3334
Call() ([]byte, error)
3435
WithNumRetries(retries uint)
3536
WithRetrySleep(sleep time.Duration)
37+
WithQueryPolicy(policy driver.QueryPolicy) Query
3638
}
3739

3840
type Chaincode interface {
@@ -116,6 +118,11 @@ func (s *stdQuery) WithRetrySleep(duration time.Duration) {
116118
s.chq.WithRetrySleep(duration)
117119
}
118120

121+
func (s *stdQuery) WithQueryPolicy(policy driver.QueryPolicy) Query {
122+
s.chq.WithQueryPolicy(fabric.QueryPolicy(policy))
123+
return s
124+
}
125+
119126
type stdChaincode struct {
120127
ch *fabric.Chaincode
121128
}

Diff for: platform/fabric/services/chaincode/query.go

+22-1
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,26 @@ import (
1010
"time"
1111

1212
"github.com/hyperledger-labs/fabric-smart-client/platform/fabric"
13+
"github.com/hyperledger-labs/fabric-smart-client/platform/fabric/driver"
1314
"github.com/hyperledger-labs/fabric-smart-client/platform/view/view"
1415
"github.com/pkg/errors"
1516
)
1617

18+
// QueryPolicy defines the policy to use to decide if a query is successful
19+
type QueryPolicy = driver.QueryPolicy
20+
21+
const (
22+
// QueryAll requires an answer from all selected peers
23+
QueryAll = driver.QueryAll
24+
// QueryMajority requires an answer from the majority of the selected peers
25+
QueryMajority = driver.QueryMajority
26+
// QueryOne requires an answer from at least one of the selected peers
27+
QueryOne = driver.QueryOne
28+
)
29+
1730
type queryChaincodeView struct {
1831
*InvokeCall
32+
policy QueryPolicy
1933
}
2034

2135
func NewQueryView(chaincode, function string, args ...interface{}) *queryChaincodeView {
@@ -54,7 +68,9 @@ func (i *queryChaincodeView) Query(context view.Context) ([]byte, error) {
5468
chaincode = &stdChaincode{ch: stdChannelChaincode}
5569
logger.Debugf("chaincode [%s:%s:%s] is a standard chaincode", i.Network, i.Channel, i.ChaincodeName)
5670

57-
invocation := chaincode.Query(i.Function, i.Args...).WithInvokerIdentity(i.InvokerIdentity)
71+
invocation := chaincode.Query(i.Function, i.Args...).
72+
WithInvokerIdentity(i.InvokerIdentity).
73+
WithQueryPolicy(i.policy)
5874
for k, v := range i.TransientMap {
5975
invocation.WithTransientEntry(k, v)
6076
}
@@ -126,3 +142,8 @@ func (i *queryChaincodeView) WithRetrySleep(duration time.Duration) *queryChainc
126142
i.RetrySleep = duration
127143
return i
128144
}
145+
146+
func (i *queryChaincodeView) WithQueryPolicy(policy QueryPolicy) *queryChaincodeView {
147+
i.policy = policy
148+
return i
149+
}

0 commit comments

Comments
 (0)