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 }