github.com/inklabsfoundation/inkchain@v0.17.1-0.20181025012015-c3cef8062f19/core/scc/vscc/validator_onevalidsignature.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 vscc
    18  
    19  import (
    20  	"fmt"
    21  
    22  	"errors"
    23  
    24  	"github.com/golang/protobuf/proto"
    25  
    26  	"github.com/inklabsfoundation/inkchain/common/cauthdsl"
    27  	"github.com/inklabsfoundation/inkchain/common/flogging"
    28  	"github.com/inklabsfoundation/inkchain/core/chaincode/shim"
    29  	"github.com/inklabsfoundation/inkchain/core/common/ccprovider"
    30  	"github.com/inklabsfoundation/inkchain/core/common/sysccprovider"
    31  	"github.com/inklabsfoundation/inkchain/core/ledger/kvledger/txmgmt/ledgerutil"
    32  	"github.com/inklabsfoundation/inkchain/core/scc/lscc"
    33  	"github.com/inklabsfoundation/inkchain/core/wallet"
    34  	mspmgmt "github.com/inklabsfoundation/inkchain/msp/mgmt"
    35  	"github.com/inklabsfoundation/inkchain/protos/common"
    36  	"github.com/inklabsfoundation/inkchain/protos/ledger/rwset/kvrwset"
    37  	"github.com/inklabsfoundation/inkchain/protos/msp"
    38  	pb "github.com/inklabsfoundation/inkchain/protos/peer"
    39  	"github.com/inklabsfoundation/inkchain/protos/utils"
    40  )
    41  
    42  var logger = flogging.MustGetLogger("vscc")
    43  
    44  const (
    45  	DUPLICATED_IDENTITY_ERROR = "Endorsement policy evaluation failure might be caused by duplicated identities"
    46  )
    47  
    48  // ValidatorOneValidSignature implements the default transaction validation policy,
    49  // which is to check the correctness of the read-write set and the endorsement
    50  // signatures
    51  type ValidatorOneValidSignature struct {
    52  	// sccprovider is the interface with which we call
    53  	// methods of the system chaincode package without
    54  	// import cycles
    55  	sccprovider sysccprovider.SystemChaincodeProvider
    56  }
    57  
    58  // Init is called once when the chaincode started the first time
    59  func (vscc *ValidatorOneValidSignature) Init(stub shim.ChaincodeStubInterface) pb.Response {
    60  	vscc.sccprovider = sysccprovider.GetSystemChaincodeProvider()
    61  
    62  	return shim.Success(nil)
    63  }
    64  
    65  // Invoke is called to validate the specified block of transactions
    66  // This validation system chaincode will check the read-write set validity and at least 1
    67  // correct endorsement. Later we can create more validation system
    68  // chaincodes to provide more sophisticated policy processing such as enabling
    69  // policy specification to be coded as a transaction of the chaincode and the client
    70  // selecting which policy to use for validation using parameter function
    71  // @return serialized Block of valid and invalid transactions identified
    72  // Note that Peer calls this function with 3 arguments, where args[0] is the
    73  // function name, args[1] is the Envelope and args[2] is the validation policy
    74  func (vscc *ValidatorOneValidSignature) Invoke(stub shim.ChaincodeStubInterface) pb.Response {
    75  	// TODO: document the argument in some white paper or design document
    76  	// args[0] - function name (not used now)
    77  	// args[1] - serialized Envelope
    78  	// args[2] - serialized policy
    79  	args := stub.GetArgs()
    80  	if len(args) < 3 {
    81  		return shim.Error("Incorrect number of arguments")
    82  	}
    83  
    84  	if args[1] == nil {
    85  		return shim.Error("No block to validate")
    86  	}
    87  
    88  	if args[2] == nil {
    89  		return shim.Error("No policy supplied")
    90  	}
    91  
    92  	logger.Debugf("VSCC invoked")
    93  
    94  	// get the envelope...
    95  	env, err := utils.GetEnvelopeFromBlock(args[1])
    96  	if err != nil {
    97  		logger.Errorf("VSCC error: GetEnvelope failed, err %s", err)
    98  		return shim.Error(err.Error())
    99  	}
   100  
   101  	// ...and the payload...
   102  	payl, err := utils.GetPayload(env)
   103  	if err != nil {
   104  		logger.Errorf("VSCC error: GetPayload failed, err %s", err)
   105  		return shim.Error(err.Error())
   106  	}
   107  
   108  	chdr, err := utils.UnmarshalChannelHeader(payl.Header.ChannelHeader)
   109  	if err != nil {
   110  		return shim.Error(err.Error())
   111  	}
   112  
   113  	// get the policy
   114  	mgr := mspmgmt.GetManagerForChain(chdr.ChannelId)
   115  	pProvider := cauthdsl.NewPolicyProvider(mgr)
   116  	policy, _, err := pProvider.NewPolicy(args[2])
   117  	if err != nil {
   118  		logger.Errorf("VSCC error: pProvider.NewPolicy failed, err %s", err)
   119  		return shim.Error(err.Error())
   120  	}
   121  
   122  	// validate the payload type
   123  	if common.HeaderType(chdr.Type) != common.HeaderType_ENDORSER_TRANSACTION {
   124  		logger.Errorf("Only Endorser Transactions are supported, provided type %d", chdr.Type)
   125  		return shim.Error(fmt.Sprintf("Only Endorser Transactions are supported, provided type %d", chdr.Type))
   126  	}
   127  
   128  	// ...and the transaction...
   129  	tx, err := utils.GetTransaction(payl.Data)
   130  	if err != nil {
   131  		logger.Errorf("VSCC error: GetTransaction failed, err %s", err)
   132  		return shim.Error(err.Error())
   133  	}
   134  
   135  	// loop through each of the actions within
   136  	for _, act := range tx.Actions {
   137  		cap, err := utils.GetChaincodeActionPayload(act.Payload)
   138  		if err != nil {
   139  			logger.Errorf("VSCC error: GetChaincodeActionPayload failed, err %s", err)
   140  			return shim.Error(err.Error())
   141  		}
   142  
   143  		signatureSet, err := vscc.deduplicateIdentity(cap)
   144  		if err != nil {
   145  			return shim.Error(err.Error())
   146  		}
   147  
   148  		// evaluate the signature set against the policy
   149  		err = policy.Evaluate(signatureSet)
   150  		if err != nil {
   151  			logger.Warningf("Endorsement policy failure for transaction txid=%s, err: %s", chdr.GetTxId(), err.Error())
   152  			if len(signatureSet) < len(cap.Action.Endorsements) {
   153  				// Warning: duplicated identities exist, endorsement failure might be cause by this reason
   154  				return shim.Error(DUPLICATED_IDENTITY_ERROR)
   155  			}
   156  			return shim.Error(fmt.Sprintf("VSCC error: policy evaluation failed, err %s", err))
   157  		}
   158  
   159  		hdrExt, err := utils.GetChaincodeHeaderExtension(payl.Header)
   160  		if err != nil {
   161  			logger.Errorf("VSCC error: GetChaincodeHeaderExtension failed, err %s", err)
   162  			return shim.Error(err.Error())
   163  		}
   164  
   165  		// do some extra validation that is specific to lscc
   166  		if hdrExt.ChaincodeId.Name == "lscc" {
   167  			logger.Debugf("VSCC info: doing special validation for LSCC")
   168  
   169  			err = vscc.ValidateLSCCInvocation(stub, chdr.ChannelId, env, cap, payl)
   170  			if err != nil {
   171  				logger.Errorf("VSCC error: ValidateLSCCInvocation failed, err %s", err)
   172  				return shim.Error(err.Error())
   173  			}
   174  		}
   175  	}
   176  
   177  	logger.Debugf("VSCC exists successfully")
   178  
   179  	return shim.Success(nil)
   180  }
   181  
   182  // checkInstantiationPolicy evaluates an instantiation policy against a signed proposal
   183  func (vscc *ValidatorOneValidSignature) checkInstantiationPolicy(chainName string, env *common.Envelope, instantiationPolicy []byte, payl *common.Payload) error {
   184  	// create a policy object from the policy bytes
   185  	mgr := mspmgmt.GetManagerForChain(chainName)
   186  	if mgr == nil {
   187  		return fmt.Errorf("MSP manager for channel %s is nil, aborting", chainName)
   188  	}
   189  
   190  	npp := cauthdsl.NewPolicyProvider(mgr)
   191  	instPol, _, err := npp.NewPolicy(instantiationPolicy)
   192  	if err != nil {
   193  		return err
   194  	}
   195  
   196  	logger.Debugf("VSCC info: checkInstantiationPolicy starts, policy is %#v", instPol)
   197  
   198  	// get the signature header
   199  	shdr, err := utils.GetSignatureHeader(payl.Header.SignatureHeader)
   200  	if err != nil {
   201  		return err
   202  	}
   203  
   204  	// construct signed data we can evaluate the instantiation policy against
   205  	sd := []*common.SignedData{&common.SignedData{
   206  		Data:      env.Payload,
   207  		Identity:  shdr.Creator,
   208  		Signature: env.Signature,
   209  	}}
   210  	err = instPol.Evaluate(sd)
   211  	if err != nil {
   212  		return fmt.Errorf("chaincode instantiation policy violated, error %s", err)
   213  	}
   214  	return nil
   215  }
   216  
   217  func (vscc *ValidatorOneValidSignature) ValidateLSCCInvocation(stub shim.ChaincodeStubInterface, chid string, env *common.Envelope, cap *pb.ChaincodeActionPayload, payl *common.Payload) error {
   218  	cpp, err := utils.GetChaincodeProposalPayload(cap.ChaincodeProposalPayload)
   219  	if err != nil {
   220  		logger.Errorf("VSCC error: GetChaincodeProposalPayload failed, err %s", err)
   221  		return err
   222  	}
   223  
   224  	cis := &pb.ChaincodeInvocationSpec{}
   225  	err = proto.Unmarshal(cpp.Input, cis)
   226  	if err != nil {
   227  		logger.Errorf("VSCC error: Unmarshal ChaincodeInvocationSpec failed, err %s", err)
   228  		return err
   229  	}
   230  
   231  	if cis.ChaincodeSpec == nil ||
   232  		cis.ChaincodeSpec.Input == nil ||
   233  		cis.ChaincodeSpec.Input.Args == nil {
   234  		logger.Errorf("VSCC error: committing invalid vscc invocation")
   235  		return fmt.Errorf("VSCC error: committing invalid vscc invocation")
   236  	}
   237  
   238  	lsccFunc := string(cis.ChaincodeSpec.Input.Args[0])
   239  	lsccArgs := cis.ChaincodeSpec.Input.Args[1:]
   240  
   241  	logger.Debugf("VSCC info: ValidateLSCCInvocation acting on %s %#v", lsccFunc, lsccArgs)
   242  
   243  	switch lsccFunc {
   244  	case lscc.UPGRADE, lscc.DEPLOY:
   245  		logger.Debugf("VSCC info: validating invocation of lscc function %s on arguments %#v", lsccFunc, lsccArgs)
   246  
   247  		if len(lsccArgs) < 2 || len(lsccArgs) > 5 {
   248  			return fmt.Errorf("Wrong number of arguments for invocation lscc(%s): expected between 2 and 5, received %d", lsccFunc, len(lsccArgs))
   249  		}
   250  
   251  		cdsArgs, err := utils.GetChaincodeDeploymentSpec(lsccArgs[1])
   252  		if err != nil {
   253  			return fmt.Errorf("GetChaincodeDeploymentSpec error %s", err)
   254  		}
   255  
   256  		if cdsArgs == nil || cdsArgs.ChaincodeSpec == nil || cdsArgs.ChaincodeSpec.ChaincodeId == nil ||
   257  			cap.Action == nil || cap.Action.ProposalResponsePayload == nil {
   258  			return fmt.Errorf("VSCC error: invocation of lscc(%s) does not have appropriate arguments", lsccFunc)
   259  		}
   260  
   261  		// get the rwset
   262  		pRespPayload, err := utils.GetProposalResponsePayload(cap.Action.ProposalResponsePayload)
   263  		if err != nil {
   264  			return fmt.Errorf("GetProposalResponsePayload error %s", err)
   265  		}
   266  		if pRespPayload.Extension == nil {
   267  			return fmt.Errorf("nil pRespPayload.Extension")
   268  		}
   269  		respPayload, err := utils.GetChaincodeAction(pRespPayload.Extension)
   270  		if err != nil {
   271  			return fmt.Errorf("GetChaincodeAction error %s", err)
   272  		}
   273  		ledgerSet := &ledgerutil.LedgerSet{}
   274  		if err = ledgerSet.FromProtoBytes(respPayload.Results); err != nil {
   275  			return fmt.Errorf("ledgerSet.FromProtoBytes error %s", err)
   276  		}
   277  		txRWSet := ledgerSet.TxRwSet
   278  
   279  		// extract the rwset for lscc
   280  		var lsccrwset *kvrwset.KVRWSet
   281  		for _, ns := range txRWSet.NsRwSets {
   282  			logger.Debugf("Namespace %s", ns.NameSpace)
   283  			if ns.NameSpace == "lscc" {
   284  				lsccrwset = ns.KvRwSet
   285  				break
   286  			}
   287  		}
   288  
   289  		// retrieve from the ledger the entry for the chaincode at hand
   290  		cdLedger, ccExistsOnLedger, err := vscc.getInstantiatedCC(chid, cdsArgs.ChaincodeSpec.ChaincodeId.Name)
   291  		if err != nil {
   292  			return err
   293  		}
   294  
   295  		/******************************************/
   296  		/* security check 0 - validation of rwset */
   297  		/******************************************/
   298  		// there has to be one
   299  		if lsccrwset == nil {
   300  			return errors.New("No read write set for lscc was found")
   301  		}
   302  		// there can only be a single one
   303  		if len(lsccrwset.Writes) != 1 {
   304  			return errors.New("LSCC can only issue a single putState upon deploy/upgrade")
   305  		}
   306  		// the key name must be the chaincode id
   307  		if lsccrwset.Writes[0].Key != cdsArgs.ChaincodeSpec.ChaincodeId.Name {
   308  			return fmt.Errorf("Expected key %s, found %s", cdsArgs.ChaincodeSpec.ChaincodeId.Name, lsccrwset.Writes[0].Key)
   309  		}
   310  		// the value must be a ChaincodeData struct
   311  		cdRWSet := &ccprovider.ChaincodeData{}
   312  		err = proto.Unmarshal(lsccrwset.Writes[0].Value, cdRWSet)
   313  		if err != nil {
   314  			return fmt.Errorf("Unmarhsalling of ChaincodeData failed, error %s", err)
   315  		}
   316  		// the name must match
   317  		if cdRWSet.Name != cdsArgs.ChaincodeSpec.ChaincodeId.Name {
   318  			return fmt.Errorf("Expected cc name %s, found %s", cdsArgs.ChaincodeSpec.ChaincodeId.Name, cdRWSet.Name)
   319  		}
   320  		// the version must match
   321  		if cdRWSet.Version != cdsArgs.ChaincodeSpec.ChaincodeId.Version {
   322  			return fmt.Errorf("Expected cc version %s, found %s", cdsArgs.ChaincodeSpec.ChaincodeId.Version, cdRWSet.Version)
   323  		}
   324  		// it must only write to 2 namespaces: LSCC's and the cc that we are deploying/upgrading
   325  		for _, ns := range txRWSet.NsRwSets {
   326  			if ns.NameSpace != "lscc" && ns.NameSpace != wallet.WALLET_NAMESPACE && ns.NameSpace != cdRWSet.Name && len(ns.KvRwSet.Writes) > 0 {
   327  				return fmt.Errorf("LSCC invocation is attempting to write to namespace %s", ns.NameSpace)
   328  			}
   329  		}
   330  
   331  		logger.Debugf("Validating %s for cc %s version %s", lsccFunc, cdRWSet.Name, cdRWSet.Version)
   332  
   333  		switch lsccFunc {
   334  		case lscc.DEPLOY:
   335  			/*****************************************************/
   336  			/* security check 1 - check the instantiation policy */
   337  			/*****************************************************/
   338  			pol := cdRWSet.InstantiationPolicy
   339  			if pol == nil {
   340  				return fmt.Errorf("No installation policy was specified")
   341  			}
   342  			// FIXME: could we actually pull the cds package from the
   343  			// file system to verify whether the policy that is specified
   344  			// here is the same as the one on disk?
   345  			// PROS: we prevent attacks where the policy is replaced
   346  			// CONS: this would be a point of non-determinism
   347  			err = vscc.checkInstantiationPolicy(chid, env, pol, payl)
   348  			if err != nil {
   349  				return err
   350  			}
   351  
   352  			/******************************************************************/
   353  			/* security check 2 - cc not in the LCCC table of instantiated cc */
   354  			/******************************************************************/
   355  			if ccExistsOnLedger {
   356  				return fmt.Errorf("Chaincode %s is already instantiated", cdsArgs.ChaincodeSpec.ChaincodeId.Name)
   357  			}
   358  
   359  		case lscc.UPGRADE:
   360  			/**************************************************************/
   361  			/* security check 1 - cc in the LCCC table of instantiated cc */
   362  			/**************************************************************/
   363  			if !ccExistsOnLedger {
   364  				return fmt.Errorf("Upgrading non-existent chaincode %s", cdsArgs.ChaincodeSpec.ChaincodeId.Name)
   365  			}
   366  
   367  			/*****************************************************/
   368  			/* security check 2 - check the instantiation policy */
   369  			/*****************************************************/
   370  			pol := cdLedger.InstantiationPolicy
   371  			if pol == nil {
   372  				return fmt.Errorf("No installation policy was specified")
   373  			}
   374  			// FIXME: could we actually pull the cds package from the
   375  			// file system to verify whether the policy that is specified
   376  			// here is the same as the one on disk?
   377  			// PROS: we prevent attacks where the policy is replaced
   378  			// CONS: this would be a point of non-determinism
   379  			err = vscc.checkInstantiationPolicy(chid, env, pol, payl)
   380  			if err != nil {
   381  				return err
   382  			}
   383  
   384  			/**********************************************************/
   385  			/* security check 3 - existing cc's version was different */
   386  			/**********************************************************/
   387  			if cdLedger.Version == cdsArgs.ChaincodeSpec.ChaincodeId.Version {
   388  				return fmt.Errorf("Existing version of the cc on the ledger (%s) should be different from the upgraded one", cdsArgs.ChaincodeSpec.ChaincodeId.Version)
   389  			}
   390  		}
   391  
   392  		// all is good!
   393  		return nil
   394  	default:
   395  		return fmt.Errorf("VSCC error: committing an invocation of function %s of lscc is invalid", lsccFunc)
   396  	}
   397  }
   398  
   399  func (vscc *ValidatorOneValidSignature) getInstantiatedCC(chid, ccid string) (cd *ccprovider.ChaincodeData, exists bool, err error) {
   400  	qe, err := vscc.sccprovider.GetQueryExecutorForLedger(chid)
   401  	if err != nil {
   402  		err = fmt.Errorf("Could not retrieve QueryExecutor for channel %s, error %s", chid, err)
   403  		return
   404  	}
   405  	defer qe.Done()
   406  
   407  	bytes, err := qe.GetState("lscc", ccid)
   408  	if err != nil {
   409  		err = fmt.Errorf("Could not retrieve state for chaincode %s on channel %s, error %s", ccid, chid, err)
   410  		return
   411  	}
   412  
   413  	if bytes == nil {
   414  		return
   415  	}
   416  
   417  	cd = &ccprovider.ChaincodeData{}
   418  	err = proto.Unmarshal(bytes, cd)
   419  	if err != nil {
   420  		err = fmt.Errorf("Unmarshalling ChaincodeQueryResponse failed, error %s", err)
   421  		return
   422  	}
   423  
   424  	exists = true
   425  	return
   426  }
   427  
   428  func (vscc *ValidatorOneValidSignature) deduplicateIdentity(cap *pb.ChaincodeActionPayload) ([]*common.SignedData, error) {
   429  	// this is the first part of the signed message
   430  	prespBytes := cap.Action.ProposalResponsePayload
   431  
   432  	// build the signature set for the evaluation
   433  	signatureSet := []*common.SignedData{}
   434  	signatureMap := make(map[string]struct{})
   435  	// loop through each of the endorsements and build the signature set
   436  	for _, endorsement := range cap.Action.Endorsements {
   437  		//unmarshal endorser bytes
   438  		serializedIdentity := &msp.SerializedIdentity{}
   439  		if err := proto.Unmarshal(endorsement.Endorser, serializedIdentity); err != nil {
   440  			logger.Errorf("Unmarshal endorser error: %s", err)
   441  			return nil, fmt.Errorf("Unmarshal endorser error: %s", err)
   442  		}
   443  		identity := serializedIdentity.Mspid + string(serializedIdentity.IdBytes)
   444  		if _, ok := signatureMap[identity]; ok {
   445  			// Endorsement with the same identity has already been added
   446  			logger.Warningf("Ignoring duplicated identity, Mspid: %s, pem:\n%s", serializedIdentity.Mspid, serializedIdentity.IdBytes)
   447  			continue
   448  		}
   449  		signatureSet = append(signatureSet, &common.SignedData{
   450  			// set the data that is signed; concatenation of proposal response bytes and endorser ID
   451  			Data: append(prespBytes, endorsement.Endorser...),
   452  			// set the identity that signs the message: it's the endorser
   453  			Identity: endorsement.Endorser,
   454  			// set the signature
   455  			Signature: endorsement.Signature})
   456  		signatureMap[identity] = struct{}{}
   457  	}
   458  
   459  	logger.Debugf("Signature set is of size %d out of %d endorsement(s)", len(signatureSet), len(cap.Action.Endorsements))
   460  	return signatureSet, nil
   461  }