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