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 }