github.com/yacovm/fabric@v2.0.0-alpha.0.20191128145320-c5d4087dc723+incompatible/core/scc/cscc/configure.go (about) 1 /* 2 Copyright IBM Corp. All Rights Reserved. 3 4 SPDX-License-Identifier: Apache-2.0 5 */ 6 7 // Package cscc chaincode configer provides functions to manage 8 // configuration transactions as the network is being reconfigured. The 9 // configuration transactions arrive from the ordering service to the committer 10 // who calls this chaincode. The chaincode also provides peer configuration 11 // services such as joining a chain or getting configuration data. 12 package cscc 13 14 import ( 15 "fmt" 16 17 "github.com/golang/protobuf/proto" 18 "github.com/hyperledger/fabric-chaincode-go/shim" 19 "github.com/hyperledger/fabric-protos-go/common" 20 pb "github.com/hyperledger/fabric-protos-go/peer" 21 "github.com/hyperledger/fabric/bccsp" 22 "github.com/hyperledger/fabric/common/channelconfig" 23 "github.com/hyperledger/fabric/common/config" 24 "github.com/hyperledger/fabric/common/flogging" 25 "github.com/hyperledger/fabric/core/aclmgmt" 26 "github.com/hyperledger/fabric/core/aclmgmt/resources" 27 "github.com/hyperledger/fabric/core/committer/txvalidator/v20/plugindispatcher" 28 "github.com/hyperledger/fabric/core/ledger" 29 "github.com/hyperledger/fabric/core/ledger/util" 30 "github.com/hyperledger/fabric/core/peer" 31 "github.com/hyperledger/fabric/core/policy" 32 "github.com/hyperledger/fabric/protoutil" 33 "github.com/pkg/errors" 34 ) 35 36 // New creates a new instance of the CSCC. 37 // Typically, only one will be created per peer instance. 38 func New( 39 aclProvider aclmgmt.ACLProvider, 40 deployedCCInfoProvider ledger.DeployedChaincodeInfoProvider, 41 lr plugindispatcher.LifecycleResources, 42 nr plugindispatcher.CollectionAndLifecycleResources, 43 policyChecker policy.PolicyChecker, 44 p *peer.Peer, 45 bccsp bccsp.BCCSP, 46 ) *PeerConfiger { 47 return &PeerConfiger{ 48 policyChecker: policyChecker, 49 configMgr: peer.NewConfigSupport(p), 50 aclProvider: aclProvider, 51 deployedCCInfoProvider: deployedCCInfoProvider, 52 legacyLifecycle: lr, 53 newLifecycle: nr, 54 peer: p, 55 bccsp: bccsp, 56 } 57 } 58 59 func (e *PeerConfiger) Name() string { return "cscc" } 60 func (e *PeerConfiger) Chaincode() shim.Chaincode { return e } 61 62 // PeerConfiger implements the configuration handler for the peer. For every 63 // configuration transaction coming in from the ordering service, the 64 // committer calls this system chaincode to process the transaction. 65 type PeerConfiger struct { 66 policyChecker policy.PolicyChecker 67 configMgr config.Manager 68 aclProvider aclmgmt.ACLProvider 69 deployedCCInfoProvider ledger.DeployedChaincodeInfoProvider 70 legacyLifecycle plugindispatcher.LifecycleResources 71 newLifecycle plugindispatcher.CollectionAndLifecycleResources 72 peer *peer.Peer 73 bccsp bccsp.BCCSP 74 } 75 76 var cnflogger = flogging.MustGetLogger("cscc") 77 78 // These are function names from Invoke first parameter 79 const ( 80 JoinChain string = "JoinChain" 81 GetConfigBlock string = "GetConfigBlock" 82 GetChannels string = "GetChannels" 83 ) 84 85 // Init is mostly useless from an SCC perspective 86 func (e *PeerConfiger) Init(stub shim.ChaincodeStubInterface) pb.Response { 87 cnflogger.Info("Init CSCC") 88 return shim.Success(nil) 89 } 90 91 // Invoke is called for the following: 92 // # to process joining a chain (called by app as a transaction proposal) 93 // # to get the current configuration block (called by app) 94 // # to update the configuration block (called by committer) 95 // Peer calls this function with 2 arguments: 96 // # args[0] is the function name, which must be JoinChain, GetConfigBlock or 97 // UpdateConfigBlock 98 // # args[1] is a configuration Block if args[0] is JoinChain or 99 // UpdateConfigBlock; otherwise it is the chain id 100 // TODO: Improve the scc interface to avoid marshal/unmarshal args 101 func (e *PeerConfiger) Invoke(stub shim.ChaincodeStubInterface) pb.Response { 102 args := stub.GetArgs() 103 104 if len(args) < 1 { 105 return shim.Error(fmt.Sprintf("Incorrect number of arguments, %d", len(args))) 106 } 107 108 fname := string(args[0]) 109 110 if fname != GetChannels && len(args) < 2 { 111 return shim.Error(fmt.Sprintf("Incorrect number of arguments, %d", len(args))) 112 } 113 114 cnflogger.Debugf("Invoke function: %s", fname) 115 116 // Handle ACL: 117 // 1. get the signed proposal 118 sp, err := stub.GetSignedProposal() 119 if err != nil { 120 return shim.Error(fmt.Sprintf("Failed getting signed proposal from stub: [%s]", err)) 121 } 122 123 name, err := protoutil.InvokedChaincodeName(sp.ProposalBytes) 124 if err != nil { 125 return shim.Error(fmt.Sprintf("Failed to identify the called chaincode: %s", err)) 126 } 127 128 if name != e.Name() { 129 return shim.Error(fmt.Sprintf("Rejecting invoke of CSCC from another chaincode, original invocation for '%s'", name)) 130 } 131 132 return e.InvokeNoShim(args, sp) 133 } 134 135 func (e *PeerConfiger) InvokeNoShim(args [][]byte, sp *pb.SignedProposal) pb.Response { 136 var err error 137 fname := string(args[0]) 138 139 switch fname { 140 case JoinChain: 141 if args[1] == nil { 142 return shim.Error("Cannot join the channel <nil> configuration block provided") 143 } 144 145 block, err := protoutil.UnmarshalBlock(args[1]) 146 if err != nil { 147 return shim.Error(fmt.Sprintf("Failed to reconstruct the genesis block, %s", err)) 148 } 149 150 cid, err := protoutil.GetChainIDFromBlock(block) 151 if err != nil { 152 return shim.Error(fmt.Sprintf("\"JoinChain\" request failed to extract "+ 153 "channel id from the block due to [%s]", err)) 154 } 155 156 // 1. check config block's format and capabilities requirement. 157 if err := validateConfigBlock(block, e.bccsp); err != nil { 158 return shim.Error(fmt.Sprintf("\"JoinChain\" for chainID = %s failed because of validation "+ 159 "of configuration block, because of %s", cid, err)) 160 } 161 162 // 2. check join policy. 163 if err = e.aclProvider.CheckACL(resources.Cscc_JoinChain, "", sp); err != nil { 164 return shim.Error(fmt.Sprintf("access denied for [%s][%s]: [%s]", fname, cid, err)) 165 } 166 167 // Initialize txsFilter if it does not yet exist. We can do this safely since 168 // it's the genesis block anyway 169 txsFilter := util.TxValidationFlags(block.Metadata.Metadata[common.BlockMetadataIndex_TRANSACTIONS_FILTER]) 170 if len(txsFilter) == 0 { 171 // add array of validation code hardcoded to valid 172 txsFilter = util.NewTxValidationFlagsSetValue(len(block.Data.Data), pb.TxValidationCode_VALID) 173 block.Metadata.Metadata[common.BlockMetadataIndex_TRANSACTIONS_FILTER] = txsFilter 174 } 175 176 return e.joinChain(cid, block, e.deployedCCInfoProvider, e.legacyLifecycle, e.newLifecycle) 177 case GetConfigBlock: 178 // 2. check policy 179 if err = e.aclProvider.CheckACL(resources.Cscc_GetConfigBlock, string(args[1]), sp); err != nil { 180 return shim.Error(fmt.Sprintf("access denied for [%s][%s]: %s", fname, args[1], err)) 181 } 182 183 return e.getConfigBlock(args[1]) 184 case GetChannels: 185 // 2. check get channels policy 186 if err = e.aclProvider.CheckACL(resources.Cscc_GetChannels, "", sp); err != nil { 187 return shim.Error(fmt.Sprintf("access denied for [%s]: %s", fname, err)) 188 } 189 190 return e.getChannels() 191 192 } 193 return shim.Error(fmt.Sprintf("Requested function %s not found.", fname)) 194 } 195 196 // validateConfigBlock validate configuration block to see whenever it's contains valid config transaction 197 func validateConfigBlock(block *common.Block, bccsp bccsp.BCCSP) error { 198 envelopeConfig, err := protoutil.ExtractEnvelope(block, 0) 199 if err != nil { 200 return errors.Errorf("Failed to %s", err) 201 } 202 203 configEnv := &common.ConfigEnvelope{} 204 _, err = protoutil.UnmarshalEnvelopeOfType(envelopeConfig, common.HeaderType_CONFIG, configEnv) 205 if err != nil { 206 return errors.Errorf("Bad configuration envelope: %s", err) 207 } 208 209 if configEnv.Config == nil { 210 return errors.New("Nil config envelope Config") 211 } 212 213 if configEnv.Config.ChannelGroup == nil { 214 return errors.New("Nil channel group") 215 } 216 217 if configEnv.Config.ChannelGroup.Groups == nil { 218 return errors.New("No channel configuration groups are available") 219 } 220 221 _, exists := configEnv.Config.ChannelGroup.Groups[channelconfig.ApplicationGroupKey] 222 if !exists { 223 return errors.Errorf("Invalid configuration block, missing %s "+ 224 "configuration group", channelconfig.ApplicationGroupKey) 225 } 226 227 // Check the capabilities requirement 228 if err = channelconfig.ValidateCapabilities(block, bccsp); err != nil { 229 return errors.Errorf("Failed capabilities check: [%s]", err) 230 } 231 232 return nil 233 } 234 235 // joinChain will join the specified chain in the configuration block. 236 // Since it is the first block, it is the genesis block containing configuration 237 // for this chain, so we want to update the Chain object with this info 238 func (e *PeerConfiger) joinChain( 239 chainID string, 240 block *common.Block, 241 deployedCCInfoProvider ledger.DeployedChaincodeInfoProvider, 242 lr plugindispatcher.LifecycleResources, 243 nr plugindispatcher.CollectionAndLifecycleResources, 244 ) pb.Response { 245 if err := e.peer.CreateChannel(chainID, block, deployedCCInfoProvider, lr, nr); err != nil { 246 return shim.Error(err.Error()) 247 } 248 249 return shim.Success(nil) 250 } 251 252 // Return the current configuration block for the specified chainID. If the 253 // peer doesn't belong to the chain, return error 254 func (e *PeerConfiger) getConfigBlock(chainID []byte) pb.Response { 255 if chainID == nil { 256 return shim.Error("ChainID must not be nil.") 257 } 258 259 channel := e.peer.Channel(string(chainID)) 260 if channel == nil { 261 return shim.Error(fmt.Sprintf("Unknown chain ID, %s", string(chainID))) 262 } 263 block, err := peer.ConfigBlockFromLedger(channel.Ledger()) 264 if err != nil { 265 return shim.Error(err.Error()) 266 } 267 268 blockBytes, err := protoutil.Marshal(block) 269 if err != nil { 270 return shim.Error(err.Error()) 271 } 272 273 return shim.Success(blockBytes) 274 } 275 276 func (e *PeerConfiger) supportByType(chainID []byte, env *common.Envelope) (config.Config, error) { 277 payload := &common.Payload{} 278 279 if err := proto.Unmarshal(env.Payload, payload); err != nil { 280 return nil, errors.Errorf("failed unmarshaling payload: %v", err) 281 } 282 283 channelHdr := &common.ChannelHeader{} 284 if err := proto.Unmarshal(payload.Header.ChannelHeader, channelHdr); err != nil { 285 return nil, errors.Errorf("failed unmarshaling payload header: %v", err) 286 } 287 288 switch common.HeaderType(channelHdr.Type) { 289 case common.HeaderType_CONFIG_UPDATE: 290 return e.configMgr.GetChannelConfig(string(chainID)), nil 291 } 292 return nil, errors.Errorf("invalid payload header type: %d", channelHdr.Type) 293 } 294 295 // getChannels returns information about all channels for this peer 296 func (e *PeerConfiger) getChannels() pb.Response { 297 channelInfoArray := e.peer.GetChannelsInfo() 298 299 // add array with info about all channels for this peer 300 cqr := &pb.ChannelQueryResponse{Channels: channelInfoArray} 301 302 cqrbytes, err := proto.Marshal(cqr) 303 if err != nil { 304 return shim.Error(err.Error()) 305 } 306 307 return shim.Success(cqrbytes) 308 }