8 "github.com/golang/protobuf/proto"
9 log "github.com/sirupsen/logrus"
10 "github.com/tendermint/tmlibs/common"
11 dbm "github.com/tendermint/tmlibs/db"
13 "github.com/vapor/database/storage"
14 "github.com/vapor/errors"
15 "github.com/vapor/protocol"
16 "github.com/vapor/protocol/bc"
17 "github.com/vapor/protocol/bc/types"
18 "github.com/vapor/protocol/state"
21 const logModule = "leveldb"
24 blockStoreKey = []byte("blockStore")
25 blockPrefix = []byte("B:")
26 blockHeaderPrefix = []byte("BH:")
27 txStatusPrefix = []byte("BTS:")
28 cliamTxPreFix = []byte("Claim:")
29 dposPreFix = []byte("dpos:")
32 func loadBlockStoreStateJSON(db dbm.DB) *protocol.BlockStoreState {
33 bytes := db.Get(blockStoreKey)
37 bsj := &protocol.BlockStoreState{}
38 if err := json.Unmarshal(bytes, bsj); err != nil {
39 common.PanicCrisis(common.Fmt("Could not unmarshal bytes: %X", bytes))
44 // A Store encapsulates storage for blockchain validation.
45 // It satisfies the interface protocol.Store, and provides additional
46 // methods for querying current data.
52 func calcBlockKey(hash *bc.Hash) []byte {
53 return append(blockPrefix, hash.Bytes()...)
56 func calcBlockHeaderKey(height uint64, hash *bc.Hash) []byte {
58 binary.BigEndian.PutUint64(buf[:], height)
59 key := append(blockHeaderPrefix, buf[:]...)
60 return append(key, hash.Bytes()...)
63 func calcTxStatusKey(hash *bc.Hash) []byte {
64 return append(txStatusPrefix, hash.Bytes()...)
67 func calcClaimTxKey(hash *bc.Hash) []byte {
68 return append(cliamTxPreFix, hash.Bytes()...)
71 func calcDPosKey(hash *bc.Hash) []byte {
72 return append(dposPreFix, hash.Bytes()...)
75 // GetBlock return the block by given hash
76 func GetBlock(db dbm.DB, hash *bc.Hash) *types.Block {
77 bytez := db.Get(calcBlockKey(hash))
82 block := &types.Block{}
83 block.UnmarshalText(bytez)
87 // NewStore creates and returns a new Store object.
88 func NewStore(db dbm.DB) *Store {
89 cache := newBlockCache(func(hash *bc.Hash) *types.Block {
90 return GetBlock(db, hash)
98 // GetUtxo will search the utxo in db
99 func (s *Store) GetUtxo(hash *bc.Hash) (*storage.UtxoEntry, error) {
100 return getUtxo(s.db, hash)
103 // BlockExist check if the block is stored in disk
104 func (s *Store) BlockExist(hash *bc.Hash) bool {
105 block, err := s.cache.lookup(hash)
106 return err == nil && block != nil
109 // GetBlock return the block by given hash
110 func (s *Store) GetBlock(hash *bc.Hash) (*types.Block, error) {
111 return s.cache.lookup(hash)
114 // GetTransactionsUtxo will return all the utxo that related to the input txs
115 func (s *Store) GetTransactionsUtxo(view *state.UtxoViewpoint, txs []*bc.Tx) error {
116 return getTransactionsUtxo(s.db, view, txs)
119 // GetTransactionStatus will return the utxo that related to the block hash
120 func (s *Store) GetTransactionStatus(hash *bc.Hash) (*bc.TransactionStatus, error) {
121 data := s.db.Get(calcTxStatusKey(hash))
123 return nil, errors.New("can't find the transaction status by given hash")
126 ts := &bc.TransactionStatus{}
127 if err := proto.Unmarshal(data, ts); err != nil {
128 return nil, errors.Wrap(err, "unmarshaling transaction status")
133 // GetStoreStatus return the BlockStoreStateJSON
134 func (s *Store) GetStoreStatus() *protocol.BlockStoreState {
135 return loadBlockStoreStateJSON(s.db)
138 func (s *Store) LoadBlockIndex(stateBestHeight uint64) (*state.BlockIndex, error) {
139 startTime := time.Now()
140 blockIndex := state.NewBlockIndex()
141 bhIter := s.db.IteratorPrefix(blockHeaderPrefix)
142 defer bhIter.Release()
144 var lastNode *state.BlockNode
146 bh := &types.BlockHeader{}
147 if err := bh.UnmarshalText(bhIter.Value()); err != nil {
151 // If a block with a height greater than the best height of state is added to the index,
152 // It may cause a bug that the new block cant not be process properly.
153 if bh.Height > stateBestHeight {
157 var parent *state.BlockNode
158 if lastNode == nil || lastNode.Hash == bh.PreviousBlockHash {
161 parent = blockIndex.GetNode(&bh.PreviousBlockHash)
164 node, err := state.NewBlockNode(bh, parent)
169 blockIndex.AddNode(node)
173 log.WithFields(log.Fields{
175 "height": stateBestHeight,
176 "duration": time.Since(startTime),
177 }).Debug("initialize load history block index from database")
178 return blockIndex, nil
181 // SaveBlock persists a new block in the protocol.
182 func (s *Store) SaveBlock(block *types.Block, ts *bc.TransactionStatus) error {
183 startTime := time.Now()
184 binaryBlock, err := block.MarshalText()
186 return errors.Wrap(err, "Marshal block meta")
189 binaryBlockHeader, err := block.BlockHeader.MarshalText()
191 return errors.Wrap(err, "Marshal block header")
194 binaryTxStatus, err := proto.Marshal(ts)
196 return errors.Wrap(err, "marshal block transaction status")
199 blockHash := block.Hash()
200 batch := s.db.NewBatch()
201 batch.Set(calcBlockKey(&blockHash), binaryBlock)
202 batch.Set(calcBlockHeaderKey(block.Height, &blockHash), binaryBlockHeader)
203 batch.Set(calcTxStatusKey(&blockHash), binaryTxStatus)
206 log.WithFields(log.Fields{
208 "height": block.Height,
209 "hash": blockHash.String(),
210 "duration": time.Since(startTime),
211 }).Info("block saved on disk")
215 // SaveChainStatus save the core's newest status && delete old status
216 func (s *Store) SaveChainStatus(node *state.BlockNode, view *state.UtxoViewpoint) error {
217 batch := s.db.NewBatch()
218 if err := saveUtxoView(batch, view); err != nil {
222 bytes, err := json.Marshal(protocol.BlockStoreState{Height: node.Height, Hash: &node.Hash})
227 batch.Set(blockStoreKey, bytes)
232 func (s *Store) IsWithdrawSpent(hash *bc.Hash) bool {
233 data := s.db.Get(calcClaimTxKey(hash))
240 func (s *Store) SetWithdrawSpent(hash *bc.Hash) {
241 batch := s.db.NewBatch()
242 batch.Set(calcClaimTxKey(hash), []byte("1"))
246 func (s *Store) Set(hash *bc.Hash, data []byte) error {
247 batch := s.db.NewBatch()
248 batch.Set(calcDPosKey(hash), data)
253 func (s *Store) Get(hash *bc.Hash) ([]byte, error) {
254 data := s.db.Get(calcDPosKey(hash))
256 return nil, errors.New("can't find the snapshot by given hash")