github.com/yimialmonte/fabric@v2.1.1+incompatible/discovery/cmd/endorsers.go (about)

     1  /*
     2  Copyright IBM Corp. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package discovery
     8  
     9  import (
    10  	"encoding/json"
    11  	"fmt"
    12  	"io"
    13  	"reflect"
    14  	"strings"
    15  
    16  	"github.com/golang/protobuf/proto"
    17  	. "github.com/hyperledger/fabric-protos-go/discovery"
    18  	"github.com/hyperledger/fabric-protos-go/gossip"
    19  	"github.com/hyperledger/fabric-protos-go/msp"
    20  	"github.com/hyperledger/fabric/cmd/common"
    21  	discovery "github.com/hyperledger/fabric/discovery/client"
    22  	"github.com/hyperledger/fabric/gossip/protoext"
    23  	"github.com/pkg/errors"
    24  )
    25  
    26  // NewEndorsersCmd creates a new EndorsersCmd
    27  func NewEndorsersCmd(stub Stub, parser ResponseParser) *EndorsersCmd {
    28  	return &EndorsersCmd{
    29  		stub:   stub,
    30  		parser: parser,
    31  	}
    32  }
    33  
    34  // EndorsersCmd executes a command that retrieves endorsers for a chaincode invocation chain
    35  type EndorsersCmd struct {
    36  	stub        Stub
    37  	server      *string
    38  	channel     *string
    39  	chaincodes  *[]string
    40  	collections *map[string]string
    41  	noPrivReads *[]string
    42  	parser      ResponseParser
    43  }
    44  
    45  // SetCollections sets the collections to be the given collections
    46  func (pc *EndorsersCmd) SetCollections(collections *map[string]string) {
    47  	pc.collections = collections
    48  }
    49  
    50  // SetNoPrivateReads sets the collections that are expected not to have private reads
    51  func (pc *EndorsersCmd) SetNoPrivateReads(noPrivReads *[]string) {
    52  	pc.noPrivReads = noPrivReads
    53  }
    54  
    55  // SetChaincodes sets the chaincodes to be the given chaincodes
    56  func (pc *EndorsersCmd) SetChaincodes(chaincodes *[]string) {
    57  	pc.chaincodes = chaincodes
    58  }
    59  
    60  // SetServer sets the server
    61  func (pc *EndorsersCmd) SetServer(server *string) {
    62  	pc.server = server
    63  }
    64  
    65  // SetChannel sets the channel
    66  func (pc *EndorsersCmd) SetChannel(channel *string) {
    67  	pc.channel = channel
    68  }
    69  
    70  // Execute executes the command
    71  func (pc *EndorsersCmd) Execute(conf common.Config) error {
    72  	if pc.channel == nil || *pc.channel == "" {
    73  		return errors.New("no channel specified")
    74  	}
    75  
    76  	if pc.server == nil || *pc.server == "" {
    77  		return errors.New("no server specified")
    78  	}
    79  
    80  	server := *pc.server
    81  	channel := *pc.channel
    82  
    83  	ccAndCol := &chaincodesAndCollections{
    84  		Chaincodes:  pc.chaincodes,
    85  		Collections: pc.collections,
    86  		NoPrivReads: pc.noPrivReads,
    87  	}
    88  	cc2collections, err := ccAndCol.parseInput()
    89  	if err != nil {
    90  		return err
    91  	}
    92  
    93  	var ccCalls []*ChaincodeCall
    94  
    95  	for _, cc := range *ccAndCol.Chaincodes {
    96  		ccCalls = append(ccCalls, &ChaincodeCall{
    97  			Name:            cc,
    98  			CollectionNames: cc2collections[cc],
    99  			NoPrivateReads:  ccAndCol.noPrivateReads(cc),
   100  		})
   101  	}
   102  
   103  	req, err := discovery.NewRequest().OfChannel(channel).AddEndorsersQuery(&ChaincodeInterest{Chaincodes: ccCalls})
   104  	if err != nil {
   105  		return errors.Wrap(err, "failed creating request")
   106  	}
   107  
   108  	res, err := pc.stub.Send(server, conf, req)
   109  	if err != nil {
   110  		return err
   111  	}
   112  
   113  	return pc.parser.ParseResponse(channel, res)
   114  }
   115  
   116  // EndorserResponseParser parses endorsement responses from the peer
   117  type EndorserResponseParser struct {
   118  	io.Writer
   119  }
   120  
   121  // ParseResponse parses the given response for the given channel
   122  func (parser *EndorserResponseParser) ParseResponse(channel string, res ServiceResponse) error {
   123  	rawResponse := res.Raw()
   124  	if len(rawResponse.Results) == 0 {
   125  		return errors.New("empty results")
   126  	}
   127  
   128  	if e := rawResponse.Results[0].GetError(); e != nil {
   129  		return errors.Errorf("server returned: %s", e.Content)
   130  	}
   131  
   132  	ccQueryRes := rawResponse.Results[0].GetCcQueryRes()
   133  	if ccQueryRes == nil {
   134  		return errors.Errorf("server returned response of unexpected type: %v", reflect.TypeOf(rawResponse.Results[0]))
   135  	}
   136  
   137  	jsonBytes, _ := json.MarshalIndent(parseEndorsementDescriptors(ccQueryRes.Content), "", "\t")
   138  	fmt.Fprintln(parser.Writer, string(jsonBytes))
   139  	return nil
   140  }
   141  
   142  type chaincodesAndCollections struct {
   143  	Chaincodes  *[]string
   144  	Collections *map[string]string
   145  	NoPrivReads *[]string
   146  }
   147  
   148  func (ec *chaincodesAndCollections) noPrivateReads(chaincodeName string) bool {
   149  	for _, cc := range *ec.NoPrivReads {
   150  		if chaincodeName == cc {
   151  			return true
   152  		}
   153  	}
   154  	return false
   155  }
   156  
   157  func (ec *chaincodesAndCollections) existsInChaincodes(chaincodeName string) bool {
   158  	for _, cc := range *ec.Chaincodes {
   159  		if chaincodeName == cc {
   160  			return true
   161  		}
   162  	}
   163  	return false
   164  }
   165  
   166  func (ec *chaincodesAndCollections) parseInput() (map[string][]string, error) {
   167  	var emptyChaincodes []string
   168  	if ec.Chaincodes == nil {
   169  		ec.Chaincodes = &emptyChaincodes
   170  	}
   171  
   172  	if ec.NoPrivReads == nil {
   173  		ec.NoPrivReads = &emptyChaincodes
   174  	}
   175  
   176  	var emptyCollections map[string]string
   177  	if ec.Collections == nil {
   178  		ec.Collections = &emptyCollections
   179  	}
   180  
   181  	res := make(map[string][]string)
   182  
   183  	for _, cc := range *ec.Chaincodes {
   184  		res[cc] = nil
   185  	}
   186  
   187  	for _, cc := range *ec.NoPrivReads {
   188  		if !ec.existsInChaincodes(cc) {
   189  			return nil, errors.Errorf("chaincode %s is specified as not containing private data reads but should be explicitly defined via a chaincode flag", cc)
   190  		}
   191  	}
   192  
   193  	for cc, collections := range *ec.Collections {
   194  		if !ec.existsInChaincodes(cc) {
   195  			return nil, errors.Errorf("a collection specified chaincode %s but it wasn't specified with a chaincode flag", cc)
   196  		}
   197  		res[cc] = strings.Split(collections, ",")
   198  	}
   199  
   200  	return res, nil
   201  }
   202  
   203  func parseEndorsementDescriptors(descriptors []*EndorsementDescriptor) []endorsermentDescriptor {
   204  	var res []endorsermentDescriptor
   205  	for _, desc := range descriptors {
   206  		endorsersByGroups := make(map[string][]endorser)
   207  		for grp, endorsers := range desc.EndorsersByGroups {
   208  			for _, p := range endorsers.Peers {
   209  				endorsersByGroups[grp] = append(endorsersByGroups[grp], endorserFromRaw(p))
   210  			}
   211  		}
   212  		res = append(res, endorsermentDescriptor{
   213  			Chaincode:         desc.Chaincode,
   214  			Layouts:           desc.Layouts,
   215  			EndorsersByGroups: endorsersByGroups,
   216  		})
   217  	}
   218  	return res
   219  }
   220  
   221  type endorser struct {
   222  	MSPID        string
   223  	LedgerHeight uint64
   224  	Endpoint     string
   225  	Identity     string
   226  }
   227  
   228  type endorsermentDescriptor struct {
   229  	Chaincode         string
   230  	EndorsersByGroups map[string][]endorser
   231  	Layouts           []*Layout
   232  }
   233  
   234  func endorserFromRaw(p *Peer) endorser {
   235  	sId := &msp.SerializedIdentity{}
   236  	proto.Unmarshal(p.Identity, sId)
   237  	return endorser{
   238  		MSPID:        sId.Mspid,
   239  		Endpoint:     endpointFromEnvelope(p.MembershipInfo),
   240  		LedgerHeight: ledgerHeightFromEnvelope(p.StateInfo),
   241  		Identity:     string(sId.IdBytes),
   242  	}
   243  }
   244  
   245  func endpointFromEnvelope(env *gossip.Envelope) string {
   246  	if env == nil {
   247  		return ""
   248  	}
   249  	aliveMsg, _ := protoext.EnvelopeToGossipMessage(env)
   250  	if aliveMsg == nil {
   251  		return ""
   252  	}
   253  	if !protoext.IsAliveMsg(aliveMsg.GossipMessage) {
   254  		return ""
   255  	}
   256  	if aliveMsg.GetAliveMsg().Membership == nil {
   257  		return ""
   258  	}
   259  	return aliveMsg.GetAliveMsg().Membership.Endpoint
   260  }
   261  
   262  func ledgerHeightFromEnvelope(env *gossip.Envelope) uint64 {
   263  	if env == nil {
   264  		return 0
   265  	}
   266  	stateInfoMsg, _ := protoext.EnvelopeToGossipMessage(env)
   267  	if stateInfoMsg == nil {
   268  		return 0
   269  	}
   270  	if !protoext.IsStateInfoMsg(stateInfoMsg.GossipMessage) {
   271  		return 0
   272  	}
   273  	if stateInfoMsg.GetStateInfo().Properties == nil {
   274  		return 0
   275  	}
   276  	return stateInfoMsg.GetStateInfo().Properties.LedgerHeight
   277  }