configure.go 11.7 KB
Newer Older
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
9
10
11
// configuration transactions as the network is being reconfigured. The
// configuration transactions arrive from the ordering service to the committer
// who calls this chaincode. The chaincode also provides peer configuration
// services such as joining a chain or getting configuration data.
12
package cscc
13
14
15
16

import (
	"fmt"

17
	"github.com/golang/protobuf/proto"
18
	"github.com/hyperledger/fabric/common/channelconfig"
19
	"github.com/hyperledger/fabric/common/config"
20
	"github.com/hyperledger/fabric/common/flogging"
21
	"github.com/hyperledger/fabric/core/aclmgmt"
22
	"github.com/hyperledger/fabric/core/aclmgmt/resources"
23
	"github.com/hyperledger/fabric/core/chaincode/shim"
24
	"github.com/hyperledger/fabric/core/common/sysccprovider"
25
	"github.com/hyperledger/fabric/core/ledger/util"
26
	"github.com/hyperledger/fabric/core/peer"
27
	"github.com/hyperledger/fabric/core/policy"
28
	"github.com/hyperledger/fabric/events/producer"
29
	"github.com/hyperledger/fabric/msp/mgmt"
30
	"github.com/hyperledger/fabric/protos/common"
31
	pb "github.com/hyperledger/fabric/protos/peer"
32
	"github.com/hyperledger/fabric/protos/utils"
33
	"github.com/pkg/errors"
34
35
)

36
37
// New creates a new instance of the CSCC.
// Typically, only one will be created per peer instance.
38
func New(sccp sysccprovider.SystemChaincodeProvider) *PeerConfiger {
39
40
41
42
43
44
45
	return &PeerConfiger{
		policyChecker: policy.NewPolicyChecker(
			peer.NewChannelPolicyManagerGetter(),
			mgmt.GetLocalMSP(),
			mgmt.NewLocalMSPPrincipalGetter(),
		),
		configMgr: peer.NewConfigSupport(),
46
		sccp:      sccp,
47
48
49
50
	}
}

// NewAsChaincode returns a new PeerConfiger as a shim.Chaincode
51
func NewAsChaincode(sccp sysccprovider.SystemChaincodeProvider) shim.Chaincode {
52
	return New(sccp)
53
54
}

55
56
57
58
// PeerConfiger implements the configuration handler for the peer. For every
// configuration transaction coming in from the ordering service, the
// committer calls this system chaincode to process the transaction.
type PeerConfiger struct {
59
	policyChecker policy.PolicyChecker
60
	configMgr     config.Manager
61
	sccp          sysccprovider.SystemChaincodeProvider
62
63
}

64
var cnflogger = flogging.MustGetLogger("cscc")
65
66
67

// These are function names from Invoke first parameter
const (
68
69
70
71
72
	JoinChain                string = "JoinChain"
	GetConfigBlock           string = "GetConfigBlock"
	GetChannels              string = "GetChannels"
	GetConfigTree            string = "GetConfigTree"
	SimulateConfigTreeUpdate string = "SimulateConfigTreeUpdate"
73
74
)

75
// Init is mostly useless from an SCC perspective
76
func (e *PeerConfiger) Init(stub shim.ChaincodeStubInterface) pb.Response {
77
	cnflogger.Info("Init CSCC")
78
	return shim.Success(nil)
79
80
81
82
83
}

