forked from hyperledger/fabric
-
Notifications
You must be signed in to change notification settings - Fork 3
/
endorsment_info.go
139 lines (113 loc) · 5.48 KB
/
endorsment_info.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
/*
Copyright IBM Corp. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/
package lifecycle
import (
"github.com/hyperledger/fabric/core/ledger"
"github.com/hyperledger/fabric/core/scc"
"github.com/pkg/errors"
)
//go:generate counterfeiter -o mock/legacy_lifecycle.go --fake-name LegacyLifecycle . Lifecycle
// Lifecycle is the interface which the core/chaincode package and core/endorser package requires that lifecycle satisfy.
type Lifecycle interface {
ChaincodeEndorsementInfo(channelID, chaincodeName string, qe ledger.SimpleQueryExecutor) (*ChaincodeEndorsementInfo, error)
}
//go:generate counterfeiter -o mock/chaincode_info_cache.go --fake-name ChaincodeInfoCache . ChaincodeInfoCache
type ChaincodeInfoCache interface {
ChaincodeInfo(channelID, chaincodeName string) (definition *LocalChaincodeInfo, err error)
}
// ChaincodeEndorsementInfo contains the information necessary to handle a chaincode invoke request.
type ChaincodeEndorsementInfo struct {
// Version is the version from the definition in this particular channel and namespace context.
Version string
// EnforceInit is set to true for definitions which require the chaincode package to enforce
// 'init exactly once' semantics.
EnforceInit bool
// ChaincodeID is the name by which to look up or launch the underlying chaincode.
ChaincodeID string
// EndorsementPlugin is the name of the plugin to use when endorsing.
EndorsementPlugin string
}
type ChaincodeEndorsementInfoSource struct {
Resources *Resources
Cache ChaincodeInfoCache
LegacyImpl Lifecycle
BuiltinSCCs scc.BuiltinSCCs
}
func (cei *ChaincodeEndorsementInfoSource) CachedChaincodeInfo(channelID, chaincodeName string, qe ledger.SimpleQueryExecutor) (*LocalChaincodeInfo, bool, error) {
var qes ReadableState = &SimpleQueryExecutorShim{
Namespace: LifecycleNamespace,
SimpleQueryExecutor: qe,
}
if qe == nil {
// NOTE: the core/chaincode package inconsistently sets the
// query executor depending on whether the call has a channel
// context or not. We use this dummy shim which always returns
// an error for GetState calls to avoid a peer panic.
qes = &DummyQueryExecutorShim{}
}
currentSequence, err := cei.Resources.Serializer.DeserializeFieldAsInt64(NamespacesName, chaincodeName, "Sequence", qes)
if err != nil {
return nil, false, errors.WithMessagef(err, "could not get current sequence for chaincode '%s' on channel '%s'", chaincodeName, channelID)
}
// Committed sequences begin at 1
if currentSequence == 0 {
return nil, false, nil
}
chaincodeInfo, err := cei.Cache.ChaincodeInfo(channelID, chaincodeName)
if err != nil {
return nil, false, errors.WithMessage(err, "could not get approved chaincode info from cache")
}
if chaincodeInfo.Definition.Sequence != currentSequence {
// TODO this is a transient error which indicates that this query executor is executing against a chaincode
// whose definition has already changed (the cache may be ahead of the committed state, but never behind). In this
// case, we should simply abort the tx, and re-acquire a query executor and re-execute. There is no reason this
// error needs to be returned to the client.
return nil, false, errors.Errorf("chaincode cache at sequence %d but current sequence is %d, chaincode definition for '%s' changed during invoke", chaincodeInfo.Definition.Sequence, currentSequence, chaincodeName)
}
if !chaincodeInfo.Approved {
return nil, false, errors.Errorf("chaincode definition for '%s' at sequence %d on channel '%s' has not yet been approved by this org", chaincodeName, currentSequence, channelID)
}
if chaincodeInfo.InstallInfo == nil {
return nil, false, errors.Errorf("chaincode definition for '%s' exists, but chaincode is not installed", chaincodeName)
}
return chaincodeInfo, true, nil
}
// ChaincodeEndorsementInfo returns the information necessary to handle a chaincode invocation request, as well as a function to
// enforce security checks on the chaincode (in case the definition is from the legacy lscc).
func (cei *ChaincodeEndorsementInfoSource) ChaincodeEndorsementInfo(channelID, chaincodeName string, qe ledger.SimpleQueryExecutor) (*ChaincodeEndorsementInfo, error) {
if cei.BuiltinSCCs.IsSysCC(chaincodeName) {
return &ChaincodeEndorsementInfo{
Version: scc.SysCCVersion,
EndorsementPlugin: "escc",
EnforceInit: false,
ChaincodeID: scc.ChaincodeID(chaincodeName),
}, nil
}
// return legacy cc endorsement info if V20 is not enabled
channelConfig := cei.Resources.ChannelConfigSource.GetStableChannelConfig(channelID)
if channelConfig == nil {
return nil, errors.Errorf("could not get channel config for channel '%s'", channelID)
}
ac, ok := channelConfig.ApplicationConfig()
if !ok {
return nil, errors.Errorf("could not get application config for channel '%s'", channelID)
}
if !ac.Capabilities().LifecycleV20() {
return cei.LegacyImpl.ChaincodeEndorsementInfo(channelID, chaincodeName, qe)
}
chaincodeInfo, ok, err := cei.CachedChaincodeInfo(channelID, chaincodeName, qe)
if err != nil {
return nil, err
}
if !ok {
return cei.LegacyImpl.ChaincodeEndorsementInfo(channelID, chaincodeName, qe)
}
return &ChaincodeEndorsementInfo{
Version: chaincodeInfo.Definition.EndorsementInfo.Version,
EnforceInit: chaincodeInfo.Definition.EndorsementInfo.InitRequired,
EndorsementPlugin: chaincodeInfo.Definition.EndorsementInfo.EndorsementPlugin,
ChaincodeID: chaincodeInfo.InstallInfo.PackageID, // Local packages use package ID for ccid
}, nil
}