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 }