You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

133 lines
3.3 KiB

  1. package blocks
  2. import (
  3. "bytes"
  4. "encoding/binary"
  5. "encoding/json"
  6. "github.com/syndtr/goleveldb/leveldb"
  7. "github.com/syndtr/goleveldb/leveldb/opt"
  8. . "github.com/tendermint/tendermint/binary"
  9. . "github.com/tendermint/tendermint/common"
  10. )
  11. var (
  12. blockStoreKey = []byte("blockStore")
  13. )
  14. //-----------------------------------------------------------------------------
  15. type BlockStoreJSON struct {
  16. Height uint32
  17. }
  18. func (bsj BlockStoreJSON) Save(db *leveldb.DB) {
  19. bytes, err := json.Marshal(bsj)
  20. if err != nil {
  21. Panicf("Could not marshal state bytes: %v", err)
  22. }
  23. db.Put(blockStoreKey, bytes, nil)
  24. }
  25. func LoadBlockStoreJSON(db *leveldb.DB) BlockStoreJSON {
  26. bytes, err := db.Get(blockStoreKey, nil)
  27. if err != nil {
  28. Panicf("Could not load BlockStoreJSON from db: %v", err)
  29. }
  30. if bytes == nil {
  31. return BlockStoreJSON{
  32. Height: 0,
  33. }
  34. }
  35. bsj := BlockStoreJSON{}
  36. err = json.Unmarshal(bytes, &bsj)
  37. if err != nil {
  38. Panicf("Could not unmarshal bytes: %X", bytes)
  39. }
  40. return bsj
  41. }
  42. //-----------------------------------------------------------------------------
  43. /*
  44. Simple low level store for blocks, which is actually stored as separte parts (wire format).
  45. */
  46. type BlockStore struct {
  47. height uint32
  48. db *leveldb.DB
  49. }
  50. func NewBlockStore(db *leveldb.DB) *BlockStore {
  51. bsjson := LoadBlockStoreJSON(db)
  52. return &BlockStore{
  53. height: bsjson.Height,
  54. db: db,
  55. }
  56. }
  57. // Height() returns the last known contiguous block height.
  58. func (bs *BlockStore) Height() uint32 {
  59. return bs.height
  60. }
  61. // LoadBlockPart loads a part of a block.
  62. func (bs *BlockStore) LoadBlockPart(height uint32, index uint16) *BlockPart {
  63. partBytes, err := bs.db.Get(calcBlockPartKey(height, index), nil)
  64. if err != nil {
  65. Panicf("Could not load block part: %v", err)
  66. }
  67. if partBytes == nil {
  68. return nil
  69. }
  70. var n int64
  71. return ReadBlockPart(bytes.NewReader(partBytes), &n, &err)
  72. }
  73. // Convenience method for loading block parts and merging to a block.
  74. func (bs *BlockStore) LoadBlock(height uint32) *Block {
  75. // Get the first part.
  76. part0 := bs.LoadBlockPart(height, 0)
  77. if part0 == nil {
  78. return nil
  79. }
  80. parts := []*BlockPart{part0}
  81. for i := uint16(1); i < part0.Total; i++ {
  82. part := bs.LoadBlockPart(height, i)
  83. if part == nil {
  84. Panicf("Failed to retrieve block part %v at height %v", i, height)
  85. }
  86. parts = append(parts, part)
  87. }
  88. block, err := BlockPartsToBlock(parts)
  89. if err != nil {
  90. panic(err)
  91. }
  92. return block
  93. }
  94. // NOTE: Assumes that parts as well as the block are valid. See StageBlockParts().
  95. // Writes are synchronous and atomic.
  96. func (bs *BlockStore) SaveBlockParts(height uint32, parts []*BlockPart) error {
  97. if height != bs.height+1 {
  98. return Errorf("BlockStore can only save contiguous blocks. Wanted %v, got %v", bs.height+1, height)
  99. }
  100. // Save parts
  101. batch := new(leveldb.Batch)
  102. for _, part := range parts {
  103. partBytes := BinaryBytes(part)
  104. batch.Put(calcBlockPartKey(uint32(part.Height), uint16(part.Index)), partBytes)
  105. }
  106. err := bs.db.Write(batch, &opt.WriteOptions{Sync: true})
  107. // Save new BlockStoreJSON descriptor
  108. BlockStoreJSON{Height: height}.Save(bs.db)
  109. return err
  110. }
  111. //-----------------------------------------------------------------------------
  112. func calcBlockPartKey(height uint32, index uint16) []byte {
  113. buf := [11]byte{'B'}
  114. binary.BigEndian.PutUint32(buf[1:9], height)
  115. binary.BigEndian.PutUint16(buf[9:11], index)
  116. return buf[:]
  117. }