github.com/ewagmig/fabric@v2.1.1+incompatible/core/committer/txvalidator/v14/vscc_validator.go (about) 1 /* 2 Copyright IBM Corp. All Rights Reserved. 3 4 SPDX-License-Identifier: Apache-2.0 5 */ 6 7 package txvalidator 8 9 import ( 10 "fmt" 11 12 "github.com/golang/protobuf/proto" 13 "github.com/hyperledger/fabric-protos-go/common" 14 "github.com/hyperledger/fabric-protos-go/peer" 15 commonerrors "github.com/hyperledger/fabric/common/errors" 16 "github.com/hyperledger/fabric/common/policydsl" 17 "github.com/hyperledger/fabric/core/common/ccprovider" 18 "github.com/hyperledger/fabric/core/common/sysccprovider" 19 validation "github.com/hyperledger/fabric/core/handlers/validation/api" 20 "github.com/hyperledger/fabric/core/ledger/kvledger/txmgmt/rwsetutil" 21 "github.com/hyperledger/fabric/protoutil" 22 "github.com/pkg/errors" 23 ) 24 25 // VsccValidatorImpl is the implementation used to call 26 // the vscc chaincode and validate block transactions 27 type VsccValidatorImpl struct { 28 channelID string 29 cr ChannelResources 30 pluginValidator *PluginValidator 31 } 32 33 // newVSCCValidator creates new vscc validator 34 func newVSCCValidator(channelID string, cr ChannelResources, pluginValidator *PluginValidator) *VsccValidatorImpl { 35 return &VsccValidatorImpl{ 36 channelID: channelID, 37 cr: cr, 38 pluginValidator: pluginValidator, 39 } 40 } 41 42 func getChaincodeHeaderExtension(hdr *common.Header) (*peer.ChaincodeHeaderExtension, error) { 43 chdr, err := protoutil.UnmarshalChannelHeader(hdr.ChannelHeader) 44 if err != nil { 45 return nil, err 46 } 47 48 chaincodeHdrExt := &peer.ChaincodeHeaderExtension{} 49 err = proto.Unmarshal(chdr.Extension, chaincodeHdrExt) 50 return chaincodeHdrExt, errors.Wrap(err, "error unmarshaling ChaincodeHeaderExtension") 51 } 52 53 // VSCCValidateTx executes vscc validation for transaction 54 func (v *VsccValidatorImpl) VSCCValidateTx(seq int, payload *common.Payload, envBytes []byte, block *common.Block) (error, peer.TxValidationCode) { 55 chainID := v.channelID 56 logger.Debugf("[%s] VSCCValidateTx starts for bytes %p", chainID, envBytes) 57 58 // get header extensions so we have the chaincode ID 59 hdrExt, err := getChaincodeHeaderExtension(payload.Header) 60 if err != nil { 61 return err, peer.TxValidationCode_BAD_HEADER_EXTENSION 62 } 63 64 // get channel header 65 chdr, err := protoutil.UnmarshalChannelHeader(payload.Header.ChannelHeader) 66 if err != nil { 67 return err, peer.TxValidationCode_BAD_CHANNEL_HEADER 68 } 69 70 /* obtain the list of namespaces we're writing stuff to; 71 at first, we establish a few facts about this invocation: 72 1) which namespaces does it write to? 73 2) does it write to LSCC's namespace? 74 3) does it write to any cc that cannot be invoked? */ 75 writesToLSCC := false 76 writesToNonInvokableSCC := false 77 respPayload, err := protoutil.GetActionFromEnvelope(envBytes) 78 if err != nil { 79 return errors.WithMessage(err, "GetActionFromEnvelope failed"), peer.TxValidationCode_BAD_RESPONSE_PAYLOAD 80 } 81 txRWSet := &rwsetutil.TxRwSet{} 82 if err = txRWSet.FromProtoBytes(respPayload.Results); err != nil { 83 return errors.WithMessage(err, "txRWSet.FromProtoBytes failed"), peer.TxValidationCode_BAD_RWSET 84 } 85 86 // Verify the header extension and response payload contain the ChaincodeId 87 if hdrExt.ChaincodeId == nil { 88 return errors.New("nil ChaincodeId in header extension"), peer.TxValidationCode_INVALID_OTHER_REASON 89 } 90 91 if respPayload.ChaincodeId == nil { 92 return errors.New("nil ChaincodeId in ChaincodeAction"), peer.TxValidationCode_INVALID_OTHER_REASON 93 } 94 95 // get name and version of the cc we invoked 96 ccID := hdrExt.ChaincodeId.Name 97 ccVer := respPayload.ChaincodeId.Version 98 99 // sanity check on ccID 100 if ccID == "" { 101 err = errors.New("invalid chaincode ID") 102 logger.Errorf("%+v", err) 103 return err, peer.TxValidationCode_INVALID_OTHER_REASON 104 } 105 if ccID != respPayload.ChaincodeId.Name { 106 err = errors.Errorf("inconsistent ccid info (%s/%s)", ccID, respPayload.ChaincodeId.Name) 107 logger.Errorf("%+v", err) 108 return err, peer.TxValidationCode_INVALID_OTHER_REASON 109 } 110 // sanity check on ccver 111 if ccVer == "" { 112 err = errors.New("invalid chaincode version") 113 logger.Errorf("%+v", err) 114 return err, peer.TxValidationCode_INVALID_OTHER_REASON 115 } 116 117 var wrNamespace []string 118 alwaysEnforceOriginalNamespace := v.cr.Capabilities().V1_2Validation() 119 if alwaysEnforceOriginalNamespace { 120 wrNamespace = append(wrNamespace, ccID) 121 if respPayload.Events != nil { 122 ccEvent := &peer.ChaincodeEvent{} 123 if err = proto.Unmarshal(respPayload.Events, ccEvent); err != nil { 124 return errors.Wrapf(err, "invalid chaincode event"), peer.TxValidationCode_INVALID_OTHER_REASON 125 } 126 if ccEvent.ChaincodeId != ccID { 127 return errors.Errorf("chaincode event chaincode id does not match chaincode action chaincode id"), peer.TxValidationCode_INVALID_OTHER_REASON 128 } 129 } 130 } 131 132 namespaces := make(map[string]struct{}) 133 for _, ns := range txRWSet.NsRwSets { 134 // check to make sure there is no duplicate namespace in txRWSet 135 if _, ok := namespaces[ns.NameSpace]; ok { 136 return errors.Errorf("duplicate namespace '%s' in txRWSet", ns.NameSpace), 137 peer.TxValidationCode_ILLEGAL_WRITESET 138 } 139 namespaces[ns.NameSpace] = struct{}{} 140 141 if !v.txWritesToNamespace(ns) { 142 continue 143 } 144 145 // Check to make sure we did not already populate this chaincode 146 // name to avoid checking the same namespace twice 147 if ns.NameSpace != ccID || !alwaysEnforceOriginalNamespace { 148 wrNamespace = append(wrNamespace, ns.NameSpace) 149 } 150 151 if !writesToLSCC && ns.NameSpace == "lscc" { 152 writesToLSCC = true 153 } 154 155 if !writesToNonInvokableSCC && IsSysCCAndNotInvokableCC2CC(ns.NameSpace) { 156 writesToNonInvokableSCC = true 157 } 158 159 if !writesToNonInvokableSCC && IsSysCCAndNotInvokableExternal(ns.NameSpace) { 160 writesToNonInvokableSCC = true 161 } 162 } 163 164 // we've gathered all the info required to proceed to validation; 165 // validation will behave differently depending on the type of 166 // chaincode (system vs. application) 167 168 if !IsSysCC(ccID) { 169 // if we're here, we know this is an invocation of an application chaincode; 170 // first of all, we make sure that: 171 // 1) we don't write to LSCC - an application chaincode is free to invoke LSCC 172 // for instance to get information about itself or another chaincode; however 173 // these legitimate invocations only ready from LSCC's namespace; currently 174 // only two functions of LSCC write to its namespace: deploy and upgrade and 175 // neither should be used by an application chaincode 176 if writesToLSCC { 177 return errors.Errorf("chaincode %s attempted to write to the namespace of LSCC", ccID), 178 peer.TxValidationCode_ILLEGAL_WRITESET 179 } 180 // 2) we don't write to the namespace of a chaincode that we cannot invoke - if 181 // the chaincode cannot be invoked in the first place, there's no legitimate 182 // way in which a transaction has a write set that writes to it; additionally 183 // we don't have any means of verifying whether the transaction had the rights 184 // to perform that write operation because in v1, system chaincodes do not have 185 // any endorsement policies to speak of. So if the chaincode can't be invoked 186 // it can't be written to by an invocation of an application chaincode 187 if writesToNonInvokableSCC { 188 return errors.Errorf("chaincode %s attempted to write to the namespace of a system chaincode that cannot be invoked", ccID), 189 peer.TxValidationCode_ILLEGAL_WRITESET 190 } 191 192 // validate *EACH* read write set according to its chaincode's endorsement policy 193 for _, ns := range wrNamespace { 194 // Get latest chaincode version, vscc and validate policy 195 txcc, vscc, policy, err := v.GetInfoForValidate(chdr, ns) 196 if err != nil { 197 logger.Errorf("GetInfoForValidate for txId = %s returned error: %+v", chdr.TxId, err) 198 return err, peer.TxValidationCode_INVALID_OTHER_REASON 199 } 200 201 // if the namespace corresponds to the cc that was originally 202 // invoked, we check that the version of the cc that was 203 // invoked corresponds to the version that lscc has returned 204 if ns == ccID && txcc.ChaincodeVersion != ccVer { 205 err = errors.Errorf("chaincode %s:%s/%s didn't match %s:%s/%s in lscc", ccID, ccVer, chdr.ChannelId, txcc.ChaincodeName, txcc.ChaincodeVersion, chdr.ChannelId) 206 logger.Errorf("%+v", err) 207 return err, peer.TxValidationCode_EXPIRED_CHAINCODE 208 } 209 210 // do VSCC validation 211 ctx := &Context{ 212 Seq: seq, 213 Envelope: envBytes, 214 Block: block, 215 TxID: chdr.TxId, 216 Channel: chdr.ChannelId, 217 Namespace: ns, 218 Policy: policy, 219 VSCCName: vscc.ChaincodeName, 220 } 221 if err = v.VSCCValidateTxForCC(ctx); err != nil { 222 switch err.(type) { 223 case *commonerrors.VSCCEndorsementPolicyError: 224 return err, peer.TxValidationCode_ENDORSEMENT_POLICY_FAILURE 225 default: 226 return err, peer.TxValidationCode_INVALID_OTHER_REASON 227 } 228 } 229 } 230 } else { 231 // make sure that we can invoke this system chaincode - if the chaincode 232 // cannot be invoked through a proposal to this peer, we have to drop the 233 // transaction; if we didn't, we wouldn't know how to decide whether it's 234 // valid or not because in v1, system chaincodes have no endorsement policy 235 if IsSysCCAndNotInvokableExternal(ccID) { 236 return errors.Errorf("committing an invocation of cc %s is illegal", ccID), 237 peer.TxValidationCode_ILLEGAL_WRITESET 238 } 239 240 // Get latest chaincode version, vscc and validate policy 241 _, vscc, policy, err := v.GetInfoForValidate(chdr, ccID) 242 if err != nil { 243 logger.Errorf("GetInfoForValidate for txId = %s returned error: %+v", chdr.TxId, err) 244 return err, peer.TxValidationCode_INVALID_OTHER_REASON 245 } 246 247 // validate the transaction as an invocation of this system chaincode; 248 // vscc will have to do custom validation for this system chaincode 249 // currently, VSCC does custom validation for LSCC only; if an hlf 250 // user creates a new system chaincode which is invokable from the outside 251 // they have to modify VSCC to provide appropriate validation 252 ctx := &Context{ 253 Seq: seq, 254 Envelope: envBytes, 255 Block: block, 256 TxID: chdr.TxId, 257 Channel: chdr.ChannelId, 258 Namespace: ccID, 259 Policy: policy, 260 VSCCName: vscc.ChaincodeName, 261 } 262 if err = v.VSCCValidateTxForCC(ctx); err != nil { 263 switch err.(type) { 264 case *commonerrors.VSCCEndorsementPolicyError: 265 return err, peer.TxValidationCode_ENDORSEMENT_POLICY_FAILURE 266 default: 267 return err, peer.TxValidationCode_INVALID_OTHER_REASON 268 } 269 } 270 } 271 logger.Debugf("[%s] VSCCValidateTx completes env bytes %p", chainID, envBytes) 272 return nil, peer.TxValidationCode_VALID 273 } 274 275 func (v *VsccValidatorImpl) VSCCValidateTxForCC(ctx *Context) error { 276 logger.Debug("Validating", ctx, "with plugin") 277 err := v.pluginValidator.ValidateWithPlugin(ctx) 278 if err == nil { 279 return nil 280 } 281 // If the error is a pluggable validation execution error, cast it to the common errors ExecutionFailureError. 282 if e, isExecutionError := err.(*validation.ExecutionFailureError); isExecutionError { 283 return &commonerrors.VSCCExecutionFailureError{Err: e} 284 } 285 // Else, treat it as an endorsement error. 286 return &commonerrors.VSCCEndorsementPolicyError{Err: err} 287 } 288 289 func (v *VsccValidatorImpl) getCDataForCC(chid, ccid string) (*ccprovider.ChaincodeData, error) { 290 l := v.cr.Ledger() 291 if l == nil { 292 return nil, errors.New("nil ledger instance") 293 } 294 295 qe, err := l.NewQueryExecutor() 296 if err != nil { 297 return nil, errors.WithMessage(err, "could not retrieve QueryExecutor") 298 } 299 defer qe.Done() 300 301 bytes, err := qe.GetState("lscc", ccid) 302 if err != nil { 303 return nil, &commonerrors.VSCCInfoLookupFailureError{ 304 Reason: fmt.Sprintf("Could not retrieve state for chaincode %s, error %s", ccid, err), 305 } 306 } 307 308 if bytes == nil { 309 return nil, errors.Errorf("lscc's state for [%s] not found.", ccid) 310 } 311 312 cd := &ccprovider.ChaincodeData{} 313 err = proto.Unmarshal(bytes, cd) 314 if err != nil { 315 return nil, errors.Wrap(err, "unmarshalling ChaincodeQueryResponse failed") 316 } 317 318 if cd.Vscc == "" { 319 return nil, errors.Errorf("lscc's state for [%s] is invalid, vscc field must be set", ccid) 320 } 321 322 if len(cd.Policy) == 0 { 323 return nil, errors.Errorf("lscc's state for [%s] is invalid, policy field must be set", ccid) 324 } 325 326 return cd, err 327 } 328 329 // GetInfoForValidate gets the ChaincodeInstance(with latest version) of tx, vscc and policy from lscc 330 func (v *VsccValidatorImpl) GetInfoForValidate(chdr *common.ChannelHeader, ccID string) (*sysccprovider.ChaincodeInstance, *sysccprovider.ChaincodeInstance, []byte, error) { 331 cc := &sysccprovider.ChaincodeInstance{ 332 ChannelID: chdr.ChannelId, 333 ChaincodeName: ccID, 334 } 335 vscc := &sysccprovider.ChaincodeInstance{ 336 ChannelID: chdr.ChannelId, 337 ChaincodeName: "vscc", // default vscc for system chaincodes 338 } 339 var policy []byte 340 var err error 341 if !IsSysCC(ccID) { 342 // when we are validating a chaincode that is not a 343 // system CC, we need to ask the CC to give us the name 344 // of VSCC and of the policy that should be used 345 346 // obtain name of the VSCC and the policy 347 cd, err := v.getCDataForCC(chdr.ChannelId, ccID) 348 if err != nil { 349 msg := fmt.Sprintf("Unable to get chaincode data from ledger for txid %s, due to %s", chdr.TxId, err) 350 logger.Errorf(msg) 351 return nil, nil, nil, err 352 } 353 cc.ChaincodeName = cd.Name 354 cc.ChaincodeVersion = cd.Version 355 vscc.ChaincodeName, policy = cd.Vscc, cd.Policy 356 } else { 357 // when we are validating a system CC, we use the default 358 // VSCC and a default policy that requires one signature 359 // from any of the members of the channel 360 p := policydsl.SignedByAnyMember(v.cr.GetMSPIDs()) 361 policy, err = protoutil.Marshal(p) 362 if err != nil { 363 return nil, nil, nil, err 364 } 365 } 366 367 return cc, vscc, policy, nil 368 } 369 370 // txWritesToNamespace returns true if the supplied NsRwSet 371 // performs a ledger write 372 func (v *VsccValidatorImpl) txWritesToNamespace(ns *rwsetutil.NsRwSet) bool { 373 // check for public writes first 374 if ns.KvRwSet != nil && len(ns.KvRwSet.Writes) > 0 { 375 return true 376 } 377 378 // only look at collection data if we support that capability 379 if v.cr.Capabilities().PrivateChannelData() { 380 // check for private writes for all collections 381 for _, c := range ns.CollHashedRwSets { 382 if c.HashedRwSet != nil && len(c.HashedRwSet.HashedWrites) > 0 { 383 return true 384 } 385 386 // only look at private metadata writes if we support that capability 387 if v.cr.Capabilities().KeyLevelEndorsement() { 388 // private metadata updates 389 if c.HashedRwSet != nil && len(c.HashedRwSet.MetadataWrites) > 0 { 390 return true 391 } 392 } 393 } 394 } 395 396 // only look at metadata writes if we support that capability 397 if v.cr.Capabilities().KeyLevelEndorsement() { 398 // public metadata updates 399 if ns.KvRwSet != nil && len(ns.KvRwSet.MetadataWrites) > 0 { 400 return true 401 } 402 } 403 404 return false 405 } 406 407 func IsSysCCAndNotInvokableExternal(name string) bool { 408 return name == "vscc" || name == "escc" 409 } 410 411 func IsSysCC(name string) bool { 412 return name == "vscc" || name == "escc" || name == "lscc" || name == "qscc" || name == "cscc" 413 } 414 415 func IsSysCCAndNotInvokableCC2CC(name string) bool { 416 return name == "vscc" || name == "escc" || name == "cscc" 417 }