github.com/inklabsfoundation/inkchain@v0.17.1-0.20181025012015-c3cef8062f19/peer/chaincode/common.go (about) 1 /* 2 Copyright IBM Corp. 2016 All Rights Reserved. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package chaincode 18 19 import ( 20 "encoding/json" 21 "errors" 22 "fmt" 23 "os" 24 "strings" 25 26 "strconv" 27 28 "github.com/inklabsfoundation/inkchain/common/cauthdsl" 29 "github.com/inklabsfoundation/inkchain/core/chaincode" 30 "github.com/inklabsfoundation/inkchain/core/chaincode/platforms" 31 "github.com/inklabsfoundation/inkchain/core/chaincode/shim" 32 "github.com/inklabsfoundation/inkchain/core/container" 33 "github.com/inklabsfoundation/inkchain/core/wallet" 34 "github.com/inklabsfoundation/inkchain/msp" 35 "github.com/inklabsfoundation/inkchain/peer/common" 36 pcommon "github.com/inklabsfoundation/inkchain/protos/common" 37 pb "github.com/inklabsfoundation/inkchain/protos/peer" 38 putils "github.com/inklabsfoundation/inkchain/protos/utils" 39 "github.com/spf13/cobra" 40 "golang.org/x/net/context" 41 ) 42 43 // checkSpec to see if chaincode resides within current package capture for language. 44 func checkSpec(spec *pb.ChaincodeSpec) error { 45 // Don't allow nil value 46 if spec == nil { 47 return errors.New("Expected chaincode specification, nil received") 48 } 49 50 platform, err := platforms.Find(spec.Type) 51 if err != nil { 52 return fmt.Errorf("Failed to determine platform type: %s", err) 53 } 54 55 return platform.ValidateSpec(spec) 56 } 57 58 // getChaincodeDeploymentSpec get chaincode deployment spec given the chaincode spec 59 func getChaincodeDeploymentSpec(spec *pb.ChaincodeSpec, crtPkg bool) (*pb.ChaincodeDeploymentSpec, error) { 60 var codePackageBytes []byte 61 if chaincode.IsDevMode() == false && crtPkg { 62 var err error 63 if err = checkSpec(spec); err != nil { 64 return nil, err 65 } 66 67 codePackageBytes, err = container.GetChaincodePackageBytes(spec) 68 if err != nil { 69 err = fmt.Errorf("Error getting chaincode package bytes: %s", err) 70 return nil, err 71 } 72 } 73 chaincodeDeploymentSpec := &pb.ChaincodeDeploymentSpec{ChaincodeSpec: spec, CodePackage: codePackageBytes} 74 return chaincodeDeploymentSpec, nil 75 } 76 77 // getChaincodeSpec get chaincode spec from the cli cmd pramameters 78 func getChaincodeSpec(cmd *cobra.Command) (*pb.ChaincodeSpec, error) { 79 spec := &pb.ChaincodeSpec{} 80 if err := checkChaincodeCmdParams(cmd); err != nil { 81 return spec, err 82 } 83 84 // Build the spec 85 input := &pb.ChaincodeInput{} 86 if err := json.Unmarshal([]byte(chaincodeCtorJSON), &input); err != nil { 87 return spec, fmt.Errorf("Chaincode argument error: %s", err) 88 } 89 90 chaincodeLang = strings.ToUpper(chaincodeLang) 91 if pb.ChaincodeSpec_Type_value[chaincodeLang] == int32(pb.ChaincodeSpec_JAVA) { 92 return nil, fmt.Errorf("Java chaincode is work-in-progress and disabled") 93 } 94 spec = &pb.ChaincodeSpec{ 95 Type: pb.ChaincodeSpec_Type(pb.ChaincodeSpec_Type_value[chaincodeLang]), 96 ChaincodeId: &pb.ChaincodeID{Path: chaincodePath, Name: chaincodeName, Version: chaincodeVersion}, 97 Input: input, 98 } 99 return spec, nil 100 } 101 102 func getSenderSpec(cmd *cobra.Command, cf *ChaincodeCmdFactory) (*pb.SenderSpec, error) { 103 senderSpec := &pb.SenderSpec{} 104 if len(priKey) != wallet.PriKeyStringLength { 105 return nil, errors.New("invalid priKey") 106 } 107 address, err := wallet.GetAddressHexFromPrikey(priKey) 108 if err != nil { 109 return nil, err 110 } 111 address = wallet.ADDRESS_PREFIX + address 112 input := &pb.ChaincodeInput{} 113 queryJson := "{\"Args\":[\"counter\",\"" + address + "\"]}" 114 if err := json.Unmarshal([]byte(queryJson), &input); err != nil { 115 return nil, fmt.Errorf("Chaincode argument error: %s", err) 116 } 117 chaincodeSpec := &pb.ChaincodeSpec{ 118 Type: pb.ChaincodeSpec_Type(pb.ChaincodeSpec_Type_value[chaincodeLang]), 119 ChaincodeId: &pb.ChaincodeID{Path: "", Name: "token", Version: ""}, 120 Input: input, 121 } 122 counterResp, err := ChaincodeInvokeOrQuery( 123 chaincodeSpec, 124 nil, 125 nil, 126 chainID, 127 false, 128 cf.Signer, 129 cf.EndorserClient, 130 cf.BroadcastClient) 131 if err != nil { 132 return nil, err 133 } 134 if counterResp.Response.Status != shim.OK { 135 return nil, errors.New(counterResp.Response.Message) 136 } 137 counter, err := strconv.ParseUint(string(counterResp.Response.Payload[:]), 10, 64) 138 if err != nil { 139 return nil, errors.New("invalid sender spec") 140 } 141 142 senderSpec.Sender = []byte(address) 143 senderSpec.Counter = counter 144 if feeLimit == "" { 145 return nil, errors.New("0 ink limit") 146 } 147 senderSpec.FeeLimit = []byte(feeLimit) 148 senderSpec.Msg = []byte(msg) 149 return senderSpec, nil 150 } 151 func chaincodeInvokeOrQuery(cmd *cobra.Command, args []string, invoke bool, cf *ChaincodeCmdFactory) (err error) { 152 spec, err := getChaincodeSpec(cmd) 153 if err != nil { 154 return err 155 } 156 var senderSpec *pb.SenderSpec 157 var sig []byte 158 if invoke && chaincodeName != "ascc" { 159 senderSpec, err = getSenderSpec(cmd, cf) 160 if err != nil { 161 return err 162 } 163 signature, err := wallet.SignInvoke(spec, customIDGenAlg, senderSpec, priKey) 164 if err != nil { 165 return err 166 } 167 sig = signature 168 } else { 169 senderSpec = nil 170 sig = nil 171 } 172 173 proposalResp, err := ChaincodeInvokeOrQuery( 174 spec, 175 senderSpec, 176 sig, 177 chainID, 178 invoke, 179 cf.Signer, 180 cf.EndorserClient, 181 cf.BroadcastClient) 182 183 if err != nil { 184 return fmt.Errorf("%s - %v", err, proposalResp) 185 } 186 187 if invoke { 188 if proposalResp.Response.Status >= shim.ERROR { 189 logger.Debugf("ESCC invoke result: %v", proposalResp) 190 pRespPayload, err := putils.GetProposalResponsePayload(proposalResp.Payload) 191 if err != nil { 192 return fmt.Errorf("Error while unmarshaling proposal response payload: %s", err) 193 } 194 ca, err := putils.GetChaincodeAction(pRespPayload.Extension) 195 if err != nil { 196 return fmt.Errorf("Error while unmarshaling chaincode action: %s", err) 197 } 198 logger.Warningf("Endorsement failure during invoke. chaincode result: %v", ca.Response) 199 } else { 200 logger.Debugf("ESCC invoke result: %v", proposalResp) 201 pRespPayload, err := putils.GetProposalResponsePayload(proposalResp.Payload) 202 if err != nil { 203 return fmt.Errorf("Error while unmarshaling proposal response payload: %s", err) 204 } 205 ca, err := putils.GetChaincodeAction(pRespPayload.Extension) 206 if err != nil { 207 return fmt.Errorf("Error while unmarshaling chaincode action: %s", err) 208 } 209 logger.Infof("Chaincode invoke successful. result: %v", ca.Response) 210 } 211 } else { 212 if proposalResp == nil { 213 return fmt.Errorf("Error query %s by endorsing: %s", chainFuncName, err) 214 } 215 216 if chaincodeQueryRaw { 217 if chaincodeQueryHex { 218 return fmt.Errorf("Options --raw (-r) and --hex (-x) are not compatible") 219 } 220 fmt.Print("Query Result (Raw): ") 221 os.Stdout.Write(proposalResp.Response.Payload) 222 } else { 223 if chaincodeQueryHex { 224 fmt.Printf("Query Result: %x\n", proposalResp.Response.Payload) 225 } else { 226 fmt.Printf("Query Result: %s\n", string(proposalResp.Response.Payload)) 227 } 228 } 229 } 230 return nil 231 } 232 233 func checkChaincodeCmdParams(cmd *cobra.Command) error { 234 //we need chaincode name for everything, including deploy 235 if chaincodeName == common.UndefinedParamValue { 236 return fmt.Errorf("Must supply value for %s name parameter.", chainFuncName) 237 } 238 239 if cmd.Name() == instantiateCmdName || cmd.Name() == installCmdName || 240 cmd.Name() == upgradeCmdName || cmd.Name() == packageCmdName { 241 if chaincodeVersion == common.UndefinedParamValue { 242 return fmt.Errorf("Chaincode version is not provided for %s", cmd.Name()) 243 } 244 } 245 246 if escc != common.UndefinedParamValue { 247 logger.Infof("Using escc %s", escc) 248 } else { 249 logger.Info("Using default escc") 250 escc = "escc" 251 } 252 253 if vscc != common.UndefinedParamValue { 254 logger.Infof("Using vscc %s", vscc) 255 } else { 256 logger.Info("Using default vscc") 257 vscc = "vscc" 258 } 259 260 if policy != common.UndefinedParamValue { 261 p, err := cauthdsl.FromString(policy) 262 if err != nil { 263 return fmt.Errorf("Invalid policy %s", policy) 264 } 265 policyMarhsalled = putils.MarshalOrPanic(p) 266 } 267 268 // Check that non-empty chaincode parameters contain only Args as a key. 269 // Type checking is done later when the JSON is actually unmarshaled 270 // into a pb.ChaincodeInput. To better understand what's going 271 // on here with JSON parsing see http://blog.golang.org/json-and-go - 272 // Generic JSON with interface{} 273 if chaincodeCtorJSON != "{}" { 274 var f interface{} 275 err := json.Unmarshal([]byte(chaincodeCtorJSON), &f) 276 if err != nil { 277 return fmt.Errorf("Chaincode argument error: %s", err) 278 } 279 m := f.(map[string]interface{}) 280 sm := make(map[string]interface{}) 281 for k := range m { 282 sm[strings.ToLower(k)] = m[k] 283 } 284 _, argsPresent := sm["args"] 285 _, funcPresent := sm["function"] 286 if !argsPresent || (len(m) == 2 && !funcPresent) || len(m) > 2 { 287 return errors.New("Non-empty JSON chaincode parameters must contain the following keys: 'Args' or 'Function' and 'Args'") 288 } 289 } else { 290 if cmd == nil || (cmd != chaincodeInstallCmd && cmd != chaincodePackageCmd) { 291 return errors.New("Empty JSON chaincode parameters must contain the following keys: 'Args' or 'Function' and 'Args'") 292 } 293 } 294 295 return nil 296 } 297 298 // ChaincodeCmdFactory holds the clients used by ChaincodeCmd 299 type ChaincodeCmdFactory struct { 300 EndorserClient pb.EndorserClient 301 Signer msp.SigningIdentity 302 BroadcastClient common.BroadcastClient 303 } 304 305 // InitCmdFactory init the ChaincodeCmdFactory with default clients 306 func InitCmdFactory(isEndorserRequired, isOrdererRequired bool) (*ChaincodeCmdFactory, error) { 307 var err error 308 var endorserClient pb.EndorserClient 309 if isEndorserRequired { 310 endorserClient, err = common.GetEndorserClientFnc() 311 if err != nil { 312 return nil, fmt.Errorf("Error getting endorser client %s: %s", chainFuncName, err) 313 } 314 } 315 316 signer, err := common.GetDefaultSignerFnc() 317 if err != nil { 318 return nil, fmt.Errorf("Error getting default signer: %s", err) 319 } 320 321 var broadcastClient common.BroadcastClient 322 if isOrdererRequired { 323 if len(orderingEndpoint) == 0 { 324 orderingEndpoints, err := common.GetOrdererEndpointOfChainFnc(chainID, signer, endorserClient) 325 if err != nil { 326 return nil, fmt.Errorf("Error getting (%s) orderer endpoint: %s", chainID, err) 327 } 328 if len(orderingEndpoints) == 0 { 329 return nil, fmt.Errorf("Error no orderer endpoint got for %s", chainID) 330 } 331 logger.Infof("Get chain(%s) orderer endpoint: %s", chainID, orderingEndpoints[0]) 332 orderingEndpoint = orderingEndpoints[0] 333 } 334 335 broadcastClient, err = common.GetBroadcastClientFnc(orderingEndpoint, tls, caFile) 336 337 if err != nil { 338 return nil, fmt.Errorf("Error getting broadcast client: %s", err) 339 } 340 } 341 return &ChaincodeCmdFactory{ 342 EndorserClient: endorserClient, 343 Signer: signer, 344 BroadcastClient: broadcastClient, 345 }, nil 346 } 347 348 // ChaincodeInvokeOrQuery invokes or queries the chaincode. If successful, the 349 // INVOKE form prints the ProposalResponse to STDOUT, and the QUERY form prints 350 // the query result on STDOUT. A command-line flag (-r, --raw) determines 351 // whether the query result is output as raw bytes, or as a printable string. 352 // The printable form is optionally (-x, --hex) a hexadecimal representation 353 // of the query response. If the query response is NIL, nothing is output. 354 // 355 // NOTE - Query will likely go away as all interactions with the endorser are 356 // Proposal and ProposalResponses 357 func ChaincodeInvokeOrQuery( 358 spec *pb.ChaincodeSpec, 359 senderSpec *pb.SenderSpec, 360 sig []byte, 361 cID string, 362 invoke bool, 363 signer msp.SigningIdentity, 364 endorserClient pb.EndorserClient, 365 bc common.BroadcastClient, 366 ) (*pb.ProposalResponse, error) { 367 // Build the ChaincodeInvocationSpec message 368 invocation := &pb.ChaincodeInvocationSpec{ChaincodeSpec: spec, SenderSpec: senderSpec, Sig: sig} 369 if customIDGenAlg != common.UndefinedParamValue { 370 invocation.IdGenerationAlg = customIDGenAlg 371 } 372 373 creator, err := signer.Serialize() 374 if err != nil { 375 return nil, fmt.Errorf("Error serializing identity for %s: %s", signer.GetIdentifier(), err) 376 } 377 378 funcName := "invoke" 379 if !invoke { 380 funcName = "query" 381 } 382 383 var prop *pb.Proposal 384 prop, _, err = putils.CreateProposalFromCIS(pcommon.HeaderType_ENDORSER_TRANSACTION, cID, invocation, creator) 385 if err != nil { 386 return nil, fmt.Errorf("Error creating proposal %s: %s", funcName, err) 387 } 388 389 var signedProp *pb.SignedProposal 390 signedProp, err = putils.GetSignedProposal(prop, signer) 391 if err != nil { 392 return nil, fmt.Errorf("Error creating signed proposal %s: %s", funcName, err) 393 } 394 395 var proposalResp *pb.ProposalResponse 396 proposalResp, err = endorserClient.ProcessProposal(context.Background(), signedProp) 397 if err != nil { 398 return nil, fmt.Errorf("Error endorsing %s: %s", funcName, err) 399 } 400 401 if invoke { 402 if proposalResp != nil { 403 if proposalResp.Response.Status >= shim.ERROR { 404 return proposalResp, nil 405 } 406 // assemble a signed transaction (it's an Envelope message) 407 env, err := putils.CreateSignedTx(prop, signer, proposalResp) 408 if err != nil { 409 return proposalResp, fmt.Errorf("Could not assemble transaction, err %s", err) 410 } 411 412 // send the envelope for ordering 413 if err = bc.Send(env); err != nil { 414 return proposalResp, fmt.Errorf("Error sending transaction %s: %s", funcName, err) 415 } 416 } 417 } 418 419 return proposalResp, nil 420 }