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 }