github.com/sykesm/fabric@v1.1.0-preview.0.20200129034918-2aa12b1a0181/common/ledger/blkstorage/fsblkstorage/blockindex.go (about) 1 /* 2 Copyright IBM Corp. All Rights Reserved. 3 4 SPDX-License-Identifier: Apache-2.0 5 */ 6 7 package fsblkstorage 8 9 import ( 10 "bytes" 11 "fmt" 12 13 "github.com/golang/protobuf/proto" 14 "github.com/hyperledger/fabric-protos-go/common" 15 "github.com/hyperledger/fabric-protos-go/peer" 16 "github.com/hyperledger/fabric/common/ledger/blkstorage" 17 "github.com/hyperledger/fabric/common/ledger/blkstorage/fsblkstorage/msgs" 18 "github.com/hyperledger/fabric/common/ledger/util" 19 "github.com/hyperledger/fabric/common/ledger/util/leveldbhelper" 20 ledgerUtil "github.com/hyperledger/fabric/core/ledger/util" 21 "github.com/pkg/errors" 22 ) 23 24 const ( 25 blockNumIdxKeyPrefix = 'n' 26 blockHashIdxKeyPrefix = 'h' 27 txIDIdxKeyPrefix = 't' 28 blockNumTranNumIdxKeyPrefix = 'a' 29 indexCheckpointKeyStr = "indexCheckpointKey" 30 ) 31 32 var indexCheckpointKey = []byte(indexCheckpointKeyStr) 33 var errIndexEmpty = errors.New("NoBlockIndexed") 34 35 type index interface { 36 getLastBlockIndexed() (uint64, error) 37 indexBlock(blockIdxInfo *blockIdxInfo) error 38 getBlockLocByHash(blockHash []byte) (*fileLocPointer, error) 39 getBlockLocByBlockNum(blockNum uint64) (*fileLocPointer, error) 40 getTxLoc(txID string) (*fileLocPointer, error) 41 getTXLocByBlockNumTranNum(blockNum uint64, tranNum uint64) (*fileLocPointer, error) 42 getBlockLocByTxID(txID string) (*fileLocPointer, error) 43 getTxValidationCodeByTxID(txID string) (peer.TxValidationCode, error) 44 isAttributeIndexed(attribute blkstorage.IndexableAttr) bool 45 } 46 47 type blockIdxInfo struct { 48 blockNum uint64 49 blockHash []byte 50 flp *fileLocPointer 51 txOffsets []*txindexInfo 52 metadata *common.BlockMetadata 53 } 54 55 type blockIndex struct { 56 indexItemsMap map[blkstorage.IndexableAttr]bool 57 db *leveldbhelper.DBHandle 58 } 59 60 func newBlockIndex(indexConfig *blkstorage.IndexConfig, db *leveldbhelper.DBHandle) (*blockIndex, error) { 61 indexItems := indexConfig.AttrsToIndex 62 logger.Debugf("newBlockIndex() - indexItems:[%s]", indexItems) 63 indexItemsMap := make(map[blkstorage.IndexableAttr]bool) 64 for _, indexItem := range indexItems { 65 indexItemsMap[indexItem] = true 66 } 67 return &blockIndex{indexItemsMap, db}, nil 68 } 69 70 func (index *blockIndex) getLastBlockIndexed() (uint64, error) { 71 var blockNumBytes []byte 72 var err error 73 if blockNumBytes, err = index.db.Get(indexCheckpointKey); err != nil { 74 return 0, err 75 } 76 if blockNumBytes == nil { 77 return 0, errIndexEmpty 78 } 79 return decodeBlockNum(blockNumBytes), nil 80 } 81 82 func (index *blockIndex) indexBlock(blockIdxInfo *blockIdxInfo) error { 83 // do not index anything 84 if len(index.indexItemsMap) == 0 { 85 logger.Debug("Not indexing block... as nothing to index") 86 return nil 87 } 88 logger.Debugf("Indexing block [%s]", blockIdxInfo) 89 flp := blockIdxInfo.flp 90 txOffsets := blockIdxInfo.txOffsets 91 blkNum := blockIdxInfo.blockNum 92 blkHash := blockIdxInfo.blockHash 93 txsfltr := ledgerUtil.TxValidationFlags(blockIdxInfo.metadata.Metadata[common.BlockMetadataIndex_TRANSACTIONS_FILTER]) 94 batch := leveldbhelper.NewUpdateBatch() 95 flpBytes, err := flp.marshal() 96 if err != nil { 97 return err 98 } 99 100 //Index1 101 if index.isAttributeIndexed(blkstorage.IndexableAttrBlockHash) { 102 batch.Put(constructBlockHashKey(blkHash), flpBytes) 103 } 104 105 //Index2 106 if index.isAttributeIndexed(blkstorage.IndexableAttrBlockNum) { 107 batch.Put(constructBlockNumKey(blkNum), flpBytes) 108 } 109 110 //Index3 Used to find a transaction by its transaction id 111 if index.isAttributeIndexed(blkstorage.IndexableAttrTxID) { 112 for i, txoffset := range txOffsets { 113 txFlp := newFileLocationPointer(flp.fileSuffixNum, flp.offset, txoffset.loc) 114 logger.Debugf("Adding txLoc [%s] for tx ID: [%s] to txid-index", txFlp, txoffset.txID) 115 txFlpBytes, marshalErr := txFlp.marshal() 116 if marshalErr != nil { 117 return marshalErr 118 } 119 120 indexVal := &msgs.TxIDIndexValProto{ 121 BlkLocation: flpBytes, 122 TxLocation: txFlpBytes, 123 TxValidationCode: int32(txsfltr.Flag(i)), 124 } 125 indexValBytes, err := proto.Marshal(indexVal) 126 if err != nil { 127 return errors.Wrap(err, "unexpected error while marshaling TxIDIndexValProto message") 128 } 129 batch.Put( 130 constructTxIDKey(txoffset.txID, blkNum, uint64(i)), 131 indexValBytes, 132 ) 133 } 134 } 135 136 //Index4 - Store BlockNumTranNum will be used to query history data 137 if index.isAttributeIndexed(blkstorage.IndexableAttrBlockNumTranNum) { 138 for i, txoffset := range txOffsets { 139 txFlp := newFileLocationPointer(flp.fileSuffixNum, flp.offset, txoffset.loc) 140 logger.Debugf("Adding txLoc [%s] for tx number:[%d] ID: [%s] to blockNumTranNum index", txFlp, i, txoffset.txID) 141 txFlpBytes, marshalErr := txFlp.marshal() 142 if marshalErr != nil { 143 return marshalErr 144 } 145 batch.Put(constructBlockNumTranNumKey(blkNum, uint64(i)), txFlpBytes) 146 } 147 } 148 149 batch.Put(indexCheckpointKey, encodeBlockNum(blockIdxInfo.blockNum)) 150 // Setting snyc to true as a precaution, false may be an ok optimization after further testing. 151 if err := index.db.WriteBatch(batch, true); err != nil { 152 return err 153 } 154 return nil 155 } 156 157 func (index *blockIndex) isAttributeIndexed(attribute blkstorage.IndexableAttr) bool { 158 _, ok := index.indexItemsMap[attribute] 159 return ok 160 } 161 162 func (index *blockIndex) getBlockLocByHash(blockHash []byte) (*fileLocPointer, error) { 163 if !index.isAttributeIndexed(blkstorage.IndexableAttrBlockHash) { 164 return nil, blkstorage.ErrAttrNotIndexed 165 } 166 b, err := index.db.Get(constructBlockHashKey(blockHash)) 167 if err != nil { 168 return nil, err 169 } 170 if b == nil { 171 return nil, blkstorage.ErrNotFoundInIndex 172 } 173 blkLoc := &fileLocPointer{} 174 blkLoc.unmarshal(b) 175 return blkLoc, nil 176 } 177 178 func (index *blockIndex) getBlockLocByBlockNum(blockNum uint64) (*fileLocPointer, error) { 179 if !index.isAttributeIndexed(blkstorage.IndexableAttrBlockNum) { 180 return nil, blkstorage.ErrAttrNotIndexed 181 } 182 b, err := index.db.Get(constructBlockNumKey(blockNum)) 183 if err != nil { 184 return nil, err 185 } 186 if b == nil { 187 return nil, blkstorage.ErrNotFoundInIndex 188 } 189 blkLoc := &fileLocPointer{} 190 blkLoc.unmarshal(b) 191 return blkLoc, nil 192 } 193 194 func (index *blockIndex) getTxLoc(txID string) (*fileLocPointer, error) { 195 v, err := index.getTxIDVal(txID) 196 if err != nil { 197 return nil, err 198 } 199 txFLP := &fileLocPointer{} 200 if err = txFLP.unmarshal(v.TxLocation); err != nil { 201 return nil, err 202 } 203 return txFLP, nil 204 } 205 206 func (index *blockIndex) getBlockLocByTxID(txID string) (*fileLocPointer, error) { 207 v, err := index.getTxIDVal(txID) 208 if err != nil { 209 return nil, err 210 } 211 blkFLP := &fileLocPointer{} 212 if err = blkFLP.unmarshal(v.BlkLocation); err != nil { 213 return nil, err 214 } 215 return blkFLP, nil 216 } 217 218 func (index *blockIndex) getTxValidationCodeByTxID(txID string) (peer.TxValidationCode, error) { 219 v, err := index.getTxIDVal(txID) 220 if err != nil { 221 return peer.TxValidationCode(-1), err 222 } 223 return peer.TxValidationCode(v.TxValidationCode), nil 224 } 225 226 func (index *blockIndex) getTxIDVal(txID string) (*msgs.TxIDIndexValProto, error) { 227 if !index.isAttributeIndexed(blkstorage.IndexableAttrTxID) { 228 return nil, blkstorage.ErrAttrNotIndexed 229 } 230 rangeScan := constructTxIDRangeScan(txID) 231 itr := index.db.GetIterator(rangeScan.startKey, rangeScan.stopKey) 232 defer itr.Release() 233 234 present := itr.Next() 235 if err := itr.Error(); err != nil { 236 return nil, errors.Wrapf(err, "error while trying to retrieve transaction info by TXID [%s]", txID) 237 } 238 if !present { 239 return nil, blkstorage.ErrNotFoundInIndex 240 } 241 valBytes := itr.Value() 242 val := &msgs.TxIDIndexValProto{} 243 if err := proto.Unmarshal(valBytes, val); err != nil { 244 return nil, errors.Wrapf(err, "unexpected error while unmarshaling bytes [%#v] into TxIDIndexValProto", valBytes) 245 } 246 return val, nil 247 } 248 249 func (index *blockIndex) getTXLocByBlockNumTranNum(blockNum uint64, tranNum uint64) (*fileLocPointer, error) { 250 if !index.isAttributeIndexed(blkstorage.IndexableAttrBlockNumTranNum) { 251 return nil, blkstorage.ErrAttrNotIndexed 252 } 253 b, err := index.db.Get(constructBlockNumTranNumKey(blockNum, tranNum)) 254 if err != nil { 255 return nil, err 256 } 257 if b == nil { 258 return nil, blkstorage.ErrNotFoundInIndex 259 } 260 txFLP := &fileLocPointer{} 261 txFLP.unmarshal(b) 262 return txFLP, nil 263 } 264 265 func constructBlockNumKey(blockNum uint64) []byte { 266 blkNumBytes := util.EncodeOrderPreservingVarUint64(blockNum) 267 return append([]byte{blockNumIdxKeyPrefix}, blkNumBytes...) 268 } 269 270 func constructBlockHashKey(blockHash []byte) []byte { 271 return append([]byte{blockHashIdxKeyPrefix}, blockHash...) 272 } 273 274 func constructTxIDKey(txID string, blkNum, txNum uint64) []byte { 275 k := append( 276 []byte{txIDIdxKeyPrefix}, 277 util.EncodeOrderPreservingVarUint64(uint64(len(txID)))..., 278 ) 279 k = append(k, txID...) 280 k = append(k, util.EncodeOrderPreservingVarUint64(blkNum)...) 281 return append(k, util.EncodeOrderPreservingVarUint64(txNum)...) 282 } 283 284 type rangeScan struct { 285 startKey []byte 286 stopKey []byte 287 } 288 289 func constructTxIDRangeScan(txID string) *rangeScan { 290 sk := append( 291 []byte{txIDIdxKeyPrefix}, 292 util.EncodeOrderPreservingVarUint64(uint64(len(txID)))..., 293 ) 294 sk = append(sk, txID...) 295 return &rangeScan{ 296 startKey: sk, 297 stopKey: append(sk, 0xff), 298 } 299 } 300 301 func constructBlockNumTranNumKey(blockNum uint64, txNum uint64) []byte { 302 blkNumBytes := util.EncodeOrderPreservingVarUint64(blockNum) 303 tranNumBytes := util.EncodeOrderPreservingVarUint64(txNum) 304 key := append(blkNumBytes, tranNumBytes...) 305 return append([]byte{blockNumTranNumIdxKeyPrefix}, key...) 306 } 307 308 func encodeBlockNum(blockNum uint64) []byte { 309 return proto.EncodeVarint(blockNum) 310 } 311 312 func decodeBlockNum(blockNumBytes []byte) uint64 { 313 blockNum, _ := proto.DecodeVarint(blockNumBytes) 314 return blockNum 315 } 316 317 type locPointer struct { 318 offset int 319 bytesLength int 320 } 321 322 func (lp *locPointer) String() string { 323 return fmt.Sprintf("offset=%d, bytesLength=%d", 324 lp.offset, lp.bytesLength) 325 } 326 327 // fileLocPointer 328 type fileLocPointer struct { 329 fileSuffixNum int 330 locPointer 331 } 332 333 func newFileLocationPointer(fileSuffixNum int, beginningOffset int, relativeLP *locPointer) *fileLocPointer { 334 flp := &fileLocPointer{fileSuffixNum: fileSuffixNum} 335 flp.offset = beginningOffset + relativeLP.offset 336 flp.bytesLength = relativeLP.bytesLength 337 return flp 338 } 339 340 func (flp *fileLocPointer) marshal() ([]byte, error) { 341 buffer := proto.NewBuffer([]byte{}) 342 e := buffer.EncodeVarint(uint64(flp.fileSuffixNum)) 343 if e != nil { 344 return nil, errors.Wrapf(e, "unexpected error while marshaling fileLocPointer [%s]", flp) 345 } 346 e = buffer.EncodeVarint(uint64(flp.offset)) 347 if e != nil { 348 return nil, errors.Wrapf(e, "unexpected error while marshaling fileLocPointer [%s]", flp) 349 } 350 e = buffer.EncodeVarint(uint64(flp.bytesLength)) 351 if e != nil { 352 return nil, errors.Wrapf(e, "unexpected error while marshaling fileLocPointer [%s]", flp) 353 } 354 return buffer.Bytes(), nil 355 } 356 357 func (flp *fileLocPointer) unmarshal(b []byte) error { 358 buffer := proto.NewBuffer(b) 359 i, e := buffer.DecodeVarint() 360 if e != nil { 361 return errors.Wrapf(e, "unexpected error while unmarshaling bytes [%#v] into fileLocPointer", b) 362 } 363 flp.fileSuffixNum = int(i) 364 365 i, e = buffer.DecodeVarint() 366 if e != nil { 367 return errors.Wrapf(e, "unexpected error while unmarshaling bytes [%#v] into fileLocPointer", b) 368 } 369 flp.offset = int(i) 370 i, e = buffer.DecodeVarint() 371 if e != nil { 372 return errors.Wrapf(e, "unexpected error while unmarshaling bytes [%#v] into fileLocPointer", b) 373 } 374 flp.bytesLength = int(i) 375 return nil 376 } 377 378 func (flp *fileLocPointer) String() string { 379 return fmt.Sprintf("fileSuffixNum=%d, %s", flp.fileSuffixNum, flp.locPointer.String()) 380 } 381 382 func (blockIdxInfo *blockIdxInfo) String() string { 383 384 var buffer bytes.Buffer 385 for _, txOffset := range blockIdxInfo.txOffsets { 386 buffer.WriteString("txId=") 387 buffer.WriteString(txOffset.txID) 388 buffer.WriteString(" locPointer=") 389 buffer.WriteString(txOffset.loc.String()) 390 buffer.WriteString("\n") 391 } 392 txOffsetsString := buffer.String() 393 394 return fmt.Sprintf("blockNum=%d, blockHash=%#v txOffsets=\n%s", blockIdxInfo.blockNum, blockIdxInfo.blockHash, txOffsetsString) 395 }