github.com/hechain20/hechain@v0.0.0-20220316014945-b544036ba106/common/ledger/blkstorage/blockindex.go (about) 1 /* 2 Copyright hechain. All Rights Reserved. 3 4 SPDX-License-Identifier: Apache-2.0 5 */ 6 7 package blkstorage 8 9 import ( 10 "bytes" 11 "fmt" 12 "path/filepath" 13 "unicode/utf8" 14 15 "github.com/golang/protobuf/proto" 16 "github.com/hechain20/hechain/common/ledger/snapshot" 17 "github.com/hechain20/hechain/common/ledger/util" 18 "github.com/hechain20/hechain/common/ledger/util/leveldbhelper" 19 "github.com/hechain20/hechain/internal/pkg/txflags" 20 "github.com/hyperledger/fabric-protos-go/common" 21 "github.com/hyperledger/fabric-protos-go/peer" 22 "github.com/pkg/errors" 23 ) 24 25 const ( 26 blockNumIdxKeyPrefix = 'n' 27 blockHashIdxKeyPrefix = 'h' 28 txIDIdxKeyPrefix = 't' 29 blockNumTranNumIdxKeyPrefix = 'a' 30 indexSavePointKeyStr = "indexCheckpointKey" 31 32 snapshotFileFormat = byte(1) 33 snapshotDataFileName = "txids.data" 34 snapshotMetadataFileName = "txids.metadata" 35 ) 36 37 var ( 38 indexSavePointKey = []byte(indexSavePointKeyStr) 39 errIndexSavePointKeyNotPresent = errors.New("NoBlockIndexed") 40 errNilValue = errors.New("") 41 importTxIDsBatchSize = uint64(10000) // txID is 64 bytes, so batch size roughly translates to 640KB 42 ) 43 44 type blockIdxInfo struct { 45 blockNum uint64 46 blockHash []byte 47 flp *fileLocPointer 48 txOffsets []*txindexInfo 49 metadata *common.BlockMetadata 50 } 51 52 type blockIndex struct { 53 indexItemsMap map[IndexableAttr]bool 54 db *leveldbhelper.DBHandle 55 } 56 57 func newBlockIndex(indexConfig *IndexConfig, db *leveldbhelper.DBHandle) (*blockIndex, error) { 58 indexItems := indexConfig.AttrsToIndex 59 logger.Debugf("newBlockIndex() - indexItems:[%s]", indexItems) 60 indexItemsMap := make(map[IndexableAttr]bool) 61 for _, indexItem := range indexItems { 62 indexItemsMap[indexItem] = true 63 } 64 return &blockIndex{ 65 indexItemsMap: indexItemsMap, 66 db: db, 67 }, 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(indexSavePointKey); err != nil { 74 return 0, err 75 } 76 if blockNumBytes == nil { 77 return 0, errIndexSavePointKeyNotPresent 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 := txflags.ValidationFlags(blockIdxInfo.metadata.Metadata[common.BlockMetadataIndex_TRANSACTIONS_FILTER]) 94 batch := index.db.NewUpdateBatch() 95 flpBytes, err := flp.marshal() 96 if err != nil { 97 return err 98 } 99 100 // Index1 101 if index.isAttributeIndexed(IndexableAttrBlockHash) { 102 batch.Put(constructBlockHashKey(blkHash), flpBytes) 103 } 104 105 // Index2 106 if index.isAttributeIndexed(IndexableAttrBlockNum) { 107 batch.Put(constructBlockNumKey(blkNum), flpBytes) 108 } 109 110 // Index3 Used to find a transaction by its transaction id 111 if index.isAttributeIndexed(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 := &TxIDIndexValue{ 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(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(indexSavePointKey, 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 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(IndexableAttrBlockHash) { 164 return nil, errors.New("block hashes not maintained in index") 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, errors.Errorf("no such block hash [%x] in index", blockHash) 172 } 173 blkLoc := &fileLocPointer{} 174 if err := blkLoc.unmarshal(b); err != nil { 175 return nil, err 176 } 177 return blkLoc, nil 178 } 179 180 func (index *blockIndex) getBlockLocByBlockNum(blockNum uint64) (*fileLocPointer, error) { 181 if !index.isAttributeIndexed(IndexableAttrBlockNum) { 182 return nil, errors.New("block numbers not maintained in index") 183 } 184 b, err := index.db.Get(constructBlockNumKey(blockNum)) 185 if err != nil { 186 return nil, err 187 } 188 if b == nil { 189 return nil, errors.Errorf("no such block number [%d] in index", blockNum) 190 } 191 blkLoc := &fileLocPointer{} 192 if err := blkLoc.unmarshal(b); err != nil { 193 return nil, err 194 } 195 return blkLoc, nil 196 } 197 198 func (index *blockIndex) getTxLoc(txID string) (*fileLocPointer, error) { 199 v, _, err := index.getTxIDVal(txID) 200 if err != nil { 201 return nil, err 202 } 203 txFLP := &fileLocPointer{} 204 if err = txFLP.unmarshal(v.TxLocation); err != nil { 205 return nil, err 206 } 207 return txFLP, nil 208 } 209 210 func (index *blockIndex) getBlockLocByTxID(txID string) (*fileLocPointer, error) { 211 v, _, err := index.getTxIDVal(txID) 212 if err != nil { 213 return nil, err 214 } 215 blkFLP := &fileLocPointer{} 216 if err = blkFLP.unmarshal(v.BlkLocation); err != nil { 217 return nil, err 218 } 219 return blkFLP, nil 220 } 221 222 func (index *blockIndex) getTxValidationCodeByTxID(txID string) (peer.TxValidationCode, uint64, error) { 223 v, blkNum, err := index.getTxIDVal(txID) 224 if err != nil { 225 return peer.TxValidationCode(-1), 0, err 226 } 227 return peer.TxValidationCode(v.TxValidationCode), blkNum, nil 228 } 229 230 func (index *blockIndex) txIDExists(txID string) (bool, error) { 231 if !index.isAttributeIndexed(IndexableAttrTxID) { 232 return false, errors.New("transaction IDs not maintained in index") 233 } 234 rangeScan := constructTxIDRangeScan(txID) 235 itr, err := index.db.GetIterator(rangeScan.startKey, rangeScan.stopKey) 236 if err != nil { 237 return false, errors.WithMessagef(err, "error while trying to check the presence of TXID [%s]", txID) 238 } 239 defer itr.Release() 240 241 present := itr.Next() 242 if err := itr.Error(); err != nil { 243 return false, errors.Wrapf(err, "error while trying to check the presence of TXID [%s]", txID) 244 } 245 return present, nil 246 } 247 248 func (index *blockIndex) getTxIDVal(txID string) (*TxIDIndexValue, uint64, error) { 249 if !index.isAttributeIndexed(IndexableAttrTxID) { 250 return nil, 0, errors.New("transaction IDs not maintained in index") 251 } 252 rangeScan := constructTxIDRangeScan(txID) 253 itr, err := index.db.GetIterator(rangeScan.startKey, rangeScan.stopKey) 254 if err != nil { 255 return nil, 0, errors.WithMessagef(err, "error while trying to retrieve transaction info by TXID [%s]", txID) 256 } 257 defer itr.Release() 258 259 present := itr.Next() 260 if err := itr.Error(); err != nil { 261 return nil, 0, errors.Wrapf(err, "error while trying to retrieve transaction info by TXID [%s]", txID) 262 } 263 if !present { 264 return nil, 0, errors.Errorf("no such transaction ID [%s] in index", txID) 265 } 266 valBytes := itr.Value() 267 if len(valBytes) == 0 { 268 return nil, 0, errNilValue 269 } 270 val := &TxIDIndexValue{} 271 if err := proto.Unmarshal(valBytes, val); err != nil { 272 return nil, 0, errors.Wrapf(err, "unexpected error while unmarshalling bytes [%#v] into TxIDIndexValProto", valBytes) 273 } 274 blockNum, err := retrieveBlockNum(itr.Key(), len(rangeScan.startKey)) 275 if err != nil { 276 return nil, 0, errors.WithMessage(err, "error while decoding block number from txID index key") 277 } 278 return val, blockNum, nil 279 } 280 281 func (index *blockIndex) getTXLocByBlockNumTranNum(blockNum uint64, tranNum uint64) (*fileLocPointer, error) { 282 if !index.isAttributeIndexed(IndexableAttrBlockNumTranNum) { 283 return nil, errors.New("<blockNumber, transactionNumber> tuple not maintained in index") 284 } 285 b, err := index.db.Get(constructBlockNumTranNumKey(blockNum, tranNum)) 286 if err != nil { 287 return nil, err 288 } 289 if b == nil { 290 return nil, errors.Errorf("no such blockNumber, transactionNumber <%d, %d> in index", blockNum, tranNum) 291 } 292 txFLP := &fileLocPointer{} 293 if err := txFLP.unmarshal(b); err != nil { 294 return nil, err 295 } 296 return txFLP, nil 297 } 298 299 func (index *blockIndex) exportUniqueTxIDs(dir string, newHashFunc snapshot.NewHashFunc) (map[string][]byte, error) { 300 if !index.isAttributeIndexed(IndexableAttrTxID) { 301 return nil, errors.New("transaction IDs not maintained in index") 302 } 303 304 dbItr, err := index.db.GetIterator([]byte{txIDIdxKeyPrefix}, []byte{txIDIdxKeyPrefix + 1}) 305 if err != nil { 306 return nil, err 307 } 308 defer dbItr.Release() 309 310 var previousTxID string 311 var numTxIDs uint64 = 0 312 var dataFile *snapshot.FileWriter 313 for dbItr.Next() { 314 if err := dbItr.Error(); err != nil { 315 return nil, errors.Wrap(err, "internal leveldb error while iterating for txids") 316 } 317 txID, err := retrieveTxID(dbItr.Key()) 318 if err != nil { 319 return nil, err 320 } 321 // duplicate TxID may be present in the index 322 if previousTxID == txID { 323 continue 324 } 325 previousTxID = txID 326 if numTxIDs == 0 { // first iteration, create the data file 327 dataFile, err = snapshot.CreateFile(filepath.Join(dir, snapshotDataFileName), snapshotFileFormat, newHashFunc) 328 if err != nil { 329 return nil, err 330 } 331 defer dataFile.Close() 332 } 333 if err := dataFile.EncodeString(txID); err != nil { 334 return nil, err 335 } 336 numTxIDs++ 337 } 338 339 if dataFile == nil { 340 return nil, nil 341 } 342 343 dataHash, err := dataFile.Done() 344 if err != nil { 345 return nil, err 346 } 347 348 // create the metadata file 349 metadataFile, err := snapshot.CreateFile(filepath.Join(dir, snapshotMetadataFileName), snapshotFileFormat, newHashFunc) 350 if err != nil { 351 return nil, err 352 } 353 defer metadataFile.Close() 354 355 if err = metadataFile.EncodeUVarint(numTxIDs); err != nil { 356 return nil, err 357 } 358 metadataHash, err := metadataFile.Done() 359 if err != nil { 360 return nil, err 361 } 362 363 return map[string][]byte{ 364 snapshotDataFileName: dataHash, 365 snapshotMetadataFileName: metadataHash, 366 }, nil 367 } 368 369 func importTxIDsFromSnapshot( 370 snapshotDir string, 371 lastBlockNumInSnapshot uint64, 372 db *leveldbhelper.DBHandle) error { 373 txIDsMetadata, err := snapshot.OpenFile(filepath.Join(snapshotDir, snapshotMetadataFileName), snapshotFileFormat) 374 if err != nil { 375 return err 376 } 377 numTxIDs, err := txIDsMetadata.DecodeUVarInt() 378 if err != nil { 379 return err 380 } 381 txIDsData, err := snapshot.OpenFile(filepath.Join(snapshotDir, snapshotDataFileName), snapshotFileFormat) 382 if err != nil { 383 return err 384 } 385 386 batch := db.NewUpdateBatch() 387 for i := uint64(0); i < numTxIDs; i++ { 388 txID, err := txIDsData.DecodeString() 389 if err != nil { 390 return err 391 } 392 batch.Put( 393 constructTxIDKey(txID, lastBlockNumInSnapshot, uint64(i)), 394 []byte{}, 395 ) 396 if (i+1)%importTxIDsBatchSize == 0 { 397 if err := db.WriteBatch(batch, true); err != nil { 398 return err 399 } 400 batch.Reset() 401 } 402 } 403 batch.Put(indexSavePointKey, encodeBlockNum(lastBlockNumInSnapshot)) 404 if err := db.WriteBatch(batch, true); err != nil { 405 return err 406 } 407 return nil 408 } 409 410 func constructBlockNumKey(blockNum uint64) []byte { 411 blkNumBytes := util.EncodeOrderPreservingVarUint64(blockNum) 412 return append([]byte{blockNumIdxKeyPrefix}, blkNumBytes...) 413 } 414 415 func constructBlockHashKey(blockHash []byte) []byte { 416 return append([]byte{blockHashIdxKeyPrefix}, blockHash...) 417 } 418 419 func constructTxIDKey(txID string, blkNum, txNum uint64) []byte { 420 k := append( 421 []byte{txIDIdxKeyPrefix}, 422 util.EncodeOrderPreservingVarUint64(uint64(len(txID)))..., 423 ) 424 k = append(k, txID...) 425 k = append(k, util.EncodeOrderPreservingVarUint64(blkNum)...) 426 return append(k, util.EncodeOrderPreservingVarUint64(txNum)...) 427 } 428 429 // retrieveTxID takes input an encoded txid key of the format `prefix:len(TxID):TxID:BlkNum:TxNum` 430 // and returns the TxID from this 431 func retrieveTxID(encodedTxIDKey []byte) (string, error) { 432 if len(encodedTxIDKey) == 0 { 433 return "", errors.New("invalid txIDKey - zero-length slice") 434 } 435 if encodedTxIDKey[0] != txIDIdxKeyPrefix { 436 return "", errors.Errorf("invalid txIDKey {%x} - unexpected prefix", encodedTxIDKey) 437 } 438 remainingBytes := encodedTxIDKey[utf8.RuneLen(txIDIdxKeyPrefix):] 439 440 txIDLen, n, err := util.DecodeOrderPreservingVarUint64(remainingBytes) 441 if err != nil { 442 return "", errors.WithMessagef(err, "invalid txIDKey {%x}", encodedTxIDKey) 443 } 444 remainingBytes = remainingBytes[n:] 445 if len(remainingBytes) <= int(txIDLen) { 446 return "", errors.Errorf("invalid txIDKey {%x}, fewer bytes present", encodedTxIDKey) 447 } 448 return string(remainingBytes[:int(txIDLen)]), nil 449 } 450 451 func retrieveBlockNum(encodedTxIDKey []byte, BlkNumStartingIndex int) (uint64, error) { 452 n, _, err := util.DecodeOrderPreservingVarUint64(encodedTxIDKey[BlkNumStartingIndex:]) 453 return n, err 454 } 455 456 type rangeScan struct { 457 startKey []byte 458 stopKey []byte 459 } 460 461 func constructTxIDRangeScan(txID string) *rangeScan { 462 sk := append( 463 []byte{txIDIdxKeyPrefix}, 464 util.EncodeOrderPreservingVarUint64(uint64(len(txID)))..., 465 ) 466 sk = append(sk, txID...) 467 return &rangeScan{ 468 startKey: sk, 469 stopKey: append(sk, 0xff), 470 } 471 } 472 473 func constructBlockNumTranNumKey(blockNum uint64, txNum uint64) []byte { 474 blkNumBytes := util.EncodeOrderPreservingVarUint64(blockNum) 475 tranNumBytes := util.EncodeOrderPreservingVarUint64(txNum) 476 key := append(blkNumBytes, tranNumBytes...) 477 return append([]byte{blockNumTranNumIdxKeyPrefix}, key...) 478 } 479 480 func encodeBlockNum(blockNum uint64) []byte { 481 return proto.EncodeVarint(blockNum) 482 } 483 484 func decodeBlockNum(blockNumBytes []byte) uint64 { 485 blockNum, _ := proto.DecodeVarint(blockNumBytes) 486 return blockNum 487 } 488 489 type locPointer struct { 490 offset int 491 bytesLength int 492 } 493 494 func (lp *locPointer) String() string { 495 return fmt.Sprintf("offset=%d, bytesLength=%d", 496 lp.offset, lp.bytesLength) 497 } 498 499 // fileLocPointer 500 type fileLocPointer struct { 501 fileSuffixNum int 502 locPointer 503 } 504 505 func newFileLocationPointer(fileSuffixNum int, beginningOffset int, relativeLP *locPointer) *fileLocPointer { 506 flp := &fileLocPointer{fileSuffixNum: fileSuffixNum} 507 flp.offset = beginningOffset + relativeLP.offset 508 flp.bytesLength = relativeLP.bytesLength 509 return flp 510 } 511 512 func (flp *fileLocPointer) marshal() ([]byte, error) { 513 buffer := proto.NewBuffer([]byte{}) 514 e := buffer.EncodeVarint(uint64(flp.fileSuffixNum)) 515 if e != nil { 516 return nil, errors.Wrapf(e, "unexpected error while marshaling fileLocPointer [%s]", flp) 517 } 518 e = buffer.EncodeVarint(uint64(flp.offset)) 519 if e != nil { 520 return nil, errors.Wrapf(e, "unexpected error while marshaling fileLocPointer [%s]", flp) 521 } 522 e = buffer.EncodeVarint(uint64(flp.bytesLength)) 523 if e != nil { 524 return nil, errors.Wrapf(e, "unexpected error while marshaling fileLocPointer [%s]", flp) 525 } 526 return buffer.Bytes(), nil 527 } 528 529 func (flp *fileLocPointer) unmarshal(b []byte) error { 530 buffer := proto.NewBuffer(b) 531 i, e := buffer.DecodeVarint() 532 if e != nil { 533 return errors.Wrapf(e, "unexpected error while unmarshalling bytes [%#v] into fileLocPointer", b) 534 } 535 flp.fileSuffixNum = int(i) 536 537 i, e = buffer.DecodeVarint() 538 if e != nil { 539 return errors.Wrapf(e, "unexpected error while unmarshalling bytes [%#v] into fileLocPointer", b) 540 } 541 flp.offset = int(i) 542 i, e = buffer.DecodeVarint() 543 if e != nil { 544 return errors.Wrapf(e, "unexpected error while unmarshalling bytes [%#v] into fileLocPointer", b) 545 } 546 flp.bytesLength = int(i) 547 return nil 548 } 549 550 func (flp *fileLocPointer) String() string { 551 return fmt.Sprintf("fileSuffixNum=%d, %s", flp.fileSuffixNum, flp.locPointer.String()) 552 } 553 554 func (blockIdxInfo *blockIdxInfo) String() string { 555 var buffer bytes.Buffer 556 for _, txOffset := range blockIdxInfo.txOffsets { 557 buffer.WriteString("txId=") 558 buffer.WriteString(txOffset.txID) 559 buffer.WriteString(" locPointer=") 560 buffer.WriteString(txOffset.loc.String()) 561 buffer.WriteString("\n") 562 } 563 txOffsetsString := buffer.String() 564 565 return fmt.Sprintf("blockNum=%d, blockHash=%#v txOffsets=\n%s", blockIdxInfo.blockNum, blockIdxInfo.blockHash, txOffsetsString) 566 }