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 }