github.com/kaituanwang/hyperledger@v2.0.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  	parser      ResponseParser
    42  }
    43  
    44  // SetCollections sets the collections to be the given collections
    45  func (pc *EndorsersCmd) SetCollections(collections *map[string]string) {
    46  	pc.collections = collections
    47  }
    48  
    49  // SetChaincodes sets the chaincodes to be the given chaincodes
    50  func (pc *EndorsersCmd) SetChaincodes(chaincodes *[]string) {
    51  	pc.chaincodes = chaincodes
    52  }
    53  
    54  // SetServer sets the server
    55  func (pc *EndorsersCmd) SetServer(server *string) {
    56  	pc.server = server
    57  }
    58  
    59  // SetChannel sets the channel
    60  func (pc *EndorsersCmd) SetChannel(channel *string) {
    61  	pc.channel = channel
    62  }
    63  
    64  // Execute executes the command
    65  func (pc *EndorsersCmd) Execute(conf common.Config) error {
    66  	if pc.channel == nil || *pc.channel == "" {
    67  		return errors.New("no channel specified")
    68  	}
    69  
    70  	if pc.server == nil || *pc.server == "" {
    71  		return errors.New("no server specified")
    72  	}
    73  
    74  	server := *pc.server
    75  	channel := *pc.channel
    76  
    77  	ccAndCol := &chaincodesAndCollections{
    78  		Chaincodes:  pc.chaincodes,
    79  		Collections: pc.collections,
    80  	}
    81  	cc2collections, err := ccAndCol.parseInput()
    82  	if err != nil {
    83  		return err
    84  	}
    85  
    86  	var ccCalls []*ChaincodeCall
    87  
    88  	for _, cc := range *ccAndCol.Chaincodes {
    89  		ccCalls = append(ccCalls, &ChaincodeCall{
    90  			Name:            cc,
    91  			CollectionNames: cc2collections[cc],
    92  		})
    93  	}
    94  
    95  	req, err := discovery.NewRequest().OfChannel(channel).AddEndorsersQuery(&ChaincodeInterest{Chaincodes: ccCalls})
    96  	if err != nil {
    97  		return errors.Wrap(err, "failed creating request")
    98  	}
    99  
   100  	res, err := pc.stub.Send(server, conf, req)
   101  	if err != nil {
   102  		return err
   103  	}
   104  
   105  	return pc.parser.ParseResponse(channel, res)
   106  }
   107  
   108  // EndorserResponseParser parses endorsement responses from the peer
   109  type EndorserResponseParser struct {
   110  	io.Writer
   111  }
   112  
   113  // ParseResponse parses the given response for the given channel
   114  func (parser *EndorserResponseParser) ParseResponse(channel string, res ServiceResponse) error {
   115  	rawResponse := res.Raw()
   116  	if len(rawResponse.Results) == 0 {
   117  		return errors.New("empty results")
   118  	}
   119  
   120  	if e := rawResponse.Results[0].GetError(); e != nil {
   121  		return errors.Errorf("server returned: %s", e.Content)
   122  	}
   123  
   124  	ccQueryRes := rawResponse.Results[0].GetCcQueryRes()
   125  	if ccQueryRes == nil {
   126  		return errors.Errorf("server returned response of unexpected type: %v", reflect.TypeOf(rawResponse.Results[0]))
   127  	}
   128  
   129  	jsonBytes, _ := json.MarshalIndent(parseEndorsementDescriptors(ccQueryRes.Content), "", "\t")
   130  	fmt.Fprintln(parser.Writer, string(jsonBytes))
   131  	return nil
   132  }
   133  
   134  type chaincodesAndCollections struct {
   135  	Chaincodes  *[]string
   136  	Collections *map[string]string
   137  }
   138  
   139  func (ec *chaincodesAndCollections) existsInChaincodes(chaincodeName string) bool {
   140  	for _, cc := range *ec.Chaincodes {
   141  		if chaincodeName == cc {
   142  			return true
   143  		}
   144  	}
   145  	return false
   146  }
   147  
   148  func (ec *chaincodesAndCollections) parseInput() (map[string][]string, error) {
   149  	var emptyChaincodes []string
   150  	if ec.Chaincodes == nil {
   151  		ec.Chaincodes = &emptyChaincodes
   152  	}
   153  	var emptyCollections map[string]string
   154  	if ec.Collections == nil {
   155  		ec.Collections = &emptyCollections
   156  	}
   157  
   158  	res := make(map[string][]string)
   159  
   160  	for _, cc := range *ec.Chaincodes {
   161  		res[cc] = nil
   162  	}
   163  
   164  	for cc, collections := range *ec.Collections {
   165  		if !ec.existsInChaincodes(cc) {
   166  			return nil, errors.Errorf("a collection specified chaincode %s but it wasn't specified with a chaincode flag", cc)
   167  		}
   168  		res[cc] = strings.Split(collections, ",")
   169  	}
   170  	return res, nil
   171  }
   172  
   173  func parseEndorsementDescriptors(descriptors []*EndorsementDescriptor) []endorsermentDescriptor {
   174  	var res []endorsermentDescriptor
   175  	for _, desc := range descriptors {
   176  		endorsersByGroups := make(map[string][]endorser)
   177  		for grp, endorsers := range desc.EndorsersByGroups {
   178  			for _, p := range endorsers.Peers {
   179  				endorsersByGroups[grp] = append(endorsersByGroups[grp], endorserFromRaw(p))
   180  			}
   181  		}
   182  		res = append(res, endorsermentDescriptor{
   183  			Chaincode:         desc.Chaincode,
   184  			Layouts:           desc.Layouts,
   185  			EndorsersByGroups: endorsersByGroups,
   186  		})
   187  	}
   188  	return res
   189  }
   190  
   191  type endorser struct {
   192  	MSPID        string
   193  	LedgerHeight uint64
   194  	Endpoint     string
   195  	Identity     string
   196  }
   197  
   198  type endorsermentDescriptor struct {
   199  	Chaincode         string
   200  	EndorsersByGroups map[string][]endorser
   201  	Layouts           []*Layout
   202  }
   203  
   204  func endorserFromRaw(p *Peer) endorser {
   205  	sId := &msp.SerializedIdentity{}
   206  	proto.Unmarshal(p.Identity, sId)
   207  	return endorser{
   208  		MSPID:        sId.Mspid,
   209  		Endpoint:     endpointFromEnvelope(p.MembershipInfo),
   210  		LedgerHeight: ledgerHeightFromEnvelope(p.StateInfo),
   211  		Identity:     string(sId.IdBytes),
   212  	}
   213  }
   214  
   215  func endpointFromEnvelope(env *gossip.Envelope) string {
   216  	if env == nil {
   217  		return ""
   218  	}
   219  	aliveMsg, _ := protoext.EnvelopeToGossipMessage(env)
   220  	if aliveMsg == nil {
   221  		return ""
   222  	}
   223  	if !protoext.IsAliveMsg(aliveMsg.GossipMessage) {
   224  		return ""
   225  	}
   226  	if aliveMsg.GetAliveMsg().Membership == nil {
   227  		return ""
   228  	}
   229  	return aliveMsg.GetAliveMsg().Membership.Endpoint
   230  }
   231  
   232  func ledgerHeightFromEnvelope(env *gossip.Envelope) uint64 {
   233  	if env == nil {
   234  		return 0
   235  	}
   236  	stateInfoMsg, _ := protoext.EnvelopeToGossipMessage(env)
   237  	if stateInfoMsg == nil {
   238  		return 0
   239  	}
   240  	if !protoext.IsStateInfoMsg(stateInfoMsg.GossipMessage) {
   241  		return 0
   242  	}
   243  	if stateInfoMsg.GetStateInfo().Properties == nil {
   244  		return 0
   245  	}
   246  	return stateInfoMsg.GetStateInfo().Properties.LedgerHeight
   247  }