OSDN Git Service

update master (#487)
[bytom/bytom-spv.git] / database / leveldb / store.go
1 package leveldb
2
3 import (
4         "encoding/json"
5
6         "github.com/golang/protobuf/proto"
7         "github.com/tendermint/tmlibs/common"
8         dbm "github.com/tendermint/tmlibs/db"
9
10         "github.com/bytom/database"
11         "github.com/bytom/database/storage"
12         "github.com/bytom/errors"
13         "github.com/bytom/protocol/bc"
14         "github.com/bytom/protocol/bc/types"
15         "github.com/bytom/protocol/state"
16 )
17
18 var (
19         blockStoreKey     = []byte("blockStore")
20         blockPrefix       = []byte("B:")
21         blockHeaderPrefix = []byte("BH:")
22         blockSeedPrefix   = []byte("BS:")
23         txStatusPrefix    = []byte("BTS:")
24 )
25
26 func loadBlockStoreStateJSON(db dbm.DB) database.BlockStoreStateJSON {
27         bytes := db.Get(blockStoreKey)
28         if bytes == nil {
29                 return database.BlockStoreStateJSON{
30                         Height: 0,
31                 }
32         }
33         bsj := database.BlockStoreStateJSON{}
34         if err := json.Unmarshal(bytes, &bsj); err != nil {
35                 common.PanicCrisis(common.Fmt("Could not unmarshal bytes: %X", bytes))
36         }
37         return bsj
38 }
39
40 // A Store encapsulates storage for blockchain validation.
41 // It satisfies the interface protocol.Store, and provides additional
42 // methods for querying current data.
43 type Store struct {
44         db    dbm.DB
45         cache blockCache
46 }
47
48 func calcBlockKey(hash *bc.Hash) []byte {
49         return append(blockPrefix, hash.Bytes()...)
50 }
51
52 func calcBlockHeaderKey(hash *bc.Hash) []byte {
53         return append(blockHeaderPrefix, hash.Bytes()...)
54 }
55
56 func calcSeedKey(hash *bc.Hash) []byte {
57         return append(blockSeedPrefix, hash.Bytes()...)
58 }
59
60 func calcTxStatusKey(hash *bc.Hash) []byte {
61         return append(txStatusPrefix, hash.Bytes()...)
62 }
63
64 // GetBlock return the block by given hash
65 func GetBlock(db dbm.DB, hash *bc.Hash) *types.Block {
66         bytez := db.Get(calcBlockKey(hash))
67         if bytez == nil {
68                 return nil
69         }
70
71         block := &types.Block{}
72         block.UnmarshalText(bytez)
73         return block
74 }
75
76 // NewStore creates and returns a new Store object.
77 func NewStore(db dbm.DB) *Store {
78         cache := newBlockCache(func(hash *bc.Hash) *types.Block {
79                 return GetBlock(db, hash)
80         })
81         return &Store{
82                 db:    db,
83                 cache: cache,
84         }
85 }
86
87 // GetUtxo will search the utxo in db
88 func (s *Store) GetUtxo(hash *bc.Hash) (*storage.UtxoEntry, error) {
89         return getUtxo(s.db, hash)
90 }
91
92 // BlockExist check if the block is stored in disk
93 func (s *Store) BlockExist(hash *bc.Hash) bool {
94         block, err := s.cache.lookup(hash)
95         return err == nil && block != nil
96 }
97
98 // GetBlock return the block by given hash
99 func (s *Store) GetBlock(hash *bc.Hash) (*types.Block, error) {
100         return s.cache.lookup(hash)
101 }
102
103 // GetBlockHeader return the block by given hash
104 func (s *Store) GetBlockHeader(hash *bc.Hash) (*types.BlockHeader, error) {
105         bytez := s.db.Get(calcBlockHeaderKey(hash))
106         if bytez == nil {
107                 return nil, errors.New("can't find the block header by given hash")
108         }
109
110         bh := &types.BlockHeader{}
111         err := bh.UnmarshalText(bytez)
112         return bh, err
113 }
114
115 // GetSeed will return the seed of given block
116 func (s *Store) GetSeed(hash *bc.Hash) (*bc.Hash, error) {
117         data := s.db.Get(calcSeedKey(hash))
118         if data == nil {
119                 return nil, errors.New("can't find the seed by given hash")
120         }
121
122         seed := &bc.Hash{}
123         if err := proto.Unmarshal(data, seed); err != nil {
124                 return nil, errors.Wrap(err, "unmarshaling seed")
125         }
126         return seed, nil
127 }
128
129 // GetTransactionsUtxo will return all the utxo that related to the input txs
130 func (s *Store) GetTransactionsUtxo(view *state.UtxoViewpoint, txs []*bc.Tx) error {
131         return getTransactionsUtxo(s.db, view, txs)
132 }
133
134 // GetTransactionStatus will return the utxo that related to the block hash
135 func (s *Store) GetTransactionStatus(hash *bc.Hash) (*bc.TransactionStatus, error) {
136         data := s.db.Get(calcTxStatusKey(hash))
137         if data == nil {
138                 return nil, errors.New("can't find the transaction status by given hash")
139         }
140
141         ts := &bc.TransactionStatus{}
142         if err := proto.Unmarshal(data, ts); err != nil {
143                 return nil, errors.Wrap(err, "unmarshaling transaction status")
144         }
145         return ts, nil
146 }
147
148 // GetStoreStatus return the BlockStoreStateJSON
149 func (s *Store) GetStoreStatus() database.BlockStoreStateJSON {
150         return loadBlockStoreStateJSON(s.db)
151 }
152
153 // GetMainchain read the mainchain map from db
154 func (s *Store) GetMainchain(hash *bc.Hash) (map[uint64]*bc.Hash, error) {
155         return getMainchain(s.db, hash)
156 }
157
158 // SaveBlock persists a new block in the database.
159 func (s *Store) SaveBlock(block *types.Block, ts *bc.TransactionStatus, seed *bc.Hash) error {
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         binaryTxStatus, err := proto.Marshal(ts)
171         if err != nil {
172                 return errors.Wrap(err, "marshal block transaction status")
173         }
174
175         binarySeed, err := proto.Marshal(seed)
176         if err != nil {
177                 return errors.Wrap(err, "marshal block seed")
178         }
179
180         blockHash := block.Hash()
181         batch := s.db.NewBatch()
182         batch.Set(calcBlockKey(&blockHash), binaryBlock)
183         batch.Set(calcBlockHeaderKey(&blockHash), binaryBlockHeader)
184         batch.Set(calcTxStatusKey(&blockHash), binaryTxStatus)
185         batch.Set(calcSeedKey(&blockHash), binarySeed)
186         batch.Write()
187         return nil
188 }
189
190 // SaveChainStatus save the core's newest status && delete old status
191 func (s *Store) SaveChainStatus(block *types.Block, view *state.UtxoViewpoint, m map[uint64]*bc.Hash) error {
192         hash := block.Hash()
193         batch := s.db.NewBatch()
194
195         if err := saveMainchain(batch, m, &hash); err != nil {
196                 return err
197         }
198
199         if err := saveUtxoView(batch, view); err != nil {
200                 return err
201         }
202
203         bytes, err := json.Marshal(database.BlockStoreStateJSON{Height: block.Height, Hash: &hash})
204         if err != nil {
205                 return err
206         }
207
208         batch.Set(blockStoreKey, bytes)
209         batch.Write()
210         cleanMainchainDB(s.db, &hash)
211         return nil
212 }