github.com/leonlxy/hyperledger@v1.0.0-alpha.0.20170427033203-34922035d248/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  	"github.com/hyperledger/fabric/common/cauthdsl"
    27  	"github.com/hyperledger/fabric/core/chaincode"
    28  	"github.com/hyperledger/fabric/core/chaincode/platforms"
    29  	"github.com/hyperledger/fabric/core/container"
    30  	"github.com/hyperledger/fabric/msp"
    31  	"github.com/hyperledger/fabric/peer/common"
    32  	pcommon "github.com/hyperledger/fabric/protos/common"
    33  	pb "github.com/hyperledger/fabric/protos/peer"
    34  	putils "github.com/hyperledger/fabric/protos/utils"
    35  	"github.com/spf13/cobra"
    36  	"golang.org/x/net/context"
    37  )
    38  
    39  // checkSpec to see if chaincode resides within current package capture for language.
    40  func checkSpec(spec *pb.ChaincodeSpec) error {
    41  	// Don't allow nil value
    42  	if spec == nil {
    43  		return errors.New("Expected chaincode specification, nil received")
    44  	}
    45  
    46  	platform, err := platforms.Find(spec.Type)
    47  	if err != nil {
    48  		return fmt.Errorf("Failed to determine platform type: %s", err)
    49  	}
    50  
    51  	return platform.ValidateSpec(spec)
    52  }
    53  
    54  // getChaincodeDeploymentSpec get chaincode deployment spec given the chaincode spec
    55  func getChaincodeDeploymentSpec(spec *pb.ChaincodeSpec, crtPkg bool) (*pb.ChaincodeDeploymentSpec, error) {
    56  	var codePackageBytes []byte
    57  	if chaincode.IsDevMode() == false && crtPkg {
    58  		var err error
    59  		if err = checkSpec(spec); err != nil {
    60  			return nil, err
    61  		}
    62  
    63  		codePackageBytes, err = container.GetChaincodePackageBytes(spec)
    64  		if err != nil {
    65  			err = fmt.Errorf("Error getting chaincode package bytes: %s", err)
    66  			return nil, err
    67  		}
    68  	}
    69  	chaincodeDeploymentSpec := &pb.ChaincodeDeploymentSpec{ChaincodeSpec: spec, CodePackage: codePackageBytes}
    70  	return chaincodeDeploymentSpec, nil
    71  }
    72  
    73  // getChaincodeSpec get chaincode spec from the cli cmd pramameters
    74  func getChaincodeSpec(cmd *cobra.Command) (*pb.ChaincodeSpec, error) {
    75  	spec := &pb.ChaincodeSpec{}
    76  	if err := checkChaincodeCmdParams(cmd); err != nil {
    77  		return spec, err
    78  	}
    79  
    80  	// Build the spec
    81  	input := &pb.ChaincodeInput{}
    82  	if err := json.Unmarshal([]byte(chaincodeCtorJSON), &input); err != nil {
    83  		return spec, fmt.Errorf("Chaincode argument error: %s", err)
    84  	}
    85  
    86  	chaincodeLang = strings.ToUpper(chaincodeLang)
    87  	spec = &pb.ChaincodeSpec{
    88  		Type:        pb.ChaincodeSpec_Type(pb.ChaincodeSpec_Type_value[chaincodeLang]),
    89  		ChaincodeId: &pb.ChaincodeID{Path: chaincodePath, Name: chaincodeName, Version: chaincodeVersion},
    90  		Input:       input,
    91  	}
    92  	return spec, nil
    93  }
    94  
    95  func chaincodeInvokeOrQuery(cmd *cobra.Command, args []string, invoke bool, cf *ChaincodeCmdFactory) (err error) {
    96  	spec, err := getChaincodeSpec(cmd)
    97  	if err != nil {
    98  		return err
    99  	}
   100  
   101  	proposalResp, err := ChaincodeInvokeOrQuery(spec, chainID, invoke, cf.Signer, cf.EndorserClient, cf.BroadcastClient)
   102  	if err != nil {
   103  		return err
   104  	}
   105  
   106  	if invoke {
   107  		logger.Infof("Invoke result: %v", proposalResp)
   108  	} else {
   109  		if proposalResp == nil {
   110  			return fmt.Errorf("Error query %s by endorsing: %s", chainFuncName, err)
   111  		}
   112  
   113  		if chaincodeQueryRaw {
   114  			if chaincodeQueryHex {
   115  				err = errors.New("Options --raw (-r) and --hex (-x) are not compatible")
   116  				return
   117  			}
   118  			fmt.Print("Query Result (Raw): ")
   119  			os.Stdout.Write(proposalResp.Response.Payload)
   120  		} else {
   121  			if chaincodeQueryHex {
   122  				fmt.Printf("Query Result: %x\n", proposalResp.Response.Payload)
   123  			} else {
   124  				fmt.Printf("Query Result: %s\n", string(proposalResp.Response.Payload))
   125  			}
   126  		}
   127  	}
   128  	return err
   129  }
   130  
   131  func checkChaincodeCmdParams(cmd *cobra.Command) error {
   132  	//we need chaincode name for everything, including deploy
   133  	if chaincodeName == common.UndefinedParamValue {
   134  		return fmt.Errorf("Must supply value for %s name parameter.", chainFuncName)
   135  	}
   136  
   137  	if cmd.Name() == instantiate_cmdname || cmd.Name() == install_cmdname || cmd.Name() == upgrade_cmdname || cmd.Name() == package_cmdname {
   138  		if chaincodeVersion == common.UndefinedParamValue {
   139  			return fmt.Errorf("Chaincode version is not provided for %s", cmd.Name())
   140  		}
   141  	}
   142  
   143  	// if it's not a deploy or an upgrade we don't need policy, escc and vscc
   144  	if cmd.Name() != instantiate_cmdname && cmd.Name() != upgrade_cmdname {
   145  		if escc != common.UndefinedParamValue {
   146  			return errors.New("escc should be supplied only to chaincode deploy requests")
   147  		}
   148  
   149  		if vscc != common.UndefinedParamValue {
   150  			return errors.New("vscc should be supplied only to chaincode deploy requests")
   151  		}
   152  
   153  		if policy != common.UndefinedParamValue {
   154  			return errors.New("policy should be supplied only to chaincode deploy requests")
   155  		}
   156  	} else {
   157  		if escc != common.UndefinedParamValue {
   158  			logger.Infof("Using escc %s", escc)
   159  		} else {
   160  			logger.Info("Using default escc")
   161  			escc = "escc"
   162  		}
   163  
   164  		if vscc != common.UndefinedParamValue {
   165  			logger.Infof("Using vscc %s", vscc)
   166  		} else {
   167  			logger.Info("Using default vscc")
   168  			vscc = "vscc"
   169  		}
   170  
   171  		if policy != common.UndefinedParamValue {
   172  			p, err := cauthdsl.FromString(policy)
   173  			if err != nil {
   174  				return fmt.Errorf("Invalid policy %s", policy)
   175  			}
   176  			policyMarhsalled = putils.MarshalOrPanic(p)
   177  		}
   178  	}
   179  
   180  	// Check that non-empty chaincode parameters contain only Args as a key.
   181  	// Type checking is done later when the JSON is actually unmarshaled
   182  	// into a pb.ChaincodeInput. To better understand what's going
   183  	// on here with JSON parsing see http://blog.golang.org/json-and-go -
   184  	// Generic JSON with interface{}
   185  	if chaincodeCtorJSON != "{}" {
   186  		var f interface{}
   187  		err := json.Unmarshal([]byte(chaincodeCtorJSON), &f)
   188  		if err != nil {
   189  			return fmt.Errorf("Chaincode argument error: %s", err)
   190  		}
   191  		m := f.(map[string]interface{})
   192  		sm := make(map[string]interface{})
   193  		for k := range m {
   194  			sm[strings.ToLower(k)] = m[k]
   195  		}
   196  		_, argsPresent := sm["args"]
   197  		_, funcPresent := sm["function"]
   198  		if !argsPresent || (len(m) == 2 && !funcPresent) || len(m) > 2 {
   199  			return errors.New("Non-empty JSON chaincode parameters must contain the following keys: 'Args' or 'Function' and 'Args'")
   200  		}
   201  	} else {
   202  		if cmd == nil || (cmd != chaincodeInstallCmd && cmd != chaincodePackageCmd) {
   203  			return errors.New("Empty JSON chaincode parameters must contain the following keys: 'Args' or 'Function' and 'Args'")
   204  		}
   205  	}
   206  
   207  	return nil
   208  }
   209  
   210  // ChaincodeCmdFactory holds the clients used by ChaincodeCmd
   211  type ChaincodeCmdFactory struct {
   212  	EndorserClient  pb.EndorserClient
   213  	Signer          msp.SigningIdentity
   214  	BroadcastClient common.BroadcastClient
   215  }
   216  
   217  // InitCmdFactory init the ChaincodeCmdFactory with default clients
   218  func InitCmdFactory(isEndorserRequired, isOrdererRequired bool) (*ChaincodeCmdFactory, error) {
   219  	var err error
   220  	var endorserClient pb.EndorserClient
   221  	if isEndorserRequired {
   222  		endorserClient, err = common.GetEndorserClient()
   223  		if err != nil {
   224  			return nil, fmt.Errorf("Error getting endorser client %s: %s", chainFuncName, err)
   225  		}
   226  	}
   227  
   228  	signer, err := common.GetDefaultSigner()
   229  	if err != nil {
   230  		return nil, fmt.Errorf("Error getting default signer: %s", err)
   231  	}
   232  
   233  	var broadcastClient common.BroadcastClient
   234  	if isOrdererRequired {
   235  		broadcastClient, err = common.GetBroadcastClient(orderingEndpoint, tls, caFile)
   236  
   237  		if err != nil {
   238  			return nil, fmt.Errorf("Error getting broadcast client: %s", err)
   239  		}
   240  	}
   241  	return &ChaincodeCmdFactory{
   242  		EndorserClient:  endorserClient,
   243  		Signer:          signer,
   244  		BroadcastClient: broadcastClient,
   245  	}, nil
   246  }
   247  
   248  // ChaincodeInvokeOrQuery invokes or queries the chaincode. If successful, the
   249  // INVOKE form prints the ProposalResponse to STDOUT, and the QUERY form prints
   250  // the query result on STDOUT. A command-line flag (-r, --raw) determines
   251  // whether the query result is output as raw bytes, or as a printable string.
   252  // The printable form is optionally (-x, --hex) a hexadecimal representation
   253  // of the query response. If the query response is NIL, nothing is output.
   254  //
   255  // NOTE - Query will likely go away as all interactions with the endorser are
   256  // Proposal and ProposalResponses
   257  func ChaincodeInvokeOrQuery(spec *pb.ChaincodeSpec, cID string, invoke bool, signer msp.SigningIdentity, endorserClient pb.EndorserClient, bc common.BroadcastClient) (*pb.ProposalResponse, error) {
   258  	// Build the ChaincodeInvocationSpec message
   259  	invocation := &pb.ChaincodeInvocationSpec{ChaincodeSpec: spec}
   260  	if customIDGenAlg != common.UndefinedParamValue {
   261  		invocation.IdGenerationAlg = customIDGenAlg
   262  	}
   263  
   264  	creator, err := signer.Serialize()
   265  	if err != nil {
   266  		return nil, fmt.Errorf("Error serializing identity for %s: %s", signer.GetIdentifier(), err)
   267  	}
   268  
   269  	funcName := "invoke"
   270  	if !invoke {
   271  		funcName = "query"
   272  	}
   273  
   274  	var prop *pb.Proposal
   275  	prop, _, err = putils.CreateProposalFromCIS(pcommon.HeaderType_ENDORSER_TRANSACTION, cID, invocation, creator)
   276  	if err != nil {
   277  		return nil, fmt.Errorf("Error creating proposal  %s: %s", funcName, err)
   278  	}
   279  
   280  	var signedProp *pb.SignedProposal
   281  	signedProp, err = putils.GetSignedProposal(prop, signer)
   282  	if err != nil {
   283  		return nil, fmt.Errorf("Error creating signed proposal  %s: %s", funcName, err)
   284  	}
   285  
   286  	var proposalResp *pb.ProposalResponse
   287  	proposalResp, err = endorserClient.ProcessProposal(context.Background(), signedProp)
   288  	if err != nil {
   289  		return nil, fmt.Errorf("Error endorsing %s: %s", funcName, err)
   290  	}
   291  
   292  	if invoke {
   293  		if proposalResp != nil {
   294  			// assemble a signed transaction (it's an Envelope message)
   295  			env, err := putils.CreateSignedTx(prop, signer, proposalResp)
   296  			if err != nil {
   297  				return proposalResp, fmt.Errorf("Could not assemble transaction, err %s", err)
   298  			}
   299  
   300  			// send the envelope for ordering
   301  			if err = bc.Send(env); err != nil {
   302  				return proposalResp, fmt.Errorf("Error sending transaction %s: %s", funcName, err)
   303  			}
   304  		}
   305  	}
   306  
   307  	return proposalResp, nil
   308  }