// Invoke is called for the following:
// # to process joining a chain (called by app as a transaction proposal)
// # to get the current configuration block (called by app)
84
// # to update the configuration block (called by committer)
85
86
87
88
89
90
// Peer calls this function with 2 arguments:
// # args[0] is the function name, which must be JoinChain, GetConfigBlock or
// UpdateConfigBlock
// # args[1] is a configuration Block if args[0] is JoinChain or
// UpdateConfigBlock; otherwise it is the chain id
// TODO: Improve the scc interface to avoid marshal/unmarshal args
91
func (e *PeerConfiger) Invoke(stub shim.ChaincodeStubInterface) pb.Response {
92
93
	args := stub.GetArgs()

94
	if len(args) < 1 {
95
		return shim.Error(fmt.Sprintf("Incorrect number of arguments, %d", len(args)))
96
	}
97

98
99
	fname := string(args[0])

100
101
102
103
	if fname != GetChannels && len(args) < 2 {
		return shim.Error(fmt.Sprintf("Incorrect number of arguments, %d", len(args)))
	}

104
	cnflogger.Debugf("Invoke function: %s", fname)
105

106
107
108
109
110
111
112
113
114
	// Handle ACL:
	// 1. get the signed proposal
	sp, err := stub.GetSignedProposal()
	if err != nil {
		return shim.Error(fmt.Sprintf("Failed getting signed proposal from stub: [%s]", err))
	}

	switch fname {
	case JoinChain:
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
		if args[1] == nil {
			return shim.Error("Cannot join the channel <nil> configuration block provided")
		}

		block, err := utils.GetBlockFromBlockBytes(args[1])
		if err != nil {
			return shim.Error(fmt.Sprintf("Failed to reconstruct the genesis block, %s", err))
		}

		cid, err := utils.GetChainIDFromBlock(block)
		if err != nil {
			return shim.Error(fmt.Sprintf("\"JoinChain\" request failed to extract "+
				"channel id from the block due to [%s]", err))
		}

		if err := validateConfigBlock(block); err != nil {
			return shim.Error(fmt.Sprintf("\"JoinChain\" for chainID = %s failed because of validation "+
				"of configuration block, because of %s", cid, err))
		}

135
		// 2. check local MSP Admins policy
136
		// TODO: move to ACLProvider once it will support chainless ACLs
137
		if err = e.policyChecker.CheckPolicyNoChannel(mgmt.Admins, sp); err != nil {
138
139
			return shim.Error(fmt.Sprintf("\"JoinChain\" request failed authorization check "+
				"for channel [%s]: [%s]", cid, err))
140
		}
141

142
143
144
145
146
147
148
149
150
		// Initialize txsFilter if it does not yet exist. We can do this safely since
		// it's the genesis block anyway
		txsFilter := util.TxValidationFlags(block.Metadata.Metadata[common.BlockMetadataIndex_TRANSACTIONS_FILTER])
		if len(txsFilter) == 0 {
			// add array of validation code hardcoded to valid
			txsFilter = util.NewTxValidationFlagsSetValue(len(block.Data.Data), pb.TxValidationCode_VALID)
			block.Metadata.Metadata[common.BlockMetadataIndex_TRANSACTIONS_FILTER] = txsFilter
		}

151
		return joinChain(cid, block, e.sccp)
152
	case GetConfigBlock:
153
		// 2. check policy
154
		if err = aclmgmt.GetACLProvider().CheckACL(resources.Cscc_GetConfigBlock, string(args[1]), sp); err != nil {
155
156
			return shim.Error(fmt.Sprintf("\"GetConfigBlock\" request failed authorization check for channel [%s]: [%s]", args[1], err))
		}
157

158
		return getConfigBlock(args[1])
159
160
	case GetConfigTree:
		// 2. check policy
161
		if err = aclmgmt.GetACLProvider().CheckACL(resources.Cscc_GetConfigTree, string(args[1]), sp); err != nil {
162
163
164
165
166
167
			return shim.Error(fmt.Sprintf("\"GetConfigTree\" request failed authorization check for channel [%s]: [%s]", args[1], err))
		}

		return e.getConfigTree(args[1])
	case SimulateConfigTreeUpdate:
		// Check policy
168
		if err = aclmgmt.GetACLProvider().CheckACL(resources.Cscc_SimulateConfigTreeUpdate, string(args[1]), sp); err != nil {
169
170
171
			return shim.Error(fmt.Sprintf("\"SimulateConfigTreeUpdate\" request failed authorization check for channel [%s]: [%s]", args[1], err))
		}
		return e.simulateConfigTreeUpdate(args[1], args[2])
172
173
	case GetChannels:
		// 2. check local MSP Members policy
174
		// TODO: move to ACLProvider once it will support chainless ACLs
175
		if err = e.policyChecker.CheckPolicyNoChannel(mgmt.Members, sp); err != nil {
176
			return shim.Error(fmt.Sprintf("\"GetChannels\" request failed authorization check: [%s]", err))
177
178
		}

179
		return getChannels()
180

181
	}
182
	return shim.Error(fmt.Sprintf("Requested function %s not found.", fname))
183
184
}

185
186
187
// validateConfigBlock validate configuration block to see whenever it's contains valid config transaction
func validateConfigBlock(block *common.Block) error {
	envelopeConfig, err := utils.ExtractEnvelope(block, 0)
188
	if err != nil {
189
		return errors.Errorf("Failed to %s", err)
190
191
	}

192
193
194
	configEnv := &common.ConfigEnvelope{}
	_, err = utils.UnmarshalEnvelopeOfType(envelopeConfig, common.HeaderType_CONFIG, configEnv)
	if err != nil {
195
		return errors.Errorf("Bad configuration envelope: %s", err)
196
197
	}

198
199
	if configEnv.Config == nil {
		return errors.New("Nil config envelope Config")
200
201
	}

202
203
204
	if configEnv.Config.ChannelGroup == nil {
		return errors.New("Nil channel group")
	}
205

206
207
	if configEnv.Config.ChannelGroup.Groups == nil {
		return errors.New("No channel configuration groups are available")
208
209
	}

210
	_, exists := configEnv.Config.ChannelGroup.Groups[channelconfig.ApplicationGroupKey]
211
	if !exists {
212
213
		return errors.Errorf("Invalid configuration block, missing %s "+
			"configuration group", channelconfig.ApplicationGroupKey)
214
215
216
	}

	return nil
217
218
}

219
220
221
// joinChain will join the specified chain in the configuration block.
// Since it is the first block, it is the genesis block containing configuration
// for this chain, so we want to update the Chain object with this info
222
223
func joinChain(chainID string, block *common.Block, sccp sysccprovider.SystemChaincodeProvider) pb.Response {
	if err := peer.CreateChainFromBlock(block, sccp); err != nil {
224
		return shim.Error(err.Error())
225
226
	}

227
228
	peer.InitChain(chainID)

229
230
231
232
233
234
235
	bevent, _, _, err := producer.CreateBlockEvents(block)
	if err != nil {
		cnflogger.Errorf("Error processing block events for block number [%d]: %s", block.Header.Number, err)
	} else {
		if err := producer.Send(bevent); err != nil {
			cnflogger.Errorf("Channel [%s] Error sending block event for block number [%d]: %s", chainID, block.Header.Number, err)
		}
236
237
	}

238
	return shim.Success(nil)
239
240
}

241
242
// Return the current configuration block for the specified chainID. If the
// peer doesn't belong to the chain, return error
243
func getConfigBlock(chainID []byte) pb.Response {
244
	if chainID == nil {
245
		return shim.Error("ChainID must not be nil.")
246
247
248
	}
	block := peer.GetCurrConfigBlock(string(chainID))
	if block == nil {
249
		return shim.Error(fmt.Sprintf("Unknown chain ID, %s", string(chainID)))
250
251
252
	}
	blockBytes, err := utils.Marshal(block)
	if err != nil {
253
		return shim.Error(err.Error())
254
255
	}

256
	return shim.Success(blockBytes)
257
}
258

259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
// getConfigTree returns the current channel and resources configuration for the specified chainID.
// If the peer doesn't belong to the chain, returns error
func (e *PeerConfiger) getConfigTree(chainID []byte) pb.Response {
	if chainID == nil {
		return shim.Error("Chain ID must not be nil")
	}
	channelCfg := e.configMgr.GetChannelConfig(string(chainID)).ConfigProto()
	if channelCfg == nil {
		return shim.Error(fmt.Sprintf("Unknown chain ID, %s", string(chainID)))
	}
	resCfg := e.configMgr.GetResourceConfig(string(chainID)).ConfigProto()
	if resCfg == nil {
		return shim.Error(fmt.Sprintf("Unknown chain ID, %s", string(chainID)))
	}
	agCfg := &pb.ConfigTree{ChannelConfig: channelCfg, ResourcesConfig: resCfg}
	configBytes, err := utils.Marshal(agCfg)
	if err != nil {
		return shim.Error(err.Error())
	}
	return shim.Success(configBytes)
}

func (e *PeerConfiger) simulateConfigTreeUpdate(chainID []byte, envb []byte) pb.Response {
	if chainID == nil {
		return shim.Error("Chain ID must not be nil")
	}
	if envb == nil {
		return shim.Error("Config delta bytes must not be nil")
	}
	env := &common.Envelope{}
	err := proto.Unmarshal(envb, env)
	if err != nil {
		return shim.Error(err.Error())
	}
	cfg, err := supportByType(e, chainID, env)
	if err != nil {
		return shim.Error(err.Error())
	}
	_, err = cfg.ProposeConfigUpdate(env)
	if err != nil {
		return shim.Error(err.Error())
	}
	return shim.Success([]byte("Simulation is successful"))
}

func supportByType(pc *PeerConfiger, chainID []byte, env *common.Envelope) (config.Config, error) {
	payload := &common.Payload{}

	if err := proto.Unmarshal(env.Payload, payload); err != nil {
		return nil, errors.Errorf("failed unmarshaling payload: %v", err)
	}

	channelHdr := &common.ChannelHeader{}
	if err := proto.Unmarshal(payload.Header.ChannelHeader, channelHdr); err != nil {
		return nil, errors.Errorf("failed unmarshaling payload header: %v", err)
	}

	switch common.HeaderType(channelHdr.Type) {
	case common.HeaderType_CONFIG_UPDATE:
		return pc.configMgr.GetChannelConfig(string(chainID)), nil
	case common.HeaderType_PEER_RESOURCE_UPDATE:
		return pc.configMgr.GetResourceConfig(string(chainID)), nil
	}
	return nil, errors.Errorf("invalid payload header type: %d", channelHdr.Type)
}

325
326
327
328
329
330
331
332
333
334
335
336
337
338
// getChannels returns information about all channels for this peer
func getChannels() pb.Response {
	channelInfoArray := peer.GetChannelsInfo()

	// add array with info about all channels for this peer
	cqr := &pb.ChannelQueryResponse{Channels: channelInfoArray}

	cqrbytes, err := proto.Marshal(cqr)
	if err != nil {
		return shim.Error(err.Error())
	}

	return shim.Success(cqrbytes)
}