github.com/kchristidis/fabric@v1.0.4-0.20171028114726-837acd08cde1/core/scc/cscc/configure.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 cscc chaincode configer provides functions to manage 18 // configuration transactions as the network is being reconfigured. The 19 // configuration transactions arrive from the ordering service to the committer 20 // who calls this chaincode. The chaincode also provides peer configuration 21 // services such as joining a chain or getting configuration data. 22 package cscc 23 24 import ( 25 "errors" 26 "fmt" 27 28 "github.com/golang/protobuf/proto" 29 "github.com/hyperledger/fabric/common/config" 30 "github.com/hyperledger/fabric/common/flogging" 31 "github.com/hyperledger/fabric/common/policies" 32 "github.com/hyperledger/fabric/core/chaincode/shim" 33 "github.com/hyperledger/fabric/core/peer" 34 "github.com/hyperledger/fabric/core/policy" 35 "github.com/hyperledger/fabric/events/producer" 36 "github.com/hyperledger/fabric/msp/mgmt" 37 "github.com/hyperledger/fabric/protos/common" 38 pb "github.com/hyperledger/fabric/protos/peer" 39 "github.com/hyperledger/fabric/protos/utils" 40 ) 41 42 // PeerConfiger implements the configuration handler for the peer. For every 43 // configuration transaction coming in from the ordering service, the 44 // committer calls this system chaincode to process the transaction. 45 type PeerConfiger struct { 46 policyChecker policy.PolicyChecker 47 } 48 49 var cnflogger = flogging.MustGetLogger("cscc") 50 51 // These are function names from Invoke first parameter 52 const ( 53 JoinChain string = "JoinChain" 54 GetConfigBlock string = "GetConfigBlock" 55 GetChannels string = "GetChannels" 56 ) 57 58 // Init is called once per chain when the chain is created. 59 // This allows the chaincode to initialize any variables on the ledger prior 60 // to any transaction execution on the chain. 61 func (e *PeerConfiger) Init(stub shim.ChaincodeStubInterface) pb.Response { 62 cnflogger.Info("Init CSCC") 63 64 // Init policy checker for access control 65 e.policyChecker = policy.NewPolicyChecker( 66 peer.NewChannelPolicyManagerGetter(), 67 mgmt.GetLocalMSP(), 68 mgmt.NewLocalMSPPrincipalGetter(), 69 ) 70 71 return shim.Success(nil) 72 } 73 74 // Invoke is called for the following: 75 // # to process joining a chain (called by app as a transaction proposal) 76 // # to get the current configuration block (called by app) 77 // # to update the configuration block (called by commmitter) 78 // Peer calls this function with 2 arguments: 79 // # args[0] is the function name, which must be JoinChain, GetConfigBlock or 80 // UpdateConfigBlock 81 // # args[1] is a configuration Block if args[0] is JoinChain or 82 // UpdateConfigBlock; otherwise it is the chain id 83 // TODO: Improve the scc interface to avoid marshal/unmarshal args 84 func (e *PeerConfiger) Invoke(stub shim.ChaincodeStubInterface) pb.Response { 85 args := stub.GetArgs() 86 87 if len(args) < 1 { 88 return shim.Error(fmt.Sprintf("Incorrect number of arguments, %d", len(args))) 89 } 90 91 fname := string(args[0]) 92 93 if fname != GetChannels && len(args) < 2 { 94 return shim.Error(fmt.Sprintf("Incorrect number of arguments, %d", len(args))) 95 } 96 97 cnflogger.Debugf("Invoke function: %s", fname) 98 99 // Handle ACL: 100 // 1. get the signed proposal 101 sp, err := stub.GetSignedProposal() 102 if err != nil { 103 return shim.Error(fmt.Sprintf("Failed getting signed proposal from stub: [%s]", err)) 104 } 105 106 switch fname { 107 case JoinChain: 108 if args[1] == nil { 109 return shim.Error("Cannot join the channel <nil> configuration block provided") 110 } 111 112 block, err := utils.GetBlockFromBlockBytes(args[1]) 113 if err != nil { 114 return shim.Error(fmt.Sprintf("Failed to reconstruct the genesis block, %s", err)) 115 } 116 117 cid, err := utils.GetChainIDFromBlock(block) 118 if err != nil { 119 return shim.Error(fmt.Sprintf("\"JoinChain\" request failed to extract "+ 120 "channel id from the block due to [%s]", err)) 121 } 122 123 if err := validateConfigBlock(block); err != nil { 124 return shim.Error(fmt.Sprintf("\"JoinChain\" for chainID = %s failed because of validation "+ 125 "of configuration block, because of %s", cid, err)) 126 } 127 128 // 2. check local MSP Admins policy 129 if err = e.policyChecker.CheckPolicyNoChannel(mgmt.Admins, sp); err != nil { 130 return shim.Error(fmt.Sprintf("\"JoinChain\" request failed authorization check "+ 131 "for channel [%s]: [%s]", cid, err)) 132 } 133 134 return joinChain(cid, block) 135 case GetConfigBlock: 136 // 2. check the channel reader policy 137 if err = e.policyChecker.CheckPolicy(string(args[1]), policies.ChannelApplicationReaders, sp); err != nil { 138 return shim.Error(fmt.Sprintf("\"GetConfigBlock\" request failed authorization check for channel [%s]: [%s]", args[1], err)) 139 } 140 return getConfigBlock(args[1]) 141 case GetChannels: 142 // 2. check local MSP Members policy 143 if err = e.policyChecker.CheckPolicyNoChannel(mgmt.Members, sp); err != nil { 144 return shim.Error(fmt.Sprintf("\"GetChannels\" request failed authorization check: [%s]", err)) 145 } 146 147 return getChannels() 148 149 } 150 return shim.Error(fmt.Sprintf("Requested function %s not found.", fname)) 151 } 152 153 // validateConfigBlock validate configuration block to see whenever it's contains valid config transaction 154 func validateConfigBlock(block *common.Block) error { 155 envelopeConfig, err := utils.ExtractEnvelope(block, 0) 156 if err != nil { 157 return errors.New(fmt.Sprintf("Failed to %s", err)) 158 } 159 160 configEnv := &common.ConfigEnvelope{} 161 _, err = utils.UnmarshalEnvelopeOfType(envelopeConfig, common.HeaderType_CONFIG, configEnv) 162 if err != nil { 163 return errors.New(fmt.Sprintf("Bad configuration envelope: %s", err)) 164 } 165 166 if configEnv.Config == nil { 167 return errors.New("Nil config envelope Config") 168 } 169 170 if configEnv.Config.ChannelGroup == nil { 171 return errors.New("Nil channel group") 172 } 173 174 if configEnv.Config.ChannelGroup.Groups == nil { 175 return errors.New("No channel configuration groups are available") 176 } 177 178 _, exists := configEnv.Config.ChannelGroup.Groups[config.ApplicationGroupKey] 179 if !exists { 180 return errors.New(fmt.Sprintf("Invalid configuration block, missing %s "+ 181 "configuration group", config.ApplicationGroupKey)) 182 } 183 184 return nil 185 } 186 187 // joinChain will join the specified chain in the configuration block. 188 // Since it is the first block, it is the genesis block containing configuration 189 // for this chain, so we want to update the Chain object with this info 190 func joinChain(chainID string, block *common.Block) pb.Response { 191 if err := peer.CreateChainFromBlock(block); err != nil { 192 return shim.Error(err.Error()) 193 } 194 195 peer.InitChain(chainID) 196 197 if err := producer.SendProducerBlockEvent(block); err != nil { 198 cnflogger.Errorf("Error sending block event %s", err) 199 } 200 201 return shim.Success(nil) 202 } 203 204 // Return the current configuration block for the specified chainID. If the 205 // peer doesn't belong to the chain, return error 206 func getConfigBlock(chainID []byte) pb.Response { 207 if chainID == nil { 208 return shim.Error("ChainID must not be nil.") 209 } 210 block := peer.GetCurrConfigBlock(string(chainID)) 211 if block == nil { 212 return shim.Error(fmt.Sprintf("Unknown chain ID, %s", string(chainID))) 213 } 214 blockBytes, err := utils.Marshal(block) 215 if err != nil { 216 return shim.Error(err.Error()) 217 } 218 219 return shim.Success(blockBytes) 220 } 221 222 // getChannels returns information about all channels for this peer 223 func getChannels() pb.Response { 224 channelInfoArray := peer.GetChannelsInfo() 225 226 // add array with info about all channels for this peer 227 cqr := &pb.ChannelQueryResponse{Channels: channelInfoArray} 228 229 cqrbytes, err := proto.Marshal(cqr) 230 if err != nil { 231 return shim.Error(err.Error()) 232 } 233 234 return shim.Success(cqrbytes) 235 }