OSDN Git Service

b2b7e1888459a757998207a76d81a7fbf29b2b97
[bytom/bytom.git] / database / store.go
1 package database
2
3 import (
4         "encoding/binary"
5         "encoding/json"
6         "time"
7
8         log "github.com/sirupsen/logrus"
9         "github.com/tendermint/tmlibs/common"
10
11         dbm "github.com/bytom/bytom/database/leveldb"
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 )
19
20 const logModule = "leveldb"
21
22 var (
23         BlockStoreKey     = []byte("blockStore")
24         BlockPrefix       = []byte("B:")
25         BlockHeaderPrefix = []byte("BH:")
26         TxStatusPrefix    = []byte("BTS:")
27 )
28
29 func loadBlockStoreStateJSON(db dbm.DB) *protocol.BlockStoreState {
30         bytes := db.Get(BlockStoreKey)
31         if bytes == nil {
32                 return nil
33         }
34         bsj := &protocol.BlockStoreState{}
35         if err := json.Unmarshal(bytes, bsj); err != nil {
36                 common.PanicCrisis(common.Fmt("Could not unmarshal bytes: %X", bytes))
37         }
38         return bsj
39 }
40
41 // A Store encapsulates storage for blockchain validation.
42 // It satisfies the interface protocol.Store, and provides additional
43 // methods for querying current data.
44 type Store struct {
45         db    dbm.DB
46         cache blockCache
47 }
48
49 func CalcBlockKey(hash *bc.Hash) []byte {
50         return append(BlockPrefix, hash.Bytes()...)
51 }
52
53 func CalcBlockHeaderKey(height uint64, hash *bc.Hash) []byte {
54         buf := [8]byte{}
55         binary.BigEndian.PutUint64(buf[:], height)
56         key := append(BlockHeaderPrefix, buf[:]...)
57         return append(key, hash.Bytes()...)
58 }
59
60 // GetBlockHeader return the BlockHeader by given hash
61 func (s *Store) GetBlockHeader(hash *bc.Hash) (*types.BlockHeader, error) {
62         return nil, nil
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 // GetStoreStatus return the BlockStoreStateJSON
110 func (s *Store) GetStoreStatus() *protocol.BlockStoreState {
111         return loadBlockStoreStateJSON(s.db)
112 }
113
114 func (s *Store) LoadBlockIndex(stateBestHeight uint64) (*state.BlockIndex, error) {
115         startTime := time.Now()
116         blockIndex := state.NewBlockIndex()
117         bhIter := s.db.IteratorPrefix(BlockHeaderPrefix)
118         defer bhIter.Release()
119
120         var lastNode *state.BlockNode
121         for bhIter.Next() {
122                 bh := &types.BlockHeader{}
123                 if err := bh.UnmarshalText(bhIter.Value()); err != nil {
124                         return nil, err
125                 }
126
127                 // If a block with a height greater than the best height of state is added to the index,
128                 // It may cause a bug that the new block cant not be process properly.
129                 if bh.Height > stateBestHeight {
130                         break
131                 }
132
133                 var parent *state.BlockNode
134                 if lastNode == nil || lastNode.Hash == bh.PreviousBlockHash {
135                         parent = lastNode
136                 } else {
137                         parent = blockIndex.GetNode(&bh.PreviousBlockHash)
138                 }
139
140                 node, err := state.NewBlockNode(bh, parent)
141                 if err != nil {
142                         return nil, err
143                 }
144
145                 blockIndex.AddNode(node)
146                 lastNode = node
147         }
148
149         log.WithFields(log.Fields{
150                 "module":   logModule,
151                 "height":   stateBestHeight,
152                 "duration": time.Since(startTime),
153         }).Debug("initialize load history block index from database")
154         return blockIndex, nil
155 }
156
157 // SaveBlock persists a new block in the protocol.
158 func (s *Store) SaveBlock(block *types.Block) error {
159         startTime := time.Now()
160         binaryBlock, err := block.MarshalText()
161         if err != nil {
162                 return errors.Wrap(err, "Marshal block meta")
163         }
164
165         binaryBlockHeader, err := block.BlockHeader.MarshalText()
166         if err != nil {
167                 return errors.Wrap(err, "Marshal block header")
168         }
169
170         blockHash := block.Hash()
171         batch := s.db.NewBatch()
172         batch.Set(CalcBlockKey(&blockHash), binaryBlock)
173         batch.Set(CalcBlockHeaderKey(block.Height, &blockHash), binaryBlockHeader)
174         batch.Write()
175
176         log.WithFields(log.Fields{
177                 "module":   logModule,
178                 "height":   block.Height,
179                 "hash":     blockHash.String(),
180                 "duration": time.Since(startTime),
181         }).Info("block saved on disk")
182         return nil
183 }
184
185 // SaveChainStatus save the core's newest status && delete old status
186 func (s *Store) SaveChainStatus(node *state.BlockNode, view *state.UtxoViewpoint, contractView *state.ContractViewpoint) error {
187         batch := s.db.NewBatch()
188         if err := saveUtxoView(batch, view); err != nil {
189                 return err
190         }
191
192         if err := deleteContractView(s.db, batch, contractView); err != nil {
193                 return err
194         }
195
196         if err := saveContractView(s.db, batch, contractView); err != nil {
197                 return err
198         }
199
200         bytes, err := json.Marshal(protocol.BlockStoreState{Height: node.Height, Hash: &node.Hash})
201         if err != nil {
202                 return err
203         }
204
205         batch.Set(BlockStoreKey, bytes)
206         batch.Write()
207         return nil
208 }
209
210 func (s *Store) GetCheckpoint(*bc.Hash) (*state.Checkpoint, error) {
211         return nil, nil
212 }
213
214 // GetCheckpointsByHeight return all checkpoints of specified block height
215 func (s *Store) GetCheckpointsByHeight(uint64) ([]*state.Checkpoint, error) {
216         return nil, nil
217 }
218
219 // SaveCheckpoints bulk save multiple checkpoint
220 func (s *Store) SaveCheckpoints(...*state.Checkpoint) error {
221         return nil
222 }