github.com/hechain20/hechain@v0.0.0-20220316014945-b544036ba106/discovery/cmd/endorsers.go (about)

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