-
Notifications
You must be signed in to change notification settings - Fork 8.8k
/
deployedcc_infoprovider.go
352 lines (299 loc) · 13.6 KB
/
deployedcc_infoprovider.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
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
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
/*
Copyright IBM Corp. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/
package lifecycle
import (
"fmt"
"regexp"
"strings"
cb "github.com/hyperledger/fabric-protos-go/common"
"github.com/hyperledger/fabric-protos-go/ledger/rwset/kvrwset"
pb "github.com/hyperledger/fabric-protos-go/peer"
"github.com/hyperledger/fabric/common/policydsl"
"github.com/hyperledger/fabric/common/util"
validationState "github.com/hyperledger/fabric/core/handlers/validation/api/state"
"github.com/hyperledger/fabric/core/ledger"
"github.com/hyperledger/fabric/core/peer"
"github.com/hyperledger/fabric/gossip/privdata"
"github.com/hyperledger/fabric/protoutil"
"github.com/pkg/errors"
)
//go:generate counterfeiter -o mock/legacy_ccinfo.go --fake-name LegacyDeployedCCInfoProvider . LegacyDeployedCCInfoProvider
type LegacyDeployedCCInfoProvider interface {
ledger.DeployedChaincodeInfoProvider
}
const (
LifecycleEndorsementPolicyRef = "/Channel/Application/LifecycleEndorsement"
)
var (
// This is a channel which was created with a lifecycle endorsement policy
LifecycleDefaultEndorsementPolicyBytes = protoutil.MarshalOrPanic(&cb.ApplicationPolicy{
Type: &cb.ApplicationPolicy_ChannelConfigPolicyReference{
ChannelConfigPolicyReference: LifecycleEndorsementPolicyRef,
},
})
)
type ValidatorCommitter struct {
CoreConfig *peer.Config
PrivdataConfig *privdata.PrivdataConfig
Resources *Resources
LegacyDeployedCCInfoProvider LegacyDeployedCCInfoProvider
}
// Namespaces returns the list of namespaces which are relevant to chaincode lifecycle
func (vc *ValidatorCommitter) Namespaces() []string {
return append([]string{LifecycleNamespace}, vc.LegacyDeployedCCInfoProvider.Namespaces()...)
}
var SequenceMatcher = regexp.MustCompile("^" + NamespacesName + "/fields/([^/]+)/Sequence$")
// UpdatedChaincodes returns the chaincodes that are getting updated by the supplied 'stateUpdates'
func (vc *ValidatorCommitter) UpdatedChaincodes(stateUpdates map[string][]*kvrwset.KVWrite) ([]*ledger.ChaincodeLifecycleInfo, error) {
lifecycleInfo := []*ledger.ChaincodeLifecycleInfo{}
// If the lifecycle table was updated, report only modified chaincodes
lifecycleUpdates := stateUpdates[LifecycleNamespace]
for _, kvWrite := range lifecycleUpdates {
matches := SequenceMatcher.FindStringSubmatch(kvWrite.Key)
if len(matches) != 2 {
continue
}
// XXX Note, this may not be a chaincode namespace, handle this later
lifecycleInfo = append(lifecycleInfo, &ledger.ChaincodeLifecycleInfo{Name: matches[1]})
}
legacyUpdates, err := vc.LegacyDeployedCCInfoProvider.UpdatedChaincodes(stateUpdates)
if err != nil {
return nil, errors.WithMessage(err, "error invoking legacy deployed cc info provider")
}
return append(lifecycleInfo, legacyUpdates...), nil
}
func (vc *ValidatorCommitter) ChaincodeInfo(channelName, chaincodeName string, qe ledger.SimpleQueryExecutor) (*ledger.DeployedChaincodeInfo, error) {
exists, definedChaincode, err := vc.Resources.ChaincodeDefinitionIfDefined(chaincodeName, &SimpleQueryExecutorShim{
Namespace: LifecycleNamespace,
SimpleQueryExecutor: qe,
})
if err != nil {
return nil, errors.WithMessage(err, "could not get info about chaincode")
}
if !exists {
return vc.LegacyDeployedCCInfoProvider.ChaincodeInfo(channelName, chaincodeName, qe)
}
return &ledger.DeployedChaincodeInfo{
Name: chaincodeName,
Version: definedChaincode.EndorsementInfo.Version,
Hash: util.ComputeSHA256([]byte(chaincodeName + ":" + definedChaincode.EndorsementInfo.Version)),
ExplicitCollectionConfigPkg: definedChaincode.Collections,
IsLegacy: false,
}, nil
}
var ImplicitCollectionMatcher = regexp.MustCompile("^" + ImplicitCollectionNameForOrg("(.+)") + "$")
// AllCollectionsConfigPkg implements function in interface ledger.DeployedChaincodeInfoProvider
// this implementation returns a combined collection config pkg that contains both explicit and implicit collections
func (vc *ValidatorCommitter) AllCollectionsConfigPkg(channelName, chaincodeName string, qe ledger.SimpleQueryExecutor) (*pb.CollectionConfigPackage, error) {
chaincodeInfo, err := vc.ChaincodeInfo(channelName, chaincodeName, qe)
if err != nil {
return nil, err
}
explicitCollectionConfigPkg := chaincodeInfo.ExplicitCollectionConfigPkg
if chaincodeInfo.IsLegacy {
return explicitCollectionConfigPkg, nil
}
implicitCollections, err := vc.ImplicitCollections(channelName, chaincodeName, qe)
if err != nil {
return nil, err
}
var combinedColls []*pb.CollectionConfig
if explicitCollectionConfigPkg != nil {
combinedColls = append(combinedColls, explicitCollectionConfigPkg.Config...)
}
for _, implicitColl := range implicitCollections {
c := &pb.CollectionConfig{}
c.Payload = &pb.CollectionConfig_StaticCollectionConfig{StaticCollectionConfig: implicitColl}
combinedColls = append(combinedColls, c)
}
return &pb.CollectionConfigPackage{
Config: combinedColls,
}, nil
}
// CollectionInfo implements function in interface ledger.DeployedChaincodeInfoProvider, it returns config for
// both static and implicit collections.
func (vc *ValidatorCommitter) CollectionInfo(channelName, chaincodeName, collectionName string, qe ledger.SimpleQueryExecutor) (*pb.StaticCollectionConfig, error) {
exists, definedChaincode, err := vc.Resources.ChaincodeDefinitionIfDefined(chaincodeName, &SimpleQueryExecutorShim{
Namespace: LifecycleNamespace,
SimpleQueryExecutor: qe,
})
if err != nil {
return nil, errors.WithMessage(err, "could not get chaincode")
}
if !exists {
return vc.LegacyDeployedCCInfoProvider.CollectionInfo(channelName, chaincodeName, collectionName, qe)
}
matches := ImplicitCollectionMatcher.FindStringSubmatch(collectionName)
if len(matches) == 2 {
return vc.GenerateImplicitCollectionForOrg(matches[1]), nil
}
if definedChaincode.Collections != nil {
for _, conf := range definedChaincode.Collections.Config {
staticCollConfig := conf.GetStaticCollectionConfig()
if staticCollConfig != nil && staticCollConfig.Name == collectionName {
return staticCollConfig, nil
}
}
}
return nil, nil
}
// ImplicitCollections implements function in interface ledger.DeployedChaincodeInfoProvider. It returns
//a slice that contains one proto msg for each of the implicit collections
func (vc *ValidatorCommitter) ImplicitCollections(channelName, chaincodeName string, qe ledger.SimpleQueryExecutor) ([]*pb.StaticCollectionConfig, error) {
exists, _, err := vc.Resources.ChaincodeDefinitionIfDefined(chaincodeName, &SimpleQueryExecutorShim{
Namespace: LifecycleNamespace,
SimpleQueryExecutor: qe,
})
if err != nil {
return nil, errors.WithMessage(err, "could not get info about chaincode")
}
if !exists {
// Implicit collections are a v2.0 lifecycle concept, if the chaincode is not in the new lifecycle, return nothing
return nil, nil
}
return vc.ChaincodeImplicitCollections(channelName)
}
// ChaincodeImplicitCollections assumes the chaincode exists in the new lifecycle and returns the implicit collections
func (vc *ValidatorCommitter) ChaincodeImplicitCollections(channelName string) ([]*pb.StaticCollectionConfig, error) {
channelConfig := vc.Resources.ChannelConfigSource.GetStableChannelConfig(channelName)
if channelConfig == nil {
return nil, errors.Errorf("could not get channelconfig for channel %s", channelName)
}
ac, ok := channelConfig.ApplicationConfig()
if !ok {
return nil, errors.Errorf("could not get application config for channel %s", channelName)
}
orgs := ac.Organizations()
implicitCollections := make([]*pb.StaticCollectionConfig, 0, len(orgs))
for _, org := range orgs {
implicitCollections = append(implicitCollections, vc.GenerateImplicitCollectionForOrg(org.MSPID()))
}
return implicitCollections, nil
}
// GenerateImplicitCollectionForOrg generates implicit collection for the org
func (vc *ValidatorCommitter) GenerateImplicitCollectionForOrg(mspid string) *pb.StaticCollectionConfig {
// set Required/MaxPeerCount to 0 if it is other org's implicit collection (mspid does not match peer's local mspid)
// set Required/MaxPeerCount to the config values if it is the peer org's implicit collection (mspid matches peer's local mspid)
requiredPeerCount := 0
maxPeerCount := 0
if mspid == vc.CoreConfig.LocalMSPID {
requiredPeerCount = vc.PrivdataConfig.ImplicitCollDisseminationPolicy.RequiredPeerCount
maxPeerCount = vc.PrivdataConfig.ImplicitCollDisseminationPolicy.MaxPeerCount
}
return &pb.StaticCollectionConfig{
Name: ImplicitCollectionNameForOrg(mspid),
MemberOrgsPolicy: &pb.CollectionPolicyConfig{
Payload: &pb.CollectionPolicyConfig_SignaturePolicy{
SignaturePolicy: policydsl.SignedByMspMember(mspid),
},
},
RequiredPeerCount: int32(requiredPeerCount),
MaximumPeerCount: int32(maxPeerCount),
}
}
func ImplicitCollectionNameForOrg(mspid string) string {
return fmt.Sprintf("_implicit_org_%s", mspid)
}
func OrgFromImplicitCollectionName(name string) string {
return strings.TrimPrefix(name, "_implicit_org_")
}
func (vc *ValidatorCommitter) ImplicitCollectionEndorsementPolicyAsBytes(channelID, orgMSPID string) (policy []byte, unexpectedErr, validationErr error) {
channelConfig := vc.Resources.ChannelConfigSource.GetStableChannelConfig(channelID)
if channelConfig == nil {
return nil, errors.Errorf("could not get channel config for channel '%s'", channelID), nil
}
ac, ok := channelConfig.ApplicationConfig()
if !ok {
return nil, errors.Errorf("could not get application config for channel '%s'", channelID), nil
}
matchedOrgName := ""
for orgName, org := range ac.Organizations() {
if org.MSPID() == orgMSPID {
matchedOrgName = orgName
break
}
}
if matchedOrgName == "" {
return nil, nil, errors.Errorf("no org found in channel with MSPID '%s'", orgMSPID)
}
policyName := fmt.Sprintf("/Channel/Application/%s/Endorsement", matchedOrgName)
if _, ok := channelConfig.PolicyManager().GetPolicy(policyName); ok {
return protoutil.MarshalOrPanic(&cb.ApplicationPolicy{
Type: &cb.ApplicationPolicy_ChannelConfigPolicyReference{
ChannelConfigPolicyReference: policyName,
},
}), nil, nil
}
// This was a channel which was upgraded or did not define an org level endorsement policy, use a default
// of "any member of the org"
return protoutil.MarshalOrPanic(&cb.ApplicationPolicy{
Type: &cb.ApplicationPolicy_SignaturePolicy{
SignaturePolicy: policydsl.SignedByAnyMember([]string{orgMSPID}),
},
}), nil, nil
}
// ValidationInfo returns the name and arguments of the validation plugin for the supplied
// chaincode. The function returns two types of errors, unexpected errors and validation
// errors. The reason for this is that this function is called from the validation code,
// which needs to differentiate the two types of error to halt processing on the channel
// if the unexpected error is not nil and mark the transaction as invalid if the validation
// error is not nil.
func (vc *ValidatorCommitter) ValidationInfo(channelID, chaincodeName string, qe ledger.SimpleQueryExecutor) (plugin string, args []byte, unexpectedErr error, validationErr error) {
// TODO, this is a bit of an overkill check, and will need to be scaled back for non-chaincode type namespaces
exists, definedChaincode, err := vc.Resources.ChaincodeDefinitionIfDefined(chaincodeName, &SimpleQueryExecutorShim{
Namespace: LifecycleNamespace,
SimpleQueryExecutor: qe,
})
if err != nil {
return "", nil, errors.WithMessage(err, "could not get chaincode"), nil
}
if !exists {
// TODO, this is inconsistent with how the legacy lifecycle reports
// that a missing chaincode is a validation error. But, for now
// this is required to make the passthrough work.
return "", nil, nil, nil
}
if chaincodeName == LifecycleNamespace {
b, err := vc.Resources.LifecycleEndorsementPolicyAsBytes(channelID)
if err != nil {
return "", nil, errors.WithMessage(err, "unexpected failure to create lifecycle endorsement policy"), nil
}
return "vscc", b, nil, nil
}
return definedChaincode.ValidationInfo.ValidationPlugin, definedChaincode.ValidationInfo.ValidationParameter, nil, nil
}
// CollectionValidationInfo returns information about collections to the validation component
func (vc *ValidatorCommitter) CollectionValidationInfo(channelID, chaincodeName, collectionName string, state validationState.State) (args []byte, unexpectedErr, validationErr error) {
exists, definedChaincode, err := vc.Resources.ChaincodeDefinitionIfDefined(chaincodeName, &ValidatorStateShim{
Namespace: LifecycleNamespace,
ValidatorState: state,
})
if err != nil {
return nil, errors.WithMessage(err, "could not get chaincode"), nil
}
if !exists {
// TODO, this is inconsistent with how the legacy lifecycle reports
// that a missing chaincode is a validation error. But, for now
// this is required to make the passthrough work.
return nil, nil, nil
}
matches := ImplicitCollectionMatcher.FindStringSubmatch(collectionName)
if len(matches) == 2 {
return vc.ImplicitCollectionEndorsementPolicyAsBytes(channelID, matches[1])
}
if definedChaincode.Collections != nil {
for _, conf := range definedChaincode.Collections.Config {
staticCollConfig := conf.GetStaticCollectionConfig()
if staticCollConfig != nil && staticCollConfig.Name == collectionName {
if staticCollConfig.EndorsementPolicy != nil {
return protoutil.MarshalOrPanic(staticCollConfig.EndorsementPolicy), nil, nil
}
// default to chaincode endorsement policy
return definedChaincode.ValidationInfo.ValidationParameter, nil, nil
}
}
}
return nil, nil, errors.Errorf("no such collection '%s'", collectionName)
}