github.com/ewagmig/fabric@v2.1.1+incompatible/core/ledger/kvledger/kv_ledger_provider.go (about)

     1  /*
     2  Copyright IBM Corp. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package kvledger
     8  
     9  import (
    10  	"bytes"
    11  	"fmt"
    12  
    13  	"github.com/golang/protobuf/proto"
    14  	"github.com/hyperledger/fabric-protos-go/common"
    15  	"github.com/hyperledger/fabric/common/ledger/dataformat"
    16  	"github.com/hyperledger/fabric/common/ledger/util/leveldbhelper"
    17  	"github.com/hyperledger/fabric/core/ledger"
    18  	"github.com/hyperledger/fabric/core/ledger/confighistory"
    19  	"github.com/hyperledger/fabric/core/ledger/kvledger/bookkeeping"
    20  	"github.com/hyperledger/fabric/core/ledger/kvledger/history"
    21  	"github.com/hyperledger/fabric/core/ledger/kvledger/msgs"
    22  	"github.com/hyperledger/fabric/core/ledger/kvledger/txmgmt/privacyenabledstate"
    23  	"github.com/hyperledger/fabric/core/ledger/ledgerstorage"
    24  	"github.com/hyperledger/fabric/core/ledger/pvtdatastorage"
    25  	"github.com/hyperledger/fabric/protoutil"
    26  	"github.com/pkg/errors"
    27  	"github.com/syndtr/goleveldb/leveldb"
    28  )
    29  
    30  var (
    31  	// ErrLedgerIDExists is thrown by a CreateLedger call if a ledger with the given id already exists
    32  	ErrLedgerIDExists = errors.New("LedgerID already exists")
    33  	// ErrNonExistingLedgerID is thrown by an OpenLedger call if a ledger with the given id does not exist
    34  	ErrNonExistingLedgerID = errors.New("LedgerID does not exist")
    35  	// ErrLedgerNotOpened is thrown by a CloseLedger call if a ledger with the given id has not been opened
    36  	ErrLedgerNotOpened = errors.New("ledger is not opened yet")
    37  	// ErrInactiveLedger is thrown by an OpenLedger call if a ledger with the given id is not active
    38  	ErrInactiveLedger = errors.New("Ledger is not active")
    39  
    40  	underConstructionLedgerKey = []byte("underConstructionLedgerKey")
    41  	// ledgerKeyPrefix is the prefix for each ledger key in idStore db
    42  	ledgerKeyPrefix = []byte{'l'}
    43  	// ledgerKeyStop is the end key when querying idStore db by ledger key
    44  	ledgerKeyStop = []byte{'l' + 1}
    45  	// metadataKeyPrefix is the prefix for each metadata key in idStore db
    46  	metadataKeyPrefix = []byte{'s'}
    47  	// metadataKeyStop is the end key when querying idStore db by metadata key
    48  	metadataKeyStop = []byte{'s' + 1}
    49  
    50  	// formatKey
    51  	formatKey = []byte("f")
    52  )
    53  
    54  // Provider implements interface ledger.PeerLedgerProvider
    55  type Provider struct {
    56  	idStore             *idStore
    57  	ledgerStoreProvider *ledgerstorage.Provider
    58  	vdbProvider         privacyenabledstate.DBProvider
    59  	historydbProvider   *history.DBProvider
    60  	configHistoryMgr    confighistory.Mgr
    61  	stateListeners      []ledger.StateListener
    62  	bookkeepingProvider bookkeeping.Provider
    63  	initializer         *ledger.Initializer
    64  	collElgNotifier     *collElgNotifier
    65  	stats               *stats
    66  	fileLock            *leveldbhelper.FileLock
    67  	hasher              ledger.Hasher
    68  }
    69  
    70  // NewProvider instantiates a new Provider.
    71  // This is not thread-safe and assumed to be synchronized by the caller
    72  func NewProvider(initializer *ledger.Initializer) (pr *Provider, e error) {
    73  	p := &Provider{
    74  		initializer: initializer,
    75  		hasher:      initializer.Hasher,
    76  	}
    77  
    78  	defer func() {
    79  		if e != nil {
    80  			p.Close()
    81  			if errFormatMismatch, ok := e.(*dataformat.ErrVersionMismatch); ok {
    82  				if errFormatMismatch.Version == dataformat.Version1x && errFormatMismatch.ExpectedVersion == dataformat.Version20 {
    83  					logger.Errorf("Please execute the 'peer node upgrade-dbs' command to upgrade the database format: %s", errFormatMismatch)
    84  				} else {
    85  					logger.Errorf("Please check the Fabric version matches the ledger data format: %s", errFormatMismatch)
    86  				}
    87  			}
    88  		}
    89  	}()
    90  
    91  	fileLockPath := fileLockPath(initializer.Config.RootFSPath)
    92  	fileLock := leveldbhelper.NewFileLock(fileLockPath)
    93  	if err := fileLock.Lock(); err != nil {
    94  		return nil, errors.Wrap(err, "as another peer node command is executing,"+
    95  			" wait for that command to complete its execution or terminate it before retrying")
    96  	}
    97  
    98  	p.fileLock = fileLock
    99  
   100  	if err := p.initLedgerIDInventory(); err != nil {
   101  		return nil, err
   102  	}
   103  
   104  	if err := p.initLedgerStorageProvider(); err != nil {
   105  		return nil, err
   106  	}
   107  
   108  	if err := p.initHistoryDBProvider(); err != nil {
   109  		return nil, err
   110  	}
   111  
   112  	if err := p.initConfigHistoryManager(); err != nil {
   113  		return nil, err
   114  	}
   115  
   116  	p.initCollElgNotifier()
   117  
   118  	p.initStateListeners()
   119  
   120  	if err := p.initStateDBProvider(); err != nil {
   121  		return nil, err
   122  	}
   123  
   124  	p.initLedgerStatistics()
   125  
   126  	p.recoverUnderConstructionLedger()
   127  
   128  	return p, nil
   129  }
   130  
   131  func (p *Provider) initLedgerIDInventory() error {
   132  	idStore, err := openIDStore(LedgerProviderPath(p.initializer.Config.RootFSPath))
   133  	if err != nil {
   134  		return err
   135  	}
   136  	p.idStore = idStore
   137  	return nil
   138  }
   139  
   140  func (p *Provider) initLedgerStorageProvider() error {
   141  	// initialize ledger storage
   142  	privateData := &pvtdatastorage.PrivateDataConfig{
   143  		PrivateDataConfig: p.initializer.Config.PrivateDataConfig,
   144  		StorePath:         PvtDataStorePath(p.initializer.Config.RootFSPath),
   145  	}
   146  
   147  	ledgerStoreProvider, err := ledgerstorage.NewProvider(
   148  		BlockStorePath(p.initializer.Config.RootFSPath),
   149  		privateData,
   150  		p.initializer.MetricsProvider,
   151  	)
   152  	if err != nil {
   153  		return err
   154  	}
   155  	p.ledgerStoreProvider = ledgerStoreProvider
   156  	return nil
   157  }
   158  
   159  func (p *Provider) initHistoryDBProvider() error {
   160  	if !p.initializer.Config.HistoryDBConfig.Enabled {
   161  		return nil
   162  	}
   163  	// Initialize the history database (index for history of values by key)
   164  	historydbProvider, err := history.NewDBProvider(
   165  		HistoryDBPath(p.initializer.Config.RootFSPath),
   166  	)
   167  	if err != nil {
   168  		return err
   169  	}
   170  	p.historydbProvider = historydbProvider
   171  	return nil
   172  }
   173  
   174  func (p *Provider) initConfigHistoryManager() error {
   175  	var err error
   176  	configHistoryMgr, err := confighistory.NewMgr(
   177  		ConfigHistoryDBPath(p.initializer.Config.RootFSPath),
   178  		p.initializer.DeployedChaincodeInfoProvider,
   179  	)
   180  	if err != nil {
   181  		return err
   182  	}
   183  	p.configHistoryMgr = configHistoryMgr
   184  	return nil
   185  }
   186  
   187  func (p *Provider) initCollElgNotifier() {
   188  	collElgNotifier := &collElgNotifier{
   189  		p.initializer.DeployedChaincodeInfoProvider,
   190  		p.initializer.MembershipInfoProvider,
   191  		make(map[string]collElgListener),
   192  	}
   193  	p.collElgNotifier = collElgNotifier
   194  }
   195  
   196  func (p *Provider) initStateListeners() {
   197  	stateListeners := p.initializer.StateListeners
   198  	stateListeners = append(stateListeners, p.collElgNotifier)
   199  	stateListeners = append(stateListeners, p.configHistoryMgr)
   200  	p.stateListeners = stateListeners
   201  }
   202  
   203  func (p *Provider) initStateDBProvider() error {
   204  	var err error
   205  	p.bookkeepingProvider, err = bookkeeping.NewProvider(
   206  		BookkeeperDBPath(p.initializer.Config.RootFSPath),
   207  	)
   208  	if err != nil {
   209  		return err
   210  	}
   211  	stateDB := &privacyenabledstate.StateDBConfig{
   212  		StateDBConfig: p.initializer.Config.StateDBConfig,
   213  		LevelDBPath:   StateDBPath(p.initializer.Config.RootFSPath),
   214  	}
   215  	sysNamespaces := p.initializer.DeployedChaincodeInfoProvider.Namespaces()
   216  	p.vdbProvider, err = privacyenabledstate.NewCommonStorageDBProvider(
   217  		p.bookkeepingProvider,
   218  		p.initializer.MetricsProvider,
   219  		p.initializer.HealthCheckRegistry,
   220  		stateDB,
   221  		sysNamespaces,
   222  	)
   223  	return err
   224  }
   225  
   226  func (p *Provider) initLedgerStatistics() {
   227  	p.stats = newStats(p.initializer.MetricsProvider)
   228  }
   229  
   230  // Create implements the corresponding method from interface ledger.PeerLedgerProvider
   231  // This functions sets a under construction flag before doing any thing related to ledger creation and
   232  // upon a successful ledger creation with the committed genesis block, removes the flag and add entry into
   233  // created ledgers list (atomically). If a crash happens in between, the 'recoverUnderConstructionLedger'
   234  // function is invoked before declaring the provider to be usable
   235  func (p *Provider) Create(genesisBlock *common.Block) (ledger.PeerLedger, error) {
   236  	ledgerID, err := protoutil.GetChainIDFromBlock(genesisBlock)
   237  	if err != nil {
   238  		return nil, err
   239  	}
   240  	exists, err := p.idStore.ledgerIDExists(ledgerID)
   241  	if err != nil {
   242  		return nil, err
   243  	}
   244  	if exists {
   245  		return nil, ErrLedgerIDExists
   246  	}
   247  	if err = p.idStore.setUnderConstructionFlag(ledgerID); err != nil {
   248  		return nil, err
   249  	}
   250  	lgr, err := p.openInternal(ledgerID)
   251  	if err != nil {
   252  		logger.Errorf("Error opening a new empty ledger. Unsetting under construction flag. Error: %+v", err)
   253  		panicOnErr(p.runCleanup(ledgerID), "Error running cleanup for ledger id [%s]", ledgerID)
   254  		panicOnErr(p.idStore.unsetUnderConstructionFlag(), "Error while unsetting under construction flag")
   255  		return nil, err
   256  	}
   257  	if err := lgr.CommitLegacy(&ledger.BlockAndPvtData{Block: genesisBlock}, &ledger.CommitOptions{}); err != nil {
   258  		lgr.Close()
   259  		return nil, err
   260  	}
   261  	panicOnErr(p.idStore.createLedgerID(ledgerID, genesisBlock), "Error while marking ledger as created")
   262  	return lgr, nil
   263  }
   264  
   265  // Open implements the corresponding method from interface ledger.PeerLedgerProvider
   266  func (p *Provider) Open(ledgerID string) (ledger.PeerLedger, error) {
   267  	logger.Debugf("Open() opening kvledger: %s", ledgerID)
   268  	// Check the ID store to ensure that the chainId/ledgerId exists
   269  	active, exists, err := p.idStore.ledgerIDActive(ledgerID)
   270  	if err != nil {
   271  		return nil, err
   272  	}
   273  	if !exists {
   274  		return nil, ErrNonExistingLedgerID
   275  	}
   276  	if !active {
   277  		return nil, ErrInactiveLedger
   278  	}
   279  	return p.openInternal(ledgerID)
   280  }
   281  
   282  func (p *Provider) openInternal(ledgerID string) (ledger.PeerLedger, error) {
   283  	// Get the block store for a chain/ledger
   284  	blockStore, err := p.ledgerStoreProvider.Open(ledgerID)
   285  	if err != nil {
   286  		return nil, err
   287  	}
   288  	p.collElgNotifier.registerListener(ledgerID, blockStore)
   289  
   290  	// Get the versioned database (state database) for a chain/ledger
   291  	vDB, err := p.vdbProvider.GetDBHandle(ledgerID)
   292  	if err != nil {
   293  		return nil, err
   294  	}
   295  
   296  	// Get the history database (index for history of values by key) for a chain/ledger
   297  	var historyDB *history.DB
   298  	if p.historydbProvider != nil {
   299  		historyDB, err = p.historydbProvider.GetDBHandle(ledgerID)
   300  		if err != nil {
   301  			return nil, err
   302  		}
   303  	}
   304  
   305  	// Create a kvLedger for this chain/ledger, which encapsulates the underlying data stores
   306  	// (id store, blockstore, state database, history database)
   307  	l, err := newKVLedger(
   308  		ledgerID,
   309  		blockStore,
   310  		vDB,
   311  		historyDB,
   312  		p.configHistoryMgr,
   313  		p.stateListeners,
   314  		p.bookkeepingProvider,
   315  		p.initializer.DeployedChaincodeInfoProvider,
   316  		p.initializer.ChaincodeLifecycleEventProvider,
   317  		p.stats.ledgerStats(ledgerID),
   318  		p.initializer.CustomTxProcessors,
   319  		p.hasher,
   320  	)
   321  	if err != nil {
   322  		return nil, err
   323  	}
   324  	return l, nil
   325  }
   326  
   327  // Exists implements the corresponding method from interface ledger.PeerLedgerProvider
   328  func (p *Provider) Exists(ledgerID string) (bool, error) {
   329  	return p.idStore.ledgerIDExists(ledgerID)
   330  }
   331  
   332  // List implements the corresponding method from interface ledger.PeerLedgerProvider
   333  func (p *Provider) List() ([]string, error) {
   334  	return p.idStore.getActiveLedgerIDs()
   335  }
   336  
   337  // Close implements the corresponding method from interface ledger.PeerLedgerProvider
   338  func (p *Provider) Close() {
   339  	if p.idStore != nil {
   340  		p.idStore.close()
   341  	}
   342  	if p.ledgerStoreProvider != nil {
   343  		p.ledgerStoreProvider.Close()
   344  	}
   345  	if p.vdbProvider != nil {
   346  		p.vdbProvider.Close()
   347  	}
   348  	if p.bookkeepingProvider != nil {
   349  		p.bookkeepingProvider.Close()
   350  	}
   351  	if p.configHistoryMgr != nil {
   352  		p.configHistoryMgr.Close()
   353  	}
   354  	if p.historydbProvider != nil {
   355  		p.historydbProvider.Close()
   356  	}
   357  	if p.fileLock != nil {
   358  		p.fileLock.Unlock()
   359  	}
   360  }
   361  
   362  // recoverUnderConstructionLedger checks whether the under construction flag is set - this would be the case
   363  // if a crash had happened during creation of ledger and the ledger creation could have been left in intermediate
   364  // state. Recovery checks if the ledger was created and the genesis block was committed successfully then it completes
   365  // the last step of adding the ledger id to the list of created ledgers. Else, it clears the under construction flag
   366  func (p *Provider) recoverUnderConstructionLedger() {
   367  	logger.Debugf("Recovering under construction ledger")
   368  	ledgerID, err := p.idStore.getUnderConstructionFlag()
   369  	panicOnErr(err, "Error while checking whether the under construction flag is set")
   370  	if ledgerID == "" {
   371  		logger.Debugf("No under construction ledger found. Quitting recovery")
   372  		return
   373  	}
   374  	logger.Infof("ledger [%s] found as under construction", ledgerID)
   375  	ledger, err := p.openInternal(ledgerID)
   376  	panicOnErr(err, "Error while opening under construction ledger [%s]", ledgerID)
   377  	bcInfo, err := ledger.GetBlockchainInfo()
   378  	panicOnErr(err, "Error while getting blockchain info for the under construction ledger [%s]", ledgerID)
   379  	ledger.Close()
   380  
   381  	switch bcInfo.Height {
   382  	case 0:
   383  		logger.Infof("Genesis block was not committed. Hence, the peer ledger not created. unsetting the under construction flag")
   384  		panicOnErr(p.runCleanup(ledgerID), "Error while running cleanup for ledger id [%s]", ledgerID)
   385  		panicOnErr(p.idStore.unsetUnderConstructionFlag(), "Error while unsetting under construction flag")
   386  	case 1:
   387  		logger.Infof("Genesis block was committed. Hence, marking the peer ledger as created")
   388  		genesisBlock, err := ledger.GetBlockByNumber(0)
   389  		panicOnErr(err, "Error while retrieving genesis block from blockchain for ledger [%s]", ledgerID)
   390  		panicOnErr(p.idStore.createLedgerID(ledgerID, genesisBlock), "Error while adding ledgerID [%s] to created list", ledgerID)
   391  	default:
   392  		panic(errors.Errorf(
   393  			"data inconsistency: under construction flag is set for ledger [%s] while the height of the blockchain is [%d]",
   394  			ledgerID, bcInfo.Height))
   395  	}
   396  	return
   397  }
   398  
   399  // runCleanup cleans up blockstorage, statedb, and historydb for what
   400  // may have got created during in-complete ledger creation
   401  func (p *Provider) runCleanup(ledgerID string) error {
   402  	// TODO - though, not having this is harmless for kv ledger.
   403  	// If we want, following could be done:
   404  	// - blockstorage could remove empty folders
   405  	// - couchdb backed statedb could delete the database if got created
   406  	// - leveldb backed statedb and history db need not perform anything as it uses a single db shared across ledgers
   407  	return nil
   408  }
   409  
   410  func panicOnErr(err error, mgsFormat string, args ...interface{}) {
   411  	if err == nil {
   412  		return
   413  	}
   414  	args = append(args, err)
   415  	panic(fmt.Sprintf(mgsFormat+" Error: %s", args...))
   416  }
   417  
   418  //////////////////////////////////////////////////////////////////////
   419  // Ledger id persistence related code
   420  ///////////////////////////////////////////////////////////////////////
   421  type idStore struct {
   422  	db     *leveldbhelper.DB
   423  	dbPath string
   424  }
   425  
   426  func openIDStore(path string) (s *idStore, e error) {
   427  	db := leveldbhelper.CreateDB(&leveldbhelper.Conf{DBPath: path})
   428  	db.Open()
   429  	defer func() {
   430  		if e != nil {
   431  			db.Close()
   432  		}
   433  	}()
   434  
   435  	emptyDB, err := db.IsEmpty()
   436  	if err != nil {
   437  		return nil, err
   438  	}
   439  
   440  	expectedFormatBytes := []byte(dataformat.Version20)
   441  	if emptyDB {
   442  		// add format key to a new db
   443  		err := db.Put(formatKey, expectedFormatBytes, true)
   444  		if err != nil {
   445  			return nil, err
   446  		}
   447  		return &idStore{db, path}, nil
   448  	}
   449  
   450  	// verify the format is current for an existing db
   451  	formatVersion, err := db.Get(formatKey)
   452  	if err != nil {
   453  		return nil, err
   454  	}
   455  	if !bytes.Equal(formatVersion, expectedFormatBytes) {
   456  		logger.Errorf("The db at path [%s] contains data in unexpected format. expected data format = [%s] (%#v), data format = [%s] (%#v).",
   457  			path, dataformat.Version20, expectedFormatBytes, formatVersion, formatVersion)
   458  		return nil, &dataformat.ErrVersionMismatch{
   459  			ExpectedVersion: dataformat.Version20,
   460  			Version:         string(formatVersion),
   461  			DBInfo:          fmt.Sprintf("leveldb for channel-IDs at [%s]", path),
   462  		}
   463  	}
   464  	return &idStore{db, path}, nil
   465  }
   466  
   467  func (s *idStore) upgradeFormat() error {
   468  	format, err := s.db.Get(formatKey)
   469  	if err != nil {
   470  		return err
   471  	}
   472  	idStoreFormatBytes := []byte(dataformat.Version20)
   473  	if bytes.Equal(format, idStoreFormatBytes) {
   474  		logger.Debug("Format is current, nothing to do")
   475  		return nil
   476  	}
   477  	if format != nil {
   478  		err = &dataformat.ErrVersionMismatch{
   479  			ExpectedVersion: "",
   480  			Version:         string(format),
   481  			DBInfo:          fmt.Sprintf("leveldb for channel-IDs at [%s]", s.dbPath),
   482  		}
   483  		logger.Errorf("Failed to upgrade format [%#v] to new format [%#v]: %s", format, idStoreFormatBytes, err)
   484  		return err
   485  	}
   486  
   487  	logger.Infof("The ledgerProvider db format is old, upgrading to the new format %s", dataformat.Version20)
   488  
   489  	batch := &leveldb.Batch{}
   490  	batch.Put(formatKey, idStoreFormatBytes)
   491  
   492  	// add new metadata key for each ledger (channel)
   493  	metadata, err := protoutil.Marshal(&msgs.LedgerMetadata{Status: msgs.Status_ACTIVE})
   494  	if err != nil {
   495  		logger.Errorf("Error marshalling ledger metadata: %s", err)
   496  		return errors.Wrapf(err, "error marshalling ledger metadata")
   497  	}
   498  	itr := s.db.GetIterator(ledgerKeyPrefix, ledgerKeyStop)
   499  	defer itr.Release()
   500  	for itr.Error() == nil && itr.Next() {
   501  		id := s.decodeLedgerID(itr.Key(), ledgerKeyPrefix)
   502  		batch.Put(s.encodeLedgerKey(id, metadataKeyPrefix), metadata)
   503  	}
   504  	if err = itr.Error(); err != nil {
   505  		logger.Errorf("Error while upgrading idStore format: %s", err)
   506  		return errors.Wrapf(err, "error while upgrading idStore format")
   507  	}
   508  
   509  	return s.db.WriteBatch(batch, true)
   510  }
   511  
   512  func (s *idStore) setUnderConstructionFlag(ledgerID string) error {
   513  	return s.db.Put(underConstructionLedgerKey, []byte(ledgerID), true)
   514  }
   515  
   516  func (s *idStore) unsetUnderConstructionFlag() error {
   517  	return s.db.Delete(underConstructionLedgerKey, true)
   518  }
   519  
   520  func (s *idStore) getUnderConstructionFlag() (string, error) {
   521  	val, err := s.db.Get(underConstructionLedgerKey)
   522  	if err != nil {
   523  		return "", err
   524  	}
   525  	return string(val), nil
   526  }
   527  
   528  func (s *idStore) createLedgerID(ledgerID string, gb *common.Block) error {
   529  	gbKey := s.encodeLedgerKey(ledgerID, ledgerKeyPrefix)
   530  	metadataKey := s.encodeLedgerKey(ledgerID, metadataKeyPrefix)
   531  	var val []byte
   532  	var metadata []byte
   533  	var err error
   534  	if val, err = s.db.Get(gbKey); err != nil {
   535  		return err
   536  	}
   537  	if val != nil {
   538  		return ErrLedgerIDExists
   539  	}
   540  	if val, err = proto.Marshal(gb); err != nil {
   541  		return err
   542  	}
   543  	if metadata, err = protoutil.Marshal(&msgs.LedgerMetadata{Status: msgs.Status_ACTIVE}); err != nil {
   544  		return err
   545  	}
   546  	batch := &leveldb.Batch{}
   547  	batch.Put(gbKey, val)
   548  	batch.Put(metadataKey, metadata)
   549  	batch.Delete(underConstructionLedgerKey)
   550  	return s.db.WriteBatch(batch, true)
   551  }
   552  
   553  func (s *idStore) updateLedgerStatus(ledgerID string, newStatus msgs.Status) error {
   554  	metadata, err := s.getLedgerMetadata(ledgerID)
   555  	if err != nil {
   556  		return err
   557  	}
   558  	if metadata == nil {
   559  		logger.Errorf("LedgerID [%s] does not exist", ledgerID)
   560  		return ErrNonExistingLedgerID
   561  	}
   562  	if metadata.Status == newStatus {
   563  		logger.Infof("Ledger [%s] is already in [%s] status, nothing to do", ledgerID, newStatus)
   564  		return nil
   565  	}
   566  	metadata.Status = newStatus
   567  	metadataBytes, err := proto.Marshal(metadata)
   568  	if err != nil {
   569  		logger.Errorf("Error marshalling ledger metadata: %s", err)
   570  		return errors.Wrapf(err, "error marshalling ledger metadata")
   571  	}
   572  	logger.Infof("Updating ledger [%s] status to [%s]", ledgerID, newStatus)
   573  	key := s.encodeLedgerKey(ledgerID, metadataKeyPrefix)
   574  	return s.db.Put(key, metadataBytes, true)
   575  }
   576  
   577  func (s *idStore) getLedgerMetadata(ledgerID string) (*msgs.LedgerMetadata, error) {
   578  	val, err := s.db.Get(s.encodeLedgerKey(ledgerID, metadataKeyPrefix))
   579  	if val == nil || err != nil {
   580  		return nil, err
   581  	}
   582  	metadata := &msgs.LedgerMetadata{}
   583  	if err := proto.Unmarshal(val, metadata); err != nil {
   584  		logger.Errorf("Error unmarshalling ledger metadata: %s", err)
   585  		return nil, errors.Wrapf(err, "error unmarshalling ledger metadata")
   586  	}
   587  	return metadata, nil
   588  }
   589  
   590  func (s *idStore) ledgerIDExists(ledgerID string) (bool, error) {
   591  	key := s.encodeLedgerKey(ledgerID, ledgerKeyPrefix)
   592  	val := []byte{}
   593  	err := error(nil)
   594  	if val, err = s.db.Get(key); err != nil {
   595  		return false, err
   596  	}
   597  	return val != nil, nil
   598  }
   599  
   600  // ledgerIDActive returns if a ledger is active and existed
   601  func (s *idStore) ledgerIDActive(ledgerID string) (bool, bool, error) {
   602  	metadata, err := s.getLedgerMetadata(ledgerID)
   603  	if metadata == nil || err != nil {
   604  		return false, false, err
   605  	}
   606  	return metadata.Status == msgs.Status_ACTIVE, true, nil
   607  }
   608  
   609  func (s *idStore) getActiveLedgerIDs() ([]string, error) {
   610  	var ids []string
   611  	itr := s.db.GetIterator(metadataKeyPrefix, metadataKeyStop)
   612  	defer itr.Release()
   613  	for itr.Error() == nil && itr.Next() {
   614  		metadata := &msgs.LedgerMetadata{}
   615  		if err := proto.Unmarshal(itr.Value(), metadata); err != nil {
   616  			logger.Errorf("Error unmarshalling ledger metadata: %s", err)
   617  			return nil, errors.Wrapf(err, "error unmarshalling ledger metadata")
   618  		}
   619  		if metadata.Status == msgs.Status_ACTIVE {
   620  			id := s.decodeLedgerID(itr.Key(), metadataKeyPrefix)
   621  			ids = append(ids, id)
   622  		}
   623  	}
   624  	if err := itr.Error(); err != nil {
   625  		logger.Errorf("Error getting ledger ids from idStore: %s", err)
   626  		return nil, errors.Wrapf(err, "error getting ledger ids from idStore")
   627  	}
   628  	return ids, nil
   629  }
   630  
   631  func (s *idStore) close() {
   632  	s.db.Close()
   633  }
   634  
   635  func (s *idStore) encodeLedgerKey(ledgerID string, prefix []byte) []byte {
   636  	return append(prefix, []byte(ledgerID)...)
   637  }
   638  
   639  func (s *idStore) decodeLedgerID(key []byte, prefix []byte) string {
   640  	return string(key[len(prefix):])
   641  }