github.com/kaituanwang/hyperledger@v2.0.1+incompatible/core/ledger/ledgerstorage/store.go (about) 1 /* 2 Copyright IBM Corp. All Rights Reserved. 3 4 SPDX-License-Identifier: Apache-2.0 5 */ 6 7 package ledgerstorage 8 9 import ( 10 "sync" 11 "sync/atomic" 12 13 "github.com/hyperledger/fabric-protos-go/common" 14 "github.com/hyperledger/fabric/common/flogging" 15 "github.com/hyperledger/fabric/common/ledger/blkstorage" 16 "github.com/hyperledger/fabric/common/ledger/blkstorage/fsblkstorage" 17 "github.com/hyperledger/fabric/common/metrics" 18 "github.com/hyperledger/fabric/core/ledger" 19 "github.com/hyperledger/fabric/core/ledger/pvtdatapolicy" 20 "github.com/hyperledger/fabric/core/ledger/pvtdatastorage" 21 ) 22 23 const maxBlockFileSize = 64 * 1024 * 1024 24 25 var logger = flogging.MustGetLogger("ledgerstorage") 26 27 // Provider encapsulates two providers 1) block store provider and 2) and pvt data store provider 28 type Provider struct { 29 blkStoreProvider blkstorage.BlockStoreProvider 30 pvtdataStoreProvider pvtdatastorage.Provider 31 } 32 33 // Store encapsulates two stores 1) block store and pvt data store 34 type Store struct { 35 blkstorage.BlockStore 36 pvtdataStore pvtdatastorage.Store 37 rwlock sync.RWMutex 38 isPvtstoreAheadOfBlockstore atomic.Value 39 } 40 41 var attrsToIndex = []blkstorage.IndexableAttr{ 42 blkstorage.IndexableAttrBlockHash, 43 blkstorage.IndexableAttrBlockNum, 44 blkstorage.IndexableAttrTxID, 45 blkstorage.IndexableAttrBlockNumTranNum, 46 } 47 48 // NewProvider returns the handle to the provider 49 func NewProvider(blockStoreDir string, conf *pvtdatastorage.PrivateDataConfig, metricsProvider metrics.Provider) (*Provider, error) { 50 // Initialize the block storage 51 indexConfig := &blkstorage.IndexConfig{AttrsToIndex: attrsToIndex} 52 blockStoreProvider, err := fsblkstorage.NewProvider( 53 fsblkstorage.NewConf( 54 blockStoreDir, 55 maxBlockFileSize, 56 ), 57 indexConfig, 58 metricsProvider, 59 ) 60 if err != nil { 61 return nil, err 62 } 63 pvtStoreProvider, err := pvtdatastorage.NewProvider(conf) 64 if err != nil { 65 return nil, err 66 } 67 return &Provider{blockStoreProvider, pvtStoreProvider}, nil 68 } 69 70 // Open opens the store 71 func (p *Provider) Open(ledgerid string) (*Store, error) { 72 var blockStore blkstorage.BlockStore 73 var pvtdataStore pvtdatastorage.Store 74 var err error 75 76 if blockStore, err = p.blkStoreProvider.OpenBlockStore(ledgerid); err != nil { 77 return nil, err 78 } 79 if pvtdataStore, err = p.pvtdataStoreProvider.OpenStore(ledgerid); err != nil { 80 return nil, err 81 } 82 store := &Store{ 83 BlockStore: blockStore, 84 pvtdataStore: pvtdataStore, 85 } 86 if err := store.init(); err != nil { 87 return nil, err 88 } 89 90 info, err := blockStore.GetBlockchainInfo() 91 if err != nil { 92 return nil, err 93 } 94 pvtstoreHeight, err := pvtdataStore.LastCommittedBlockHeight() 95 if err != nil { 96 return nil, err 97 } 98 store.isPvtstoreAheadOfBlockstore.Store(pvtstoreHeight > info.Height) 99 100 return store, nil 101 } 102 103 // Close closes the provider 104 func (p *Provider) Close() { 105 p.blkStoreProvider.Close() 106 p.pvtdataStoreProvider.Close() 107 } 108 109 // Exists checks whether the ledgerID already presents 110 func (p *Provider) Exists(ledgerID string) (bool, error) { 111 return p.blkStoreProvider.Exists(ledgerID) 112 } 113 114 // Init initializes store with essential configurations 115 func (s *Store) Init(btlPolicy pvtdatapolicy.BTLPolicy) { 116 s.pvtdataStore.Init(btlPolicy) 117 } 118 119 // CommitWithPvtData commits the block and the corresponding pvt data in an atomic operation 120 func (s *Store) CommitWithPvtData(blockAndPvtdata *ledger.BlockAndPvtData) error { 121 blockNum := blockAndPvtdata.Block.Header.Number 122 s.rwlock.Lock() 123 defer s.rwlock.Unlock() 124 125 pvtBlkStoreHt, err := s.pvtdataStore.LastCommittedBlockHeight() 126 if err != nil { 127 return err 128 } 129 130 if pvtBlkStoreHt < blockNum+1 { // The pvt data store sanity check does not allow rewriting the pvt data. 131 // when re-processing blocks (rejoin the channel or re-fetching last few block), 132 // skip the pvt data commit to the pvtdata blockstore 133 logger.Debugf("Writing block [%d] to pvt block store", blockNum) 134 // If a state fork occurs during a regular block commit, 135 // we have a mechanism to drop all blocks followed by refetching of blocks 136 // and re-processing them. In the current way of doing this, we only drop 137 // the block files (and related artifacts) but we do not drop/overwrite the 138 // pvtdatastorage as it might leads to data loss. 139 // During block reprocessing, as there is a possibility of an invalid pvtdata 140 // transaction to become valid, we store the pvtdata of invalid transactions 141 // too in the pvtdataStore as we do for the publicdata in the case of blockStore. 142 pvtData, missingPvtData := constructPvtDataAndMissingData(blockAndPvtdata) 143 if err := s.pvtdataStore.Commit(blockAndPvtdata.Block.Header.Number, pvtData, missingPvtData); err != nil { 144 return err 145 } 146 } else { 147 logger.Debugf("Skipping writing block [%d] to pvt block store as the store height is [%d]", blockNum, pvtBlkStoreHt) 148 } 149 150 if err := s.AddBlock(blockAndPvtdata.Block); err != nil { 151 return err 152 } 153 154 if pvtBlkStoreHt == blockNum+1 { 155 // we reach here only when the pvtdataStore was ahead 156 // of blockStore during the store opening time (would 157 // occur after a peer rollback/reset). 158 s.isPvtstoreAheadOfBlockstore.Store(false) 159 } 160 161 return nil 162 } 163 164 func constructPvtDataAndMissingData(blockAndPvtData *ledger.BlockAndPvtData) ([]*ledger.TxPvtData, 165 ledger.TxMissingPvtDataMap) { 166 167 var pvtData []*ledger.TxPvtData 168 missingPvtData := make(ledger.TxMissingPvtDataMap) 169 170 numTxs := uint64(len(blockAndPvtData.Block.Data.Data)) 171 172 // for all tx, construct pvtdata and missing pvtdata list 173 for txNum := uint64(0); txNum < numTxs; txNum++ { 174 if pvtdata, ok := blockAndPvtData.PvtData[txNum]; ok { 175 pvtData = append(pvtData, pvtdata) 176 } 177 178 if missingData, ok := blockAndPvtData.MissingPvtData[txNum]; ok { 179 for _, missing := range missingData { 180 missingPvtData.Add(txNum, missing.Namespace, 181 missing.Collection, missing.IsEligible) 182 } 183 } 184 } 185 return pvtData, missingPvtData 186 } 187 188 // CommitPvtDataOfOldBlocks commits the pvtData of old blocks 189 func (s *Store) CommitPvtDataOfOldBlocks(blocksPvtData map[uint64][]*ledger.TxPvtData) error { 190 err := s.pvtdataStore.CommitPvtDataOfOldBlocks(blocksPvtData) 191 if err != nil { 192 return err 193 } 194 return nil 195 } 196 197 // GetPvtDataAndBlockByNum returns the block and the corresponding pvt data. 198 // The pvt data is filtered by the list of 'collections' supplied 199 func (s *Store) GetPvtDataAndBlockByNum(blockNum uint64, filter ledger.PvtNsCollFilter) (*ledger.BlockAndPvtData, error) { 200 s.rwlock.RLock() 201 defer s.rwlock.RUnlock() 202 203 var block *common.Block 204 var pvtdata []*ledger.TxPvtData 205 var err error 206 if block, err = s.RetrieveBlockByNumber(blockNum); err != nil { 207 return nil, err 208 } 209 if pvtdata, err = s.getPvtDataByNumWithoutLock(blockNum, filter); err != nil { 210 return nil, err 211 } 212 return &ledger.BlockAndPvtData{Block: block, PvtData: constructPvtdataMap(pvtdata)}, nil 213 } 214 215 // GetPvtDataByNum returns only the pvt data corresponding to the given block number 216 // The pvt data is filtered by the list of 'ns/collections' supplied in the filter 217 // A nil filter does not filter any results 218 func (s *Store) GetPvtDataByNum(blockNum uint64, filter ledger.PvtNsCollFilter) ([]*ledger.TxPvtData, error) { 219 s.rwlock.RLock() 220 defer s.rwlock.RUnlock() 221 return s.getPvtDataByNumWithoutLock(blockNum, filter) 222 } 223 224 // getPvtDataByNumWithoutLock returns only the pvt data corresponding to the given block number. 225 // This function does not acquire a readlock and it is expected that in most of the circumstances, the caller 226 // possesses a read lock on `s.rwlock` 227 func (s *Store) getPvtDataByNumWithoutLock(blockNum uint64, filter ledger.PvtNsCollFilter) ([]*ledger.TxPvtData, error) { 228 var pvtdata []*ledger.TxPvtData 229 var err error 230 if pvtdata, err = s.pvtdataStore.GetPvtDataByBlockNum(blockNum, filter); err != nil { 231 return nil, err 232 } 233 return pvtdata, nil 234 } 235 236 // DoesPvtDataInfoExist returns true when 237 // (1) the ledger has pvtdata associated with the given block number (or) 238 // (2) a few or all pvtdata associated with the given block number is missing but the 239 // missing info is recorded in the ledger (or) 240 // (3) the block is committed does not contain any pvtData. 241 func (s *Store) DoesPvtDataInfoExist(blockNum uint64) (bool, error) { 242 pvtStoreHt, err := s.pvtdataStore.LastCommittedBlockHeight() 243 if err != nil { 244 return false, err 245 } 246 return blockNum+1 <= pvtStoreHt, nil 247 } 248 249 // GetMissingPvtDataInfoForMostRecentBlocks invokes the function on underlying pvtdata store 250 func (s *Store) GetMissingPvtDataInfoForMostRecentBlocks(maxBlock int) (ledger.MissingPvtDataInfo, error) { 251 // it is safe to not acquire a read lock on s.rwlock. Without a lock, the value of 252 // lastCommittedBlock can change due to a new block commit. As a result, we may not 253 // be able to fetch the missing data info of truly the most recent blocks. This 254 // decision was made to ensure that the regular block commit rate is not affected. 255 return s.pvtdataStore.GetMissingPvtDataInfoForMostRecentBlocks(maxBlock) 256 } 257 258 // ProcessCollsEligibilityEnabled invokes the function on underlying pvtdata store 259 func (s *Store) ProcessCollsEligibilityEnabled(committingBlk uint64, nsCollMap map[string][]string) error { 260 return s.pvtdataStore.ProcessCollsEligibilityEnabled(committingBlk, nsCollMap) 261 } 262 263 // GetLastUpdatedOldBlocksPvtData invokes the function on underlying pvtdata store 264 func (s *Store) GetLastUpdatedOldBlocksPvtData() (map[uint64][]*ledger.TxPvtData, error) { 265 return s.pvtdataStore.GetLastUpdatedOldBlocksPvtData() 266 } 267 268 // ResetLastUpdatedOldBlocksList invokes the function on underlying pvtdata store 269 func (s *Store) ResetLastUpdatedOldBlocksList() error { 270 return s.pvtdataStore.ResetLastUpdatedOldBlocksList() 271 } 272 273 // IsPvtStoreAheadOfBlockStore returns true when the pvtStore height is 274 // greater than the blockstore height. Otherwise, it returns false. 275 func (s *Store) IsPvtStoreAheadOfBlockStore() bool { 276 return s.isPvtstoreAheadOfBlockstore.Load().(bool) 277 } 278 279 // TODO: FAB-16297 -- Remove init() as it is no longer needed. The private data feature 280 // became stable from v1.2 onwards. To allow the initiation of pvtdata store with non-zero 281 // block height (mainly during a rolling upgrade from an existing v1.1 network to v1.2), 282 // we introduced pvtdata init() function which would take the height of block store and 283 // set it as a height of pvtdataStore. From v2.0 onwards, it is no longer needed as we do 284 // not support a rolling upgrade from v1.1 to v2.0 285 286 // init first invokes function `initFromExistingBlockchain` 287 // in order to check whether the pvtdata store is present because of an upgrade 288 // of peer from 1.0 and need to be updated with the existing blockchain. If, this is 289 // not the case then this init will invoke function `syncPvtdataStoreWithBlockStore` 290 // to follow the normal course 291 func (s *Store) init() error { 292 var initialized bool 293 var err error 294 if initialized, err = s.initPvtdataStoreFromExistingBlockchain(); err != nil || initialized { 295 return err 296 } 297 return nil 298 } 299 300 // initPvtdataStoreFromExistingBlockchain updates the initial state of the pvtdata store 301 // if an existing block store has a blockchain and the pvtdata store is empty. 302 // This situation is expected to happen when a peer is upgrated from version 1.0 303 // and an existing blockchain is present that was generated with version 1.0. 304 // Under this scenario, the pvtdata store is brought upto the point as if it has 305 // processed existing blocks with no pvt data. This function returns true if the 306 // above mentioned condition is found to be true and pvtdata store is successfully updated 307 func (s *Store) initPvtdataStoreFromExistingBlockchain() (bool, error) { 308 var bcInfo *common.BlockchainInfo 309 var pvtdataStoreEmpty bool 310 var err error 311 312 if bcInfo, err = s.BlockStore.GetBlockchainInfo(); err != nil { 313 return false, err 314 } 315 if pvtdataStoreEmpty, err = s.pvtdataStore.IsEmpty(); err != nil { 316 return false, err 317 } 318 if pvtdataStoreEmpty && bcInfo.Height > 0 { 319 if err = s.pvtdataStore.InitLastCommittedBlock(bcInfo.Height - 1); err != nil { 320 return false, err 321 } 322 return true, nil 323 } 324 return false, nil 325 } 326 327 func constructPvtdataMap(pvtdata []*ledger.TxPvtData) ledger.TxPvtDataMap { 328 if pvtdata == nil { 329 return nil 330 } 331 m := make(map[uint64]*ledger.TxPvtData) 332 for _, pvtdatum := range pvtdata { 333 m[pvtdatum.SeqInBlock] = pvtdatum 334 } 335 return m 336 } 337 338 // LoadPreResetHeight returns the pre reset height for the specified ledgers. 339 func LoadPreResetHeight(blockstorePath string, ledgerIDs []string) (map[string]uint64, error) { 340 return fsblkstorage.LoadPreResetHeight(blockstorePath, ledgerIDs) 341 } 342 343 // ResetBlockStore resets all ledgers to the genesis block. 344 func ResetBlockStore(blockstorePath string) error { 345 return fsblkstorage.ResetBlockStore(blockstorePath) 346 } 347 348 // ValidateRollbackParams performs necessary validation on the input given for 349 // the rollback operation. 350 func ValidateRollbackParams(blockstorePath, ledgerID string, blockNum uint64) error { 351 return fsblkstorage.ValidateRollbackParams(blockstorePath, ledgerID, blockNum) 352 } 353 354 // Rollback reverts changes made to the block store beyond a given block number. 355 func Rollback(blockstorePath, ledgerID string, blockNum uint64) error { 356 indexConfig := &blkstorage.IndexConfig{AttrsToIndex: attrsToIndex} 357 return fsblkstorage.Rollback(blockstorePath, ledgerID, blockNum, indexConfig) 358 }