9 "github.com/golang/protobuf/proto"
10 log "github.com/sirupsen/logrus"
12 dbm "github.com/vapor/database/leveldb"
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"
24 // the byte of colon(:)
29 blockStore byte = iota
39 blockStoreKey = []byte{blockStore}
40 blockHashesPrefix = []byte{blockHashes, colon}
41 blockHeaderPrefix = []byte{blockHeader, colon}
42 blockTransactonsPrefix = []byte{blockTransactons, colon}
43 mainChainIndexPrefix = []byte{mainChainIndex, colon}
44 txStatusPrefix = []byte{txStatus, colon}
45 voteResultPrefix = []byte{voteResult, colon}
48 func loadBlockStoreStateJSON(db dbm.DB) *protocol.BlockStoreState {
49 bytes := db.Get(blockStoreKey)
54 bsj := &protocol.BlockStoreState{}
55 if err := json.Unmarshal(bytes, bsj); err != nil {
56 log.WithField("err", err).Panic("fail on unmarshal BlockStoreStateJSON")
61 // A Store encapsulates storage for blockchain validation.
62 // It satisfies the interface protocol.Store, and provides additional
63 // methods for querying current data.
69 func calcMainChainIndexPrefix(height uint64) []byte {
71 binary.BigEndian.PutUint64(buf[:], height)
72 return append(mainChainIndexPrefix, buf[:]...)
75 func calcBlockHashesPrefix(height uint64) []byte {
77 binary.BigEndian.PutUint64(buf[:], height)
78 return append(blockHashesPrefix, buf[:]...)
81 func calcBlockHeaderKey(hash *bc.Hash) []byte {
82 return append(blockHeaderPrefix, hash.Bytes()...)
85 func calcBlockTransactionsKey(hash *bc.Hash) []byte {
86 return append(blockTransactonsPrefix, hash.Bytes()...)
89 func calcTxStatusKey(hash *bc.Hash) []byte {
90 return append(txStatusPrefix, hash.Bytes()...)
93 func calcVoteResultKey(seq uint64) []byte {
95 binary.BigEndian.PutUint64(buf[:], seq)
96 return append(voteResultPrefix, buf[:]...)
99 // GetBlockHeader return the block header by given hash
100 func GetBlockHeader(db dbm.DB, hash *bc.Hash) (*types.BlockHeader, error) {
101 binaryBlockHeader := db.Get(calcBlockHeaderKey(hash))
102 if binaryBlockHeader == nil {
103 return nil, fmt.Errorf("There are no blockHeader with given hash %s", hash.String())
106 blockHeader := &types.BlockHeader{}
107 if err := blockHeader.UnmarshalText(binaryBlockHeader); err != nil {
110 return blockHeader, nil
113 // GetBlockTransactions return the block transactions by given hash
114 func GetBlockTransactions(db dbm.DB, hash *bc.Hash) ([]*types.Tx, error) {
115 binaryBlockTxs := db.Get(calcBlockTransactionsKey(hash))
116 if binaryBlockTxs == nil {
117 return nil, fmt.Errorf("There are no block transactions with given hash %s", hash.String())
120 block := &types.Block{}
121 if err := block.UnmarshalText(binaryBlockTxs); err != nil {
124 return block.Transactions, nil
127 // GetBlockHashesByHeight return block hashes by given height
128 func GetBlockHashesByHeight(db dbm.DB, height uint64) ([]*bc.Hash, error) {
129 binaryHashes := db.Get(calcBlockHashesPrefix(height))
130 if binaryHashes == nil {
131 return []*bc.Hash{}, nil
134 hashes := []*bc.Hash{}
135 if err := json.Unmarshal(binaryHashes, &hashes); err != nil {
141 // GetMainChainHash return BlockHash by given height
142 func GetMainChainHash(db dbm.DB, height uint64) (*bc.Hash, error) {
143 binaryHash := db.Get(calcMainChainIndexPrefix(height))
144 if binaryHash == nil {
145 return nil, fmt.Errorf("There are no BlockHash with given height %d", height)
149 if err := hash.UnmarshalText(binaryHash); err != nil {
155 // GetVoteResult return the vote result by given sequence
156 func GetVoteResult(db dbm.DB, seq uint64) (*state.VoteResult, error) {
157 data := db.Get(calcVoteResultKey(seq))
159 return nil, protocol.ErrNotFoundVoteResult
162 voteResult := new(state.VoteResult)
163 if err := json.Unmarshal(data, voteResult); err != nil {
164 return nil, errors.Wrap(err, "unmarshaling vote result")
166 return voteResult, nil
169 // NewStore creates and returns a new Store object.
170 func NewStore(db dbm.DB) *Store {
171 fillBlockHeaderFn := func(hash *bc.Hash) (*types.BlockHeader, error) {
172 return GetBlockHeader(db, hash)
174 fillBlockTxsFn := func(hash *bc.Hash) ([]*types.Tx, error) {
175 return GetBlockTransactions(db, hash)
178 fillBlockHashesFn := func(height uint64) ([]*bc.Hash, error) {
179 return GetBlockHashesByHeight(db, height)
182 fillMainChainHashFn := func(height uint64) (*bc.Hash, error) {
183 return GetMainChainHash(db, height)
186 fillVoteResultFn := func(seq uint64) (*state.VoteResult, error) {
187 return GetVoteResult(db, seq)
190 cache := newCache(fillBlockHeaderFn, fillBlockTxsFn, fillBlockHashesFn, fillMainChainHashFn, fillVoteResultFn)
197 // BlockExist check if the block is stored in disk
198 func (s *Store) BlockExist(hash *bc.Hash) bool {
199 _, err := s.cache.lookupBlockHeader(hash)
203 // GetBlock return the block by given hash
204 func (s *Store) GetBlock(hash *bc.Hash) (*types.Block, error) {
205 blockHeader, err := s.GetBlockHeader(hash)
210 txs, err := s.GetBlockTransactions(hash)
216 BlockHeader: *blockHeader,
221 // GetBlockHeader return the BlockHeader by given hash
222 func (s *Store) GetBlockHeader(hash *bc.Hash) (*types.BlockHeader, error) {
223 return s.cache.lookupBlockHeader(hash)
226 // GetBlockTransactions return the Block transactions by given hash
227 func (s *Store) GetBlockTransactions(hash *bc.Hash) ([]*types.Tx, error) {
228 return s.cache.lookupBlockTxs(hash)
231 // GetBlockHashesByHeight return the block hash by the specified height
232 func (s *Store) GetBlockHashesByHeight(height uint64) ([]*bc.Hash, error) {
233 return s.cache.lookupBlockHashesByHeight(height)
236 // GetMainChainHash return the block hash by the specified height
237 func (s *Store) GetMainChainHash(height uint64) (*bc.Hash, error) {
238 return s.cache.lookupMainChainHash(height)
241 // GetStoreStatus return the BlockStoreStateJSON
242 func (s *Store) GetStoreStatus() *protocol.BlockStoreState {
243 return loadBlockStoreStateJSON(s.db)
246 // GetTransactionsUtxo will return all the utxo that related to the input txs
247 func (s *Store) GetTransactionsUtxo(view *state.UtxoViewpoint, txs []*bc.Tx) error {
248 return getTransactionsUtxo(s.db, view, txs)
251 // GetTransactionStatus will return the utxo that related to the block hash
252 func (s *Store) GetTransactionStatus(hash *bc.Hash) (*bc.TransactionStatus, error) {
253 data := s.db.Get(calcTxStatusKey(hash))
255 return nil, errors.New("can't find the transaction status by given hash")
258 ts := &bc.TransactionStatus{}
259 if err := proto.Unmarshal(data, ts); err != nil {
260 return nil, errors.Wrap(err, "unmarshaling transaction status")
265 // GetUtxo will search the utxo in db
266 func (s *Store) GetUtxo(hash *bc.Hash) (*storage.UtxoEntry, error) {
267 return getUtxo(s.db, hash)
270 // GetVoteResult retrive the voting result in specified vote sequence
271 func (s *Store) GetVoteResult(seq uint64) (*state.VoteResult, error) {
272 return s.cache.lookupVoteResult(seq)
275 // SaveBlock persists a new block in the protocol.
276 func (s *Store) SaveBlock(block *types.Block, ts *bc.TransactionStatus) error {
277 startTime := time.Now()
278 binaryBlockHeader, err := block.MarshalTextForBlockHeader()
280 return errors.Wrap(err, "Marshal block header")
283 binaryBlockTxs, err := block.MarshalTextForTransactions()
285 return errors.Wrap(err, "Marshal block transactions")
288 binaryTxStatus, err := proto.Marshal(ts)
290 return errors.Wrap(err, "Marshal block transaction status")
293 blockHashes := []*bc.Hash{}
294 hashes, err := s.GetBlockHashesByHeight(block.Height)
298 blockHashes = append(blockHashes, hashes...)
299 blockHash := block.Hash()
300 blockHashes = append(blockHashes, &blockHash)
301 binaryBlockHashes, err := json.Marshal(blockHashes)
303 return errors.Wrap(err, "Marshal block hashes")
306 batch := s.db.NewBatch()
307 batch.Set(calcBlockHashesPrefix(block.Height), binaryBlockHashes)
308 batch.Set(calcBlockHeaderKey(&blockHash), binaryBlockHeader)
309 batch.Set(calcBlockTransactionsKey(&blockHash), binaryBlockTxs)
310 batch.Set(calcTxStatusKey(&blockHash), binaryTxStatus)
313 s.cache.removeBlockHashes(block.Height)
314 log.WithFields(log.Fields{
316 "height": block.Height,
317 "hash": blockHash.String(),
318 "duration": time.Since(startTime),
319 }).Info("block saved on disk")
323 // SaveBlockHeader persists a new block header in the protocol.
324 func (s *Store) SaveBlockHeader(blockHeader *types.BlockHeader) error {
325 binaryBlockHeader, err := blockHeader.MarshalText()
327 return errors.Wrap(err, "Marshal block header")
330 blockHash := blockHeader.Hash()
331 s.db.Set(calcBlockHeaderKey(&blockHash), binaryBlockHeader)
332 s.cache.removeBlockHeader(blockHeader)
336 // SaveChainStatus save the core's newest status && delete old status
337 func (s *Store) SaveChainStatus(blockHeader, irrBlockHeader *types.BlockHeader, mainBlockHeaders []*types.BlockHeader, view *state.UtxoViewpoint, voteResults []*state.VoteResult) error {
338 batch := s.db.NewBatch()
339 if err := saveUtxoView(batch, view); err != nil {
343 for _, vote := range voteResults {
344 bytes, err := json.Marshal(vote)
349 batch.Set(calcVoteResultKey(vote.Seq), bytes)
350 s.cache.removeVoteResult(vote)
353 blockHash := blockHeader.Hash()
354 irrBlockHash := irrBlockHeader.Hash()
355 bytes, err := json.Marshal(protocol.BlockStoreState{
356 Height: blockHeader.Height,
358 IrreversibleHeight: irrBlockHeader.Height,
359 IrreversibleHash: &irrBlockHash,
364 batch.Set(blockStoreKey, bytes)
366 // save main chain blockHeaders
367 for _, bh := range mainBlockHeaders {
368 blockHash := bh.Hash()
369 binaryBlockHash, err := blockHash.MarshalText()
371 return errors.Wrap(err, "Marshal block hash")
374 batch.Set(calcMainChainIndexPrefix(bh.Height), binaryBlockHash)
375 s.cache.removeMainChainHash(bh.Height)