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 }