6 "github.com/golang/protobuf/proto"
7 "github.com/tendermint/tmlibs/common"
8 dbm "github.com/tendermint/tmlibs/db"
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"
19 blockStoreKey = []byte("blockStore")
20 blockPrefix = []byte("B:")
21 blockHeaderPrefix = []byte("BH:")
22 blockSeedPrefix = []byte("BS:")
23 txStatusPrefix = []byte("BTS:")
26 func loadBlockStoreStateJSON(db dbm.DB) database.BlockStoreStateJSON {
27 bytes := db.Get(blockStoreKey)
29 return database.BlockStoreStateJSON{
33 bsj := database.BlockStoreStateJSON{}
34 if err := json.Unmarshal(bytes, &bsj); err != nil {
35 common.PanicCrisis(common.Fmt("Could not unmarshal bytes: %X", bytes))
40 // A Store encapsulates storage for blockchain validation.
41 // It satisfies the interface protocol.Store, and provides additional
42 // methods for querying current data.
48 func calcBlockKey(hash *bc.Hash) []byte {
49 return append(blockPrefix, hash.Bytes()...)
52 func calcBlockHeaderKey(hash *bc.Hash) []byte {
53 return append(blockHeaderPrefix, hash.Bytes()...)
56 func calcSeedKey(hash *bc.Hash) []byte {
57 return append(blockSeedPrefix, hash.Bytes()...)
60 func calcTxStatusKey(hash *bc.Hash) []byte {
61 return append(txStatusPrefix, hash.Bytes()...)
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))
71 block := &types.Block{}
72 block.UnmarshalText(bytez)
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)
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)
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
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)
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))
107 return nil, errors.New("can't find the block header by given hash")
110 bh := &types.BlockHeader{}
111 err := bh.UnmarshalText(bytez)
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))
119 return nil, errors.New("can't find the seed by given hash")
123 if err := proto.Unmarshal(data, seed); err != nil {
124 return nil, errors.Wrap(err, "unmarshaling seed")
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)
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))
138 return nil, errors.New("can't find the transaction status by given hash")
141 ts := &bc.TransactionStatus{}
142 if err := proto.Unmarshal(data, ts); err != nil {
143 return nil, errors.Wrap(err, "unmarshaling transaction status")
148 // GetStoreStatus return the BlockStoreStateJSON
149 func (s *Store) GetStoreStatus() database.BlockStoreStateJSON {
150 return loadBlockStoreStateJSON(s.db)
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)
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()
162 return errors.Wrap(err, "Marshal block meta")
165 binaryBlockHeader, err := block.BlockHeader.MarshalText()
167 return errors.Wrap(err, "Marshal block header")
170 binaryTxStatus, err := proto.Marshal(ts)
172 return errors.Wrap(err, "marshal block transaction status")
175 binarySeed, err := proto.Marshal(seed)
177 return errors.Wrap(err, "marshal block seed")
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)
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 {
193 batch := s.db.NewBatch()
195 if err := saveMainchain(batch, m, &hash); err != nil {
199 if err := saveUtxoView(batch, view); err != nil {
203 bytes, err := json.Marshal(database.BlockStoreStateJSON{Height: block.Height, Hash: &hash})
208 batch.Set(blockStoreKey, bytes)
210 cleanMainchainDB(s.db, &hash)