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