github.com/tenywen/fabric@v1.0.0-beta.0.20170620030522-a5b1ed380643/core/ledger/kvledger/kv_ledger_provider.go (about) 1 /* 2 Copyright IBM Corp. 2016 All Rights Reserved. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package kvledger 18 19 import ( 20 "bytes" 21 "errors" 22 "fmt" 23 24 "github.com/golang/protobuf/proto" 25 "github.com/hyperledger/fabric/common/ledger/blkstorage" 26 "github.com/hyperledger/fabric/common/ledger/blkstorage/fsblkstorage" 27 "github.com/hyperledger/fabric/common/ledger/util/leveldbhelper" 28 "github.com/hyperledger/fabric/core/ledger" 29 "github.com/hyperledger/fabric/core/ledger/kvledger/history/historydb" 30 "github.com/hyperledger/fabric/core/ledger/kvledger/history/historydb/historyleveldb" 31 "github.com/hyperledger/fabric/core/ledger/kvledger/txmgmt/statedb" 32 "github.com/hyperledger/fabric/core/ledger/kvledger/txmgmt/statedb/statecouchdb" 33 "github.com/hyperledger/fabric/core/ledger/kvledger/txmgmt/statedb/stateleveldb" 34 "github.com/hyperledger/fabric/core/ledger/ledgerconfig" 35 "github.com/hyperledger/fabric/protos/common" 36 "github.com/hyperledger/fabric/protos/utils" 37 "github.com/syndtr/goleveldb/leveldb" 38 ) 39 40 var ( 41 // ErrLedgerIDExists is thrown by a CreateLedger call if a ledger with the given id already exists 42 ErrLedgerIDExists = errors.New("LedgerID already exists") 43 // ErrNonExistingLedgerID is thrown by a OpenLedger call if a ledger with the given id does not exist 44 ErrNonExistingLedgerID = errors.New("LedgerID does not exist") 45 // ErrLedgerNotOpened is thrown by a CloseLedger call if a ledger with the given id has not been opened 46 ErrLedgerNotOpened = errors.New("Ledger is not opened yet") 47 48 underConstructionLedgerKey = []byte("underConstructionLedgerKey") 49 ledgerKeyPrefix = []byte("l") 50 ) 51 52 // Provider implements interface ledger.PeerLedgerProvider 53 type Provider struct { 54 idStore *idStore 55 blockStoreProvider blkstorage.BlockStoreProvider 56 vdbProvider statedb.VersionedDBProvider 57 historydbProvider historydb.HistoryDBProvider 58 } 59 60 // NewProvider instantiates a new Provider. 61 // This is not thread-safe and assumed to be synchronized be the caller 62 func NewProvider() (ledger.PeerLedgerProvider, error) { 63 64 logger.Info("Initializing ledger provider") 65 66 // Initialize the ID store (inventory of chainIds/ledgerIds) 67 idStore := openIDStore(ledgerconfig.GetLedgerProviderPath()) 68 69 // Initialize the block storage 70 attrsToIndex := []blkstorage.IndexableAttr{ 71 blkstorage.IndexableAttrBlockHash, 72 blkstorage.IndexableAttrBlockNum, 73 blkstorage.IndexableAttrTxID, 74 blkstorage.IndexableAttrBlockNumTranNum, 75 blkstorage.IndexableAttrBlockTxID, 76 blkstorage.IndexableAttrTxValidationCode, 77 } 78 indexConfig := &blkstorage.IndexConfig{AttrsToIndex: attrsToIndex} 79 blockStoreProvider := fsblkstorage.NewProvider( 80 fsblkstorage.NewConf(ledgerconfig.GetBlockStorePath(), ledgerconfig.GetMaxBlockfileSize()), 81 indexConfig) 82 83 // Initialize the versioned database (state database) 84 var vdbProvider statedb.VersionedDBProvider 85 if !ledgerconfig.IsCouchDBEnabled() { 86 logger.Debug("Constructing leveldb VersionedDBProvider") 87 vdbProvider = stateleveldb.NewVersionedDBProvider() 88 } else { 89 logger.Debug("Constructing CouchDB VersionedDBProvider") 90 var err error 91 vdbProvider, err = statecouchdb.NewVersionedDBProvider() 92 if err != nil { 93 return nil, err 94 } 95 } 96 97 // Initialize the history database (index for history of values by key) 98 var historydbProvider historydb.HistoryDBProvider 99 historydbProvider = historyleveldb.NewHistoryDBProvider() 100 101 logger.Info("ledger provider Initialized") 102 provider := &Provider{idStore, blockStoreProvider, vdbProvider, historydbProvider} 103 provider.recoverUnderConstructionLedger() 104 return provider, nil 105 } 106 107 // Create implements the corresponding method from interface ledger.PeerLedgerProvider 108 // This functions sets a under construction flag before doing any thing related to ledger creation and 109 // upon a successful ledger creation with the committed genesis block, removes the flag and add entry into 110 // created ledgers list (atomically). If a crash happens in between, the 'recoverUnderConstructionLedger' 111 // function is invoked before declaring the provider to be usable 112 func (provider *Provider) Create(genesisBlock *common.Block) (ledger.PeerLedger, error) { 113 ledgerID, err := utils.GetChainIDFromBlock(genesisBlock) 114 if err != nil { 115 return nil, err 116 } 117 exists, err := provider.idStore.ledgerIDExists(ledgerID) 118 if err != nil { 119 return nil, err 120 } 121 if exists { 122 return nil, ErrLedgerIDExists 123 } 124 if err = provider.idStore.setUnderConstructionFlag(ledgerID); err != nil { 125 return nil, err 126 } 127 ledger, err := provider.openInternal(ledgerID) 128 if err != nil { 129 logger.Errorf("Error in opening a new empty ledger. Unsetting under construction flag. Err: %s", err) 130 panicOnErr(provider.runCleanup(ledgerID), "Error while running cleanup for ledger id [%s]", ledgerID) 131 panicOnErr(provider.idStore.unsetUnderConstructionFlag(), "Error while unsetting under construction flag") 132 return nil, err 133 } 134 if err := ledger.Commit(genesisBlock); err != nil { 135 ledger.Close() 136 return nil, err 137 } 138 panicOnErr(provider.idStore.createLedgerID(ledgerID, genesisBlock), "Error while marking ledger as created") 139 return ledger, nil 140 } 141 142 // Open implements the corresponding method from interface ledger.PeerLedgerProvider 143 func (provider *Provider) Open(ledgerID string) (ledger.PeerLedger, error) { 144 logger.Debugf("Open() opening kvledger: %s", ledgerID) 145 // Check the ID store to ensure that the chainId/ledgerId exists 146 exists, err := provider.idStore.ledgerIDExists(ledgerID) 147 if err != nil { 148 return nil, err 149 } 150 if !exists { 151 return nil, ErrNonExistingLedgerID 152 } 153 return provider.openInternal(ledgerID) 154 } 155 156 func (provider *Provider) openInternal(ledgerID string) (ledger.PeerLedger, error) { 157 // Get the block store for a chain/ledger 158 blockStore, err := provider.blockStoreProvider.OpenBlockStore(ledgerID) 159 if err != nil { 160 return nil, err 161 } 162 163 // Get the versioned database (state database) for a chain/ledger 164 vDB, err := provider.vdbProvider.GetDBHandle(ledgerID) 165 if err != nil { 166 return nil, err 167 } 168 169 // Get the history database (index for history of values by key) for a chain/ledger 170 historyDB, err := provider.historydbProvider.GetDBHandle(ledgerID) 171 if err != nil { 172 return nil, err 173 } 174 175 // Create a kvLedger for this chain/ledger, which encasulates the underlying data stores 176 // (id store, blockstore, state database, history database) 177 l, err := newKVLedger(ledgerID, blockStore, vDB, historyDB) 178 if err != nil { 179 return nil, err 180 } 181 return l, nil 182 } 183 184 // Exists implements the corresponding method from interface ledger.PeerLedgerProvider 185 func (provider *Provider) Exists(ledgerID string) (bool, error) { 186 return provider.idStore.ledgerIDExists(ledgerID) 187 } 188 189 // List implements the corresponding method from interface ledger.PeerLedgerProvider 190 func (provider *Provider) List() ([]string, error) { 191 return provider.idStore.getAllLedgerIds() 192 } 193 194 // Close implements the corresponding method from interface ledger.PeerLedgerProvider 195 func (provider *Provider) Close() { 196 provider.idStore.close() 197 provider.blockStoreProvider.Close() 198 provider.vdbProvider.Close() 199 provider.historydbProvider.Close() 200 } 201 202 // recoverUnderConstructionLedger checks whether the under construction flag is set - this would be the case 203 // if a crash had happened during creation of ledger and the ledger creation could have been left in intermediate 204 // state. Recovery checks if the ledger was created and the genesis block was committed successfully then it completes 205 // the last step of adding the ledger id to the list of created ledgers. Else, it clears the under construction flag 206 func (provider *Provider) recoverUnderConstructionLedger() { 207 logger.Debugf("Recovering under construction ledger") 208 ledgerID, err := provider.idStore.getUnderConstructionFlag() 209 panicOnErr(err, "Error while checking whether the under construction flag is set") 210 if ledgerID == "" { 211 logger.Debugf("No under construction ledger found. Quitting recovery") 212 return 213 } 214 logger.Infof("ledger [%s] found as under construction", ledgerID) 215 ledger, err := provider.openInternal(ledgerID) 216 panicOnErr(err, "Error while opening under construction ledger [%s]", ledgerID) 217 bcInfo, err := ledger.GetBlockchainInfo() 218 panicOnErr(err, "Error while getting blockchain info for the under construction ledger [%s]", ledgerID) 219 ledger.Close() 220 221 switch bcInfo.Height { 222 case 0: 223 logger.Infof("Genesis block was not committed. Hence, the peer ledger not created. unsetting the under construction flag") 224 panicOnErr(provider.runCleanup(ledgerID), "Error while running cleanup for ledger id [%s]", ledgerID) 225 panicOnErr(provider.idStore.unsetUnderConstructionFlag(), "Error while unsetting under construction flag") 226 case 1: 227 logger.Infof("Genesis block was committed. Hence, marking the peer ledger as created") 228 genesisBlock, err := ledger.GetBlockByNumber(0) 229 panicOnErr(err, "Error while retrieving genesis block from blockchain for ledger [%s]", ledgerID) 230 panicOnErr(provider.idStore.createLedgerID(ledgerID, genesisBlock), "Error while adding ledgerID [%s] to created list", ledgerID) 231 default: 232 panic(fmt.Errorf( 233 "Data inconsistency: under construction flag is set for ledger [%s] while the height of the blockchain is [%d]", 234 ledgerID, bcInfo.Height)) 235 } 236 return 237 } 238 239 // runCleanup cleans up blockstorage, statedb, and historydb for what 240 // may have got created during in-complete ledger creation 241 func (provider *Provider) runCleanup(ledgerID string) error { 242 // TODO - though, not having this is harmless for kv ledger. 243 // If we want, following could be done: 244 // - blockstorage could remove empty folders 245 // - couchdb backed statedb could delete the database if got created 246 // - leveldb backed statedb and history db need not perform anything as it uses a single db shared across ledgers 247 return nil 248 } 249 250 func panicOnErr(err error, mgsFormat string, args ...interface{}) { 251 if err == nil { 252 return 253 } 254 args = append(args, err) 255 panic(fmt.Sprintf(mgsFormat+" Err:%s ", args...)) 256 } 257 258 ////////////////////////////////////////////////////////////////////// 259 // Ledger id persistence related code 260 /////////////////////////////////////////////////////////////////////// 261 type idStore struct { 262 db *leveldbhelper.DB 263 } 264 265 func openIDStore(path string) *idStore { 266 db := leveldbhelper.CreateDB(&leveldbhelper.Conf{DBPath: path}) 267 db.Open() 268 return &idStore{db} 269 } 270 271 func (s *idStore) setUnderConstructionFlag(ledgerID string) error { 272 return s.db.Put(underConstructionLedgerKey, []byte(ledgerID), true) 273 } 274 275 func (s *idStore) unsetUnderConstructionFlag() error { 276 return s.db.Delete(underConstructionLedgerKey, true) 277 } 278 279 func (s *idStore) getUnderConstructionFlag() (string, error) { 280 val, err := s.db.Get(underConstructionLedgerKey) 281 if err != nil { 282 return "", err 283 } 284 return string(val), nil 285 } 286 287 func (s *idStore) createLedgerID(ledgerID string, gb *common.Block) error { 288 key := s.encodeLedgerKey(ledgerID) 289 var val []byte 290 var err error 291 if val, err = proto.Marshal(gb); err != nil { 292 return err 293 } 294 if val, err = s.db.Get(key); err != nil { 295 return err 296 } 297 if val != nil { 298 return ErrLedgerIDExists 299 } 300 batch := &leveldb.Batch{} 301 batch.Put(key, val) 302 batch.Delete(underConstructionLedgerKey) 303 return s.db.WriteBatch(batch, true) 304 } 305 306 func (s *idStore) ledgerIDExists(ledgerID string) (bool, error) { 307 key := s.encodeLedgerKey(ledgerID) 308 val := []byte{} 309 err := error(nil) 310 if val, err = s.db.Get(key); err != nil { 311 return false, err 312 } 313 return val != nil, nil 314 } 315 316 func (s *idStore) getAllLedgerIds() ([]string, error) { 317 var ids []string 318 itr := s.db.GetIterator(nil, nil) 319 itr.First() 320 for itr.Valid() { 321 if bytes.Equal(itr.Key(), underConstructionLedgerKey) { 322 continue 323 } 324 id := string(s.decodeLedgerID(itr.Key())) 325 ids = append(ids, id) 326 itr.Next() 327 } 328 return ids, nil 329 } 330 331 func (s *idStore) close() { 332 s.db.Close() 333 } 334 335 func (s *idStore) encodeLedgerKey(ledgerID string) []byte { 336 return append(ledgerKeyPrefix, []byte(ledgerID)...) 337 } 338 339 func (s *idStore) decodeLedgerID(key []byte) string { 340 return string(key[len(ledgerKeyPrefix):]) 341 }