github.com/tenywen/fabric@v1.0.0-beta.0.20170620030522-a5b1ed380643/core/ledger/kvledger/kv_ledger.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  	"errors"
    21  	"fmt"
    22  
    23  	"github.com/hyperledger/fabric/common/flogging"
    24  	commonledger "github.com/hyperledger/fabric/common/ledger"
    25  	"github.com/hyperledger/fabric/common/ledger/blkstorage"
    26  	"github.com/hyperledger/fabric/core/ledger"
    27  	"github.com/hyperledger/fabric/core/ledger/kvledger/history/historydb"
    28  	"github.com/hyperledger/fabric/core/ledger/kvledger/txmgmt/statedb"
    29  	"github.com/hyperledger/fabric/core/ledger/kvledger/txmgmt/txmgr"
    30  	"github.com/hyperledger/fabric/core/ledger/kvledger/txmgmt/txmgr/lockbasedtxmgr"
    31  	"github.com/hyperledger/fabric/core/ledger/ledgerconfig"
    32  	"github.com/hyperledger/fabric/protos/common"
    33  	"github.com/hyperledger/fabric/protos/peer"
    34  )
    35  
    36  var logger = flogging.MustGetLogger("kvledger")
    37  
    38  // KVLedger provides an implementation of `ledger.PeerLedger`.
    39  // This implementation provides a key-value based data model
    40  type kvLedger struct {
    41  	ledgerID   string
    42  	blockStore blkstorage.BlockStore
    43  	txtmgmt    txmgr.TxMgr
    44  	historyDB  historydb.HistoryDB
    45  }
    46  
    47  // NewKVLedger constructs new `KVLedger`
    48  func newKVLedger(ledgerID string, blockStore blkstorage.BlockStore,
    49  	versionedDB statedb.VersionedDB, historyDB historydb.HistoryDB) (*kvLedger, error) {
    50  
    51  	logger.Debugf("Creating KVLedger ledgerID=%s: ", ledgerID)
    52  
    53  	//Initialize transaction manager using state database
    54  	var txmgmt txmgr.TxMgr
    55  	txmgmt = lockbasedtxmgr.NewLockBasedTxMgr(versionedDB)
    56  
    57  	// Create a kvLedger for this chain/ledger, which encasulates the underlying
    58  	// id store, blockstore, txmgr (state database), history database
    59  	l := &kvLedger{ledgerID, blockStore, txmgmt, historyDB}
    60  
    61  	//Recover both state DB and history DB if they are out of sync with block storage
    62  	if err := l.recoverDBs(); err != nil {
    63  		panic(fmt.Errorf(`Error during state DB recovery:%s`, err))
    64  	}
    65  
    66  	return l, nil
    67  }
    68  
    69  //Recover the state database and history database (if exist)
    70  //by recommitting last valid blocks
    71  func (l *kvLedger) recoverDBs() error {
    72  	logger.Debugf("Entering recoverDB()")
    73  	//If there is no block in blockstorage, nothing to recover.
    74  	info, _ := l.blockStore.GetBlockchainInfo()
    75  	if info.Height == 0 {
    76  		logger.Debug("Block storage is empty.")
    77  		return nil
    78  	}
    79  	lastAvailableBlockNum := info.Height - 1
    80  	recoverables := []recoverable{l.txtmgmt, l.historyDB}
    81  	recoverers := []*recoverer{}
    82  	for _, recoverable := range recoverables {
    83  		recoverFlag, firstBlockNum, err := recoverable.ShouldRecover(lastAvailableBlockNum)
    84  		if err != nil {
    85  			return err
    86  		}
    87  		if recoverFlag {
    88  			recoverers = append(recoverers, &recoverer{firstBlockNum, recoverable})
    89  		}
    90  	}
    91  	if len(recoverers) == 0 {
    92  		return nil
    93  	}
    94  	if len(recoverers) == 1 {
    95  		return l.recommitLostBlocks(recoverers[0].firstBlockNum, lastAvailableBlockNum, recoverers[0].recoverable)
    96  	}
    97  
    98  	// both dbs need to be recovered
    99  	if recoverers[0].firstBlockNum > recoverers[1].firstBlockNum {
   100  		// swap (put the lagger db at 0 index)
   101  		recoverers[0], recoverers[1] = recoverers[1], recoverers[0]
   102  	}
   103  	if recoverers[0].firstBlockNum != recoverers[1].firstBlockNum {
   104  		// bring the lagger db equal to the other db
   105  		if err := l.recommitLostBlocks(recoverers[0].firstBlockNum, recoverers[1].firstBlockNum-1,
   106  			recoverers[0].recoverable); err != nil {
   107  			return err
   108  		}
   109  	}
   110  	// get both the db upto block storage
   111  	return l.recommitLostBlocks(recoverers[1].firstBlockNum, lastAvailableBlockNum,
   112  		recoverers[0].recoverable, recoverers[1].recoverable)
   113  }
   114  
   115  //recommitLostBlocks retrieves blocks in specified range and commit the write set to either
   116  //state DB or history DB or both
   117  func (l *kvLedger) recommitLostBlocks(firstBlockNum uint64, lastBlockNum uint64, recoverables ...recoverable) error {
   118  	var err error
   119  	var block *common.Block
   120  	for blockNumber := firstBlockNum; blockNumber <= lastBlockNum; blockNumber++ {
   121  		if block, err = l.GetBlockByNumber(blockNumber); err != nil {
   122  			return err
   123  		}
   124  		for _, r := range recoverables {
   125  			if err := r.CommitLostBlock(block); err != nil {
   126  				return err
   127  			}
   128  		}
   129  	}
   130  	return nil
   131  }
   132  
   133  // GetTransactionByID retrieves a transaction by id
   134  func (l *kvLedger) GetTransactionByID(txID string) (*peer.ProcessedTransaction, error) {
   135  
   136  	tranEnv, err := l.blockStore.RetrieveTxByID(txID)
   137  	if err != nil {
   138  		return nil, err
   139  	}
   140  
   141  	txVResult, err := l.blockStore.RetrieveTxValidationCodeByTxID(txID)
   142  
   143  	if err != nil {
   144  		return nil, err
   145  	}
   146  
   147  	processedTran := &peer.ProcessedTransaction{TransactionEnvelope: tranEnv, ValidationCode: int32(txVResult)}
   148  	return processedTran, nil
   149  }
   150  
   151  // GetBlockchainInfo returns basic info about blockchain
   152  func (l *kvLedger) GetBlockchainInfo() (*common.BlockchainInfo, error) {
   153  	return l.blockStore.GetBlockchainInfo()
   154  }
   155  
   156  // GetBlockByNumber returns block at a given height
   157  // blockNumber of  math.MaxUint64 will return last block
   158  func (l *kvLedger) GetBlockByNumber(blockNumber uint64) (*common.Block, error) {
   159  	return l.blockStore.RetrieveBlockByNumber(blockNumber)
   160  
   161  }
   162  
   163  // GetBlocksIterator returns an iterator that starts from `startBlockNumber`(inclusive).
   164  // The iterator is a blocking iterator i.e., it blocks till the next block gets available in the ledger
   165  // ResultsIterator contains type BlockHolder
   166  func (l *kvLedger) GetBlocksIterator(startBlockNumber uint64) (commonledger.ResultsIterator, error) {
   167  	return l.blockStore.RetrieveBlocks(startBlockNumber)
   168  
   169  }
   170  
   171  // GetBlockByHash returns a block given it's hash
   172  func (l *kvLedger) GetBlockByHash(blockHash []byte) (*common.Block, error) {
   173  	return l.blockStore.RetrieveBlockByHash(blockHash)
   174  }
   175  
   176  // GetBlockByTxID returns a block which contains a transaction
   177  func (l *kvLedger) GetBlockByTxID(txID string) (*common.Block, error) {
   178  	return l.blockStore.RetrieveBlockByTxID(txID)
   179  }
   180  
   181  func (l *kvLedger) GetTxValidationCodeByTxID(txID string) (peer.TxValidationCode, error) {
   182  	return l.blockStore.RetrieveTxValidationCodeByTxID(txID)
   183  }
   184  
   185  //Prune prunes the blocks/transactions that satisfy the given policy
   186  func (l *kvLedger) Prune(policy commonledger.PrunePolicy) error {
   187  	return errors.New("Not yet implemented")
   188  }
   189  
   190  // NewTxSimulator returns new `ledger.TxSimulator`
   191  func (l *kvLedger) NewTxSimulator() (ledger.TxSimulator, error) {
   192  	return l.txtmgmt.NewTxSimulator()
   193  }
   194  
   195  // NewQueryExecutor gives handle to a query executor.
   196  // A client can obtain more than one 'QueryExecutor's for parallel execution.
   197  // Any synchronization should be performed at the implementation level if required
   198  func (l *kvLedger) NewQueryExecutor() (ledger.QueryExecutor, error) {
   199  	return l.txtmgmt.NewQueryExecutor()
   200  }
   201  
   202  // NewHistoryQueryExecutor gives handle to a history query executor.
   203  // A client can obtain more than one 'HistoryQueryExecutor's for parallel execution.
   204  // Any synchronization should be performed at the implementation level if required
   205  // Pass the ledger blockstore so that historical values can be looked up from the chain
   206  func (l *kvLedger) NewHistoryQueryExecutor() (ledger.HistoryQueryExecutor, error) {
   207  	return l.historyDB.NewHistoryQueryExecutor(l.blockStore)
   208  }
   209  
   210  // Commit commits the valid block (returned in the method RemoveInvalidTransactionsAndPrepare) and related state changes
   211  func (l *kvLedger) Commit(block *common.Block) error {
   212  	var err error
   213  	blockNo := block.Header.Number
   214  
   215  	logger.Debugf("Channel [%s]: Validating block [%d]", l.ledgerID, blockNo)
   216  	err = l.txtmgmt.ValidateAndPrepare(block, true)
   217  	if err != nil {
   218  		return err
   219  	}
   220  
   221  	logger.Debugf("Channel [%s]: Committing block [%d] to storage", l.ledgerID, blockNo)
   222  	if err = l.blockStore.AddBlock(block); err != nil {
   223  		return err
   224  	}
   225  	logger.Infof("Channel [%s]: Created block [%d] with %d transaction(s)", l.ledgerID, block.Header.Number, len(block.Data.Data))
   226  
   227  	logger.Debugf("Channel [%s]: Committing block [%d] transactions to state database", l.ledgerID, blockNo)
   228  	if err = l.txtmgmt.Commit(); err != nil {
   229  		panic(fmt.Errorf(`Error during commit to txmgr:%s`, err))
   230  	}
   231  
   232  	// History database could be written in parallel with state and/or async as a future optimization
   233  	if ledgerconfig.IsHistoryDBEnabled() {
   234  		logger.Debugf("Channel [%s]: Committing block [%d] transactions to history database", l.ledgerID, blockNo)
   235  		if err := l.historyDB.Commit(block); err != nil {
   236  			panic(fmt.Errorf(`Error during commit to history db:%s`, err))
   237  		}
   238  	}
   239  
   240  	return nil
   241  }
   242  
   243  // Close closes `KVLedger`
   244  func (l *kvLedger) Close() {
   245  	l.blockStore.Shutdown()
   246  	l.txtmgmt.Shutdown()
   247  }