@@ -11,14 +11,22 @@ import (
11
11
"sync"
12
12
13
13
"github.com/hyperledger/fabric/common/util"
14
+ "github.com/hyperledger/fabric/core/chaincode/persistence"
14
15
"github.com/hyperledger/fabric/core/ledger"
15
16
16
17
"github.com/pkg/errors"
17
18
)
18
19
20
+ type ChaincodeInstallInfo struct {
21
+ Hash []byte
22
+ Type string
23
+ Path string
24
+ }
25
+
19
26
type CachedChaincodeDefinition struct {
20
- Definition * ChaincodeDefinition
21
- Approved bool
27
+ Definition * ChaincodeDefinition
28
+ Approved bool
29
+ InstallInfo * ChaincodeInstallInfo
22
30
23
31
// Hashes is the list of hashed keys in the implicit collection referring to this definition.
24
32
// These hashes are determined by the current sequence number of chaincode definition. When dirty,
@@ -48,16 +56,72 @@ type Cache struct {
48
56
// event, by synchronizing at a peer global level, we drastically simplify accounting for which
49
57
// chaincodes are installed and which channels that installed chaincode is currently in use on.
50
58
mutex sync.RWMutex
59
+
60
+ // localChaincodes is a map from the hash of the locally installed chaincode's hash
61
+ // (yes, the hash of the hash), to a set of channels, to a set of chaincode
62
+ // definitions which reference this local installed chaincode hash.
63
+ localChaincodes map [string ]* LocalChaincode
64
+ }
65
+
66
+ type LocalChaincode struct {
67
+ Info * ChaincodeInstallInfo
68
+ References map [string ]map [string ]* CachedChaincodeDefinition
51
69
}
52
70
53
71
func NewCache (lifecycle * Lifecycle , myOrgMSPID string ) * Cache {
54
72
return & Cache {
55
73
definedChaincodes : map [string ]* ChannelCache {},
74
+ localChaincodes : map [string ]* LocalChaincode {},
56
75
Lifecycle : lifecycle ,
57
76
MyOrgMSPID : myOrgMSPID ,
58
77
}
59
78
}
60
79
80
+ // InitializeLocalChaincodes should be called once after cache creation (timing doesn't matter,
81
+ // though already installed chaincodes will not be invokable until it it completes). Ideally,
82
+ // this would be part of the constructor, but, we cannot rely on the chaincode store being created
83
+ // before the cache is created.
84
+ func (c * Cache ) InitializeLocalChaincodes () error {
85
+ c .mutex .Lock ()
86
+ defer c .mutex .Unlock ()
87
+ ccPackages , err := c .Lifecycle .ChaincodeStore .ListInstalledChaincodes ()
88
+ if err != nil {
89
+ return errors .WithMessage (err , "could not list installed chaincodes" )
90
+ }
91
+
92
+ for _ , ccPackage := range ccPackages {
93
+ ccPackageBytes , _ , err := c .Lifecycle .ChaincodeStore .Load (ccPackage .Id )
94
+ if err != nil {
95
+ return errors .WithMessage (err , fmt .Sprintf ("could not load chaincode with hash '%x'" , ccPackage .Id ))
96
+ }
97
+ parsedCCPackage , err := c .Lifecycle .PackageParser .Parse (ccPackageBytes )
98
+ if err != nil {
99
+ return errors .WithMessage (err , fmt .Sprintf ("could not parse chaincode package with hash '%x'" , ccPackage .Id ))
100
+ }
101
+ c .handleChaincodeInstalledWhileLocked (parsedCCPackage .Metadata , ccPackage .Id )
102
+ }
103
+
104
+ logger .Infof ("Initialized lifecycle cache with %d already installed chaincodes" , len (c .localChaincodes ))
105
+ for channelID , chaincodeCache := range c .definedChaincodes {
106
+ approved , installed , runnable := 0 , 0 , 0
107
+ for _ , cachedChaincode := range chaincodeCache .Chaincodes {
108
+ if cachedChaincode .Approved {
109
+ approved ++
110
+ }
111
+ if cachedChaincode .InstallInfo != nil {
112
+ installed ++
113
+ }
114
+ if cachedChaincode .Approved && cachedChaincode .InstallInfo != nil {
115
+ runnable ++
116
+ }
117
+ }
118
+
119
+ logger .Infof ("Initialized lifecycle cache for channel '%s' with %d chaincodes runnable (%d approved, %d installed)" , channelID , runnable , approved , installed )
120
+ }
121
+
122
+ return nil
123
+ }
124
+
61
125
// Initialize will populate the set of currently committed chaincode definitions
62
126
// for a channel into the cache. Note, it this looks like a bit of a DRY violation
63
127
// with respect to 'Update', but, the error handling is quite different and attempting
@@ -88,6 +152,35 @@ func (c *Cache) Initialize(channelID string, qe ledger.SimpleQueryExecutor) erro
88
152
return c .update (channelID , dirtyChaincodes , qe )
89
153
}
90
154
155
+ // HandleChaincodeInstalled should be invoked whenever a new chaincode is installed
156
+ func (c * Cache ) HandleChaincodeInstalled (md * persistence.ChaincodePackageMetadata , hash []byte ) {
157
+ c .mutex .Lock ()
158
+ defer c .mutex .Unlock ()
159
+ c .handleChaincodeInstalledWhileLocked (md , hash )
160
+ }
161
+
162
+ func (c * Cache ) handleChaincodeInstalledWhileLocked (md * persistence.ChaincodePackageMetadata , hash []byte ) {
163
+ hashOfCCHash := string (util .ComputeSHA256 (hash ))
164
+ localChaincode , ok := c .localChaincodes [hashOfCCHash ]
165
+ if ! ok {
166
+ localChaincode = & LocalChaincode {
167
+ References : map [string ]map [string ]* CachedChaincodeDefinition {},
168
+ }
169
+ c .localChaincodes [hashOfCCHash ] = localChaincode
170
+ }
171
+ localChaincode .Info = & ChaincodeInstallInfo {
172
+ Hash : hash ,
173
+ Type : md .Type ,
174
+ Path : md .Path ,
175
+ }
176
+ for channelID , channelCache := range localChaincode .References {
177
+ for chaincodeName , cachedChaincode := range channelCache {
178
+ cachedChaincode .InstallInfo = localChaincode .Info
179
+ logger .Infof ("Installed chaincode with hash %x now available on channel %s for chaincode definition %s:%s" , hash , channelID , chaincodeName , cachedChaincode .Definition .EndorsementInfo .Version )
180
+ }
181
+ }
182
+ }
183
+
91
184
// HandleStateUpdates is required to implement the ledger state listener interface. It applies
92
185
// any state updates to the cache.
93
186
func (c * Cache ) HandleStateUpdates (trigger * ledger.StateUpdateTrigger ) error {
@@ -200,6 +293,7 @@ func (c *Cache) update(channelID string, dirtyChaincodes map[string]struct{}, qe
200
293
}
201
294
202
295
for name := range dirtyChaincodes {
296
+ logger .Infof ("Updating cached definition for chaincode '%s' on channel '%s'" , name , channelID )
203
297
cachedChaincode , ok := channelCache .Chaincodes [name ]
204
298
if ! ok {
205
299
cachedChaincode = & CachedChaincodeDefinition {}
@@ -243,9 +337,35 @@ func (c *Cache) update(channelID string, dirtyChaincodes map[string]struct{}, qe
243
337
if err != nil {
244
338
return errors .WithMessage (err , fmt .Sprintf ("could not check opaque org state for '%s' on channel '%s'" , name , channelID ))
245
339
}
246
- if ok {
247
- cachedChaincode .Approved = true
340
+ if ! ok {
341
+ logger .Debugf ("Channel %s for chaincode definition %s:%s does not have our org's approval" , channelID , name , chaincodeDefinition .EndorsementInfo .Version )
342
+ continue
343
+ }
344
+
345
+ cachedChaincode .Approved = true
346
+ hashOfCCHash := string (util .ComputeSHA256 (cachedChaincode .Definition .EndorsementInfo .Id ))
347
+ localChaincode , ok := c .localChaincodes [hashOfCCHash ]
348
+ if ! ok {
349
+ localChaincode = & LocalChaincode {
350
+ References : map [string ]map [string ]* CachedChaincodeDefinition {},
351
+ }
352
+ c .localChaincodes [hashOfCCHash ] = localChaincode
248
353
}
354
+
355
+ cachedChaincode .InstallInfo = localChaincode .Info
356
+ if localChaincode .Info != nil {
357
+ logger .Infof ("Chaincode with hash %x now available on channel %s for chaincode definition %s:%s" , localChaincode .Info .Hash , channelID , name , cachedChaincode .Definition .EndorsementInfo .Version )
358
+ } else {
359
+ logger .Debugf ("Chaincode definition for chaincode '%s' on channel '%s' is approved, but not installed" , name , channelID )
360
+ }
361
+
362
+ channelReferences , ok := localChaincode .References [channelID ]
363
+ if ! ok {
364
+ channelReferences = map [string ]* CachedChaincodeDefinition {}
365
+ localChaincode .References [channelID ] = channelReferences
366
+ }
367
+
368
+ channelReferences [name ] = cachedChaincode
249
369
}
250
370
251
371
return nil
0 commit comments