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  }