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  }