github.com/Bytom/bytom@v1.1.2-0.20210127130405-ae40204c0b09/database/store.go (about) 1 package database 2 3 import ( 4 "encoding/binary" 5 "encoding/json" 6 "time" 7 8 "github.com/golang/protobuf/proto" 9 log "github.com/sirupsen/logrus" 10 "github.com/tendermint/tmlibs/common" 11 12 "github.com/bytom/bytom/database/storage" 13 "github.com/bytom/bytom/errors" 14 "github.com/bytom/bytom/protocol" 15 "github.com/bytom/bytom/protocol/bc" 16 "github.com/bytom/bytom/protocol/bc/types" 17 "github.com/bytom/bytom/protocol/state" 18 dbm "github.com/bytom/bytom/database/leveldb" 19 ) 20 21 const logModule = "leveldb" 22 23 var ( 24 BlockStoreKey = []byte("blockStore") 25 BlockPrefix = []byte("B:") 26 BlockHeaderPrefix = []byte("BH:") 27 TxStatusPrefix = []byte("BTS:") 28 ) 29 30 func loadBlockStoreStateJSON(db dbm.DB) *protocol.BlockStoreState { 31 bytes := db.Get(BlockStoreKey) 32 if bytes == nil { 33 return nil 34 } 35 bsj := &protocol.BlockStoreState{} 36 if err := json.Unmarshal(bytes, bsj); err != nil { 37 common.PanicCrisis(common.Fmt("Could not unmarshal bytes: %X", bytes)) 38 } 39 return bsj 40 } 41 42 // A Store encapsulates storage for blockchain validation. 43 // It satisfies the interface protocol.Store, and provides additional 44 // methods for querying current data. 45 type Store struct { 46 db dbm.DB 47 cache blockCache 48 } 49 50 func CalcBlockKey(hash *bc.Hash) []byte { 51 return append(BlockPrefix, hash.Bytes()...) 52 } 53 54 func CalcBlockHeaderKey(height uint64, hash *bc.Hash) []byte { 55 buf := [8]byte{} 56 binary.BigEndian.PutUint64(buf[:], height) 57 key := append(BlockHeaderPrefix, buf[:]...) 58 return append(key, hash.Bytes()...) 59 } 60 61 func CalcTxStatusKey(hash *bc.Hash) []byte { 62 return append(TxStatusPrefix, hash.Bytes()...) 63 } 64 65 // GetBlock return the block by given hash 66 func GetBlock(db dbm.DB, hash *bc.Hash) (*types.Block, error) { 67 bytez := db.Get(CalcBlockKey(hash)) 68 if bytez == nil { 69 return nil, nil 70 } 71 72 block := &types.Block{} 73 err := block.UnmarshalText(bytez) 74 return block, err 75 } 76 77 // NewStore creates and returns a new Store object. 78 func NewStore(db dbm.DB) *Store { 79 cache := newBlockCache(func(hash *bc.Hash) (*types.Block, error) { 80 return GetBlock(db, hash) 81 }) 82 return &Store{ 83 db: db, 84 cache: cache, 85 } 86 } 87 88 // GetUtxo will search the utxo in db 89 func (s *Store) GetUtxo(hash *bc.Hash) (*storage.UtxoEntry, error) { 90 return getUtxo(s.db, hash) 91 } 92 93 // BlockExist check if the block is stored in disk 94 func (s *Store) BlockExist(hash *bc.Hash) bool { 95 block, err := s.cache.lookup(hash) 96 return err == nil && block != nil 97 } 98 99 // GetBlock return the block by given hash 100 func (s *Store) GetBlock(hash *bc.Hash) (*types.Block, error) { 101 return s.cache.lookup(hash) 102 } 103 104 // GetTransactionsUtxo will return all the utxo that related to the input txs 105 func (s *Store) GetTransactionsUtxo(view *state.UtxoViewpoint, txs []*bc.Tx) error { 106 return getTransactionsUtxo(s.db, view, txs) 107 } 108 109 // GetTransactionStatus will return the utxo that related to the block hash 110 func (s *Store) GetTransactionStatus(hash *bc.Hash) (*bc.TransactionStatus, error) { 111 data := s.db.Get(CalcTxStatusKey(hash)) 112 if data == nil { 113 return nil, errors.New("can't find the transaction status by given hash") 114 } 115 116 ts := &bc.TransactionStatus{} 117 if err := proto.Unmarshal(data, ts); err != nil { 118 return nil, errors.Wrap(err, "unmarshaling transaction status") 119 } 120 return ts, nil 121 } 122 123 // GetStoreStatus return the BlockStoreStateJSON 124 func (s *Store) GetStoreStatus() *protocol.BlockStoreState { 125 return loadBlockStoreStateJSON(s.db) 126 } 127 128 func (s *Store) LoadBlockIndex(stateBestHeight uint64) (*state.BlockIndex, error) { 129 startTime := time.Now() 130 blockIndex := state.NewBlockIndex() 131 bhIter := s.db.IteratorPrefix(BlockHeaderPrefix) 132 defer bhIter.Release() 133 134 var lastNode *state.BlockNode 135 for bhIter.Next() { 136 bh := &types.BlockHeader{} 137 if err := bh.UnmarshalText(bhIter.Value()); err != nil { 138 return nil, err 139 } 140 141 // If a block with a height greater than the best height of state is added to the index, 142 // It may cause a bug that the new block cant not be process properly. 143 if bh.Height > stateBestHeight { 144 break 145 } 146 147 var parent *state.BlockNode 148 if lastNode == nil || lastNode.Hash == bh.PreviousBlockHash { 149 parent = lastNode 150 } else { 151 parent = blockIndex.GetNode(&bh.PreviousBlockHash) 152 } 153 154 node, err := state.NewBlockNode(bh, parent) 155 if err != nil { 156 return nil, err 157 } 158 159 blockIndex.AddNode(node) 160 lastNode = node 161 } 162 163 log.WithFields(log.Fields{ 164 "module": logModule, 165 "height": stateBestHeight, 166 "duration": time.Since(startTime), 167 }).Debug("initialize load history block index from database") 168 return blockIndex, nil 169 } 170 171 // SaveBlock persists a new block in the protocol. 172 func (s *Store) SaveBlock(block *types.Block, ts *bc.TransactionStatus) error { 173 startTime := time.Now() 174 binaryBlock, err := block.MarshalText() 175 if err != nil { 176 return errors.Wrap(err, "Marshal block meta") 177 } 178 179 binaryBlockHeader, err := block.BlockHeader.MarshalText() 180 if err != nil { 181 return errors.Wrap(err, "Marshal block header") 182 } 183 184 binaryTxStatus, err := proto.Marshal(ts) 185 if err != nil { 186 return errors.Wrap(err, "marshal block transaction status") 187 } 188 189 blockHash := block.Hash() 190 batch := s.db.NewBatch() 191 batch.Set(CalcBlockKey(&blockHash), binaryBlock) 192 batch.Set(CalcBlockHeaderKey(block.Height, &blockHash), binaryBlockHeader) 193 batch.Set(CalcTxStatusKey(&blockHash), binaryTxStatus) 194 batch.Write() 195 196 log.WithFields(log.Fields{ 197 "module": logModule, 198 "height": block.Height, 199 "hash": blockHash.String(), 200 "duration": time.Since(startTime), 201 }).Info("block saved on disk") 202 return nil 203 } 204 205 // SaveChainStatus save the core's newest status && delete old status 206 func (s *Store) SaveChainStatus(node *state.BlockNode, view *state.UtxoViewpoint) error { 207 batch := s.db.NewBatch() 208 if err := saveUtxoView(batch, view); err != nil { 209 return err 210 } 211 212 bytes, err := json.Marshal(protocol.BlockStoreState{Height: node.Height, Hash: &node.Hash}) 213 if err != nil { 214 return err 215 } 216 217 batch.Set(BlockStoreKey, bytes) 218 batch.Write() 219 return nil 220 }