github.com/yimialmonte/fabric@v2.1.1+incompatible/core/ledger/confighistory/mgr.go (about) 1 /* 2 Copyright IBM Corp. All Rights Reserved. 3 4 SPDX-License-Identifier: Apache-2.0 5 */ 6 7 package confighistory 8 9 import ( 10 "fmt" 11 12 "github.com/golang/protobuf/proto" 13 "github.com/hyperledger/fabric-protos-go/common" 14 "github.com/hyperledger/fabric-protos-go/ledger/rwset/kvrwset" 15 "github.com/hyperledger/fabric-protos-go/peer" 16 "github.com/hyperledger/fabric/common/flogging" 17 "github.com/hyperledger/fabric/core/ledger" 18 "github.com/pkg/errors" 19 ) 20 21 var logger = flogging.MustGetLogger("confighistory") 22 23 const ( 24 collectionConfigNamespace = "lscc" // lscc namespace was introduced in version 1.2 and we continue to use this in order to be compatible with existing data 25 ) 26 27 // Mgr should be registered as a state listener. The state listener builds the history and retriever helps in querying the history 28 type Mgr interface { 29 ledger.StateListener 30 GetRetriever(ledgerID string, ledgerInfoRetriever LedgerInfoRetriever) ledger.ConfigHistoryRetriever 31 Close() 32 } 33 34 type mgr struct { 35 ccInfoProvider ledger.DeployedChaincodeInfoProvider 36 dbProvider *dbProvider 37 } 38 39 // NewMgr constructs an instance that implements interface `Mgr` 40 func NewMgr(dbPath string, ccInfoProvider ledger.DeployedChaincodeInfoProvider) (Mgr, error) { 41 p, err := newDBProvider(dbPath) 42 if err != nil { 43 return nil, err 44 } 45 return &mgr{ccInfoProvider, p}, nil 46 } 47 48 func (m *mgr) Initialize(ledgerID string, qe ledger.SimpleQueryExecutor) error { 49 // Noop 50 return nil 51 } 52 53 // InterestedInNamespaces implements function from the interface ledger.StateListener 54 func (m *mgr) InterestedInNamespaces() []string { 55 return m.ccInfoProvider.Namespaces() 56 } 57 58 // StateCommitDone implements function from the interface ledger.StateListener 59 func (m *mgr) StateCommitDone(ledgerID string) { 60 // Noop 61 } 62 63 // HandleStateUpdates implements function from the interface ledger.StateListener 64 // In this implementation, the latest collection config package is retrieved via 65 // ledger.DeployedChaincodeInfoProvider and is persisted as a separate entry in a separate db. 66 // The composite key for the entry is a tuple of <blockNum, namespace, key> 67 func (m *mgr) HandleStateUpdates(trigger *ledger.StateUpdateTrigger) error { 68 updatedCCs, err := m.ccInfoProvider.UpdatedChaincodes(extractPublicUpdates(trigger.StateUpdates)) 69 if err != nil { 70 return err 71 } 72 // updated chaincodes can be empty if the invocation to this function is triggered 73 // because of state updates that contains only chaincode approval transaction output 74 if len(updatedCCs) == 0 { 75 return nil 76 } 77 updatedCollConfigs := map[string]*peer.CollectionConfigPackage{} 78 for _, cc := range updatedCCs { 79 ccInfo, err := m.ccInfoProvider.ChaincodeInfo(trigger.LedgerID, cc.Name, trigger.PostCommitQueryExecutor) 80 if err != nil { 81 return err 82 } 83 84 // DeployedChaincodeInfoProvider implementation in new lifecycle return an empty 'CollectionConfigPackage' 85 // (instead of a nil) to indicate the absence of collection config, so check for both conditions 86 if ccInfo.ExplicitCollectionConfigPkg == nil || len(ccInfo.ExplicitCollectionConfigPkg.Config) == 0 { 87 continue 88 } 89 updatedCollConfigs[ccInfo.Name] = ccInfo.ExplicitCollectionConfigPkg 90 } 91 if len(updatedCollConfigs) == 0 { 92 return nil 93 } 94 batch, err := prepareDBBatch(updatedCollConfigs, trigger.CommittingBlockNum) 95 if err != nil { 96 return err 97 } 98 dbHandle := m.dbProvider.getDB(trigger.LedgerID) 99 return dbHandle.writeBatch(batch, true) 100 } 101 102 // GetRetriever returns an implementation of `ledger.ConfigHistoryRetriever` for the given ledger id. 103 func (m *mgr) GetRetriever(ledgerID string, ledgerInfoRetriever LedgerInfoRetriever) ledger.ConfigHistoryRetriever { 104 return &retriever{ 105 ledgerInfoRetriever: ledgerInfoRetriever, 106 ledgerID: ledgerID, 107 deployedCCInfoProvider: m.ccInfoProvider, 108 dbHandle: m.dbProvider.getDB(ledgerID), 109 } 110 } 111 112 // Close implements the function in the interface 'Mgr' 113 func (m *mgr) Close() { 114 m.dbProvider.Close() 115 } 116 117 type retriever struct { 118 ledgerInfoRetriever LedgerInfoRetriever 119 ledgerID string 120 deployedCCInfoProvider ledger.DeployedChaincodeInfoProvider 121 dbHandle *db 122 } 123 124 // MostRecentCollectionConfigBelow implements function from the interface ledger.ConfigHistoryRetriever 125 func (r *retriever) MostRecentCollectionConfigBelow(blockNum uint64, chaincodeName string) (*ledger.CollectionConfigInfo, error) { 126 compositeKV, err := r.dbHandle.mostRecentEntryBelow(blockNum, collectionConfigNamespace, constructCollectionConfigKey(chaincodeName)) 127 if err != nil { 128 return nil, err 129 } 130 implicitColls, err := r.getImplicitCollection(chaincodeName) 131 if err != nil { 132 return nil, err 133 } 134 135 return constructCollectionConfigInfo(compositeKV, implicitColls) 136 } 137 138 // CollectionConfigAt implements function from the interface ledger.ConfigHistoryRetriever 139 func (r *retriever) CollectionConfigAt(blockNum uint64, chaincodeName string) (*ledger.CollectionConfigInfo, error) { 140 info, err := r.ledgerInfoRetriever.GetBlockchainInfo() 141 if err != nil { 142 return nil, err 143 } 144 maxCommittedBlockNum := info.Height - 1 145 if maxCommittedBlockNum < blockNum { 146 return nil, &ledger.ErrCollectionConfigNotYetAvailable{MaxBlockNumCommitted: maxCommittedBlockNum, 147 Msg: fmt.Sprintf("The maximum block number committed [%d] is less than the requested block number [%d]", maxCommittedBlockNum, blockNum)} 148 } 149 150 compositeKV, err := r.dbHandle.entryAt(blockNum, collectionConfigNamespace, constructCollectionConfigKey(chaincodeName)) 151 if err != nil { 152 return nil, err 153 } 154 implicitColls, err := r.getImplicitCollection(chaincodeName) 155 if err != nil { 156 return nil, err 157 } 158 return constructCollectionConfigInfo(compositeKV, implicitColls) 159 } 160 161 func (r *retriever) getImplicitCollection(chaincodeName string) ([]*peer.StaticCollectionConfig, error) { 162 qe, err := r.ledgerInfoRetriever.NewQueryExecutor() 163 if err != nil { 164 return nil, err 165 } 166 defer qe.Done() 167 return r.deployedCCInfoProvider.ImplicitCollections(r.ledgerID, chaincodeName, qe) 168 } 169 170 func prepareDBBatch(chaincodeCollConfigs map[string]*peer.CollectionConfigPackage, committingBlockNum uint64) (*batch, error) { 171 batch := newBatch() 172 for ccName, collConfig := range chaincodeCollConfigs { 173 key := constructCollectionConfigKey(ccName) 174 var configBytes []byte 175 var err error 176 if configBytes, err = proto.Marshal(collConfig); err != nil { 177 return nil, errors.WithStack(err) 178 } 179 batch.add(collectionConfigNamespace, key, committingBlockNum, configBytes) 180 } 181 return batch, nil 182 } 183 184 func compositeKVToCollectionConfig(compositeKV *compositeKV) (*ledger.CollectionConfigInfo, error) { 185 conf := &peer.CollectionConfigPackage{} 186 if err := proto.Unmarshal(compositeKV.value, conf); err != nil { 187 return nil, errors.Wrap(err, "error unmarshalling compositeKV to collection config") 188 } 189 return &ledger.CollectionConfigInfo{ 190 CollectionConfig: conf, 191 CommittingBlockNum: compositeKV.blockNum, 192 }, nil 193 } 194 195 func constructCollectionConfigKey(chaincodeName string) string { 196 return chaincodeName + "~collection" // collection config key as in version 1.2 and we continue to use this in order to be compatible with existing data 197 } 198 199 func extractPublicUpdates(stateUpdates ledger.StateUpdates) map[string][]*kvrwset.KVWrite { 200 m := map[string][]*kvrwset.KVWrite{} 201 for ns, updates := range stateUpdates { 202 m[ns] = updates.PublicUpdates 203 } 204 return m 205 } 206 207 func constructCollectionConfigInfo( 208 compositeKV *compositeKV, 209 implicitColls []*peer.StaticCollectionConfig, 210 ) (*ledger.CollectionConfigInfo, error) { 211 var collConf *ledger.CollectionConfigInfo 212 var err error 213 214 if compositeKV == nil && len(implicitColls) == 0 { 215 return nil, nil 216 } 217 218 collConf = &ledger.CollectionConfigInfo{ 219 CollectionConfig: &peer.CollectionConfigPackage{}, 220 } 221 if compositeKV != nil { 222 if collConf, err = compositeKVToCollectionConfig(compositeKV); err != nil { 223 return nil, err 224 } 225 } 226 227 for _, implicitColl := range implicitColls { 228 cc := &peer.CollectionConfig{} 229 cc.Payload = &peer.CollectionConfig_StaticCollectionConfig{StaticCollectionConfig: implicitColl} 230 collConf.CollectionConfig.Config = append( 231 collConf.CollectionConfig.Config, 232 cc, 233 ) 234 } 235 return collConf, nil 236 } 237 238 // LedgerInfoRetriever retrieves the relevant info from ledger 239 type LedgerInfoRetriever interface { 240 GetBlockchainInfo() (*common.BlockchainInfo, error) 241 NewQueryExecutor() (ledger.QueryExecutor, error) 242 }