OSDN Git Service

fix review
authormars <mars@bytom.io>
Sat, 15 Jun 2019 06:02:29 +0000 (14:02 +0800)
committermars <mars@bytom.io>
Sat, 15 Jun 2019 06:02:29 +0000 (14:02 +0800)
database/cache.go
database/store.go
protocol/bc/types/block.go
protocol/bc/types/block_header.go

index dc118fb..e2f604f 100644 (file)
@@ -11,20 +11,40 @@ import (
        "github.com/vapor/protocol/bc/types"
 )
 
-const maxCachedBlocks = 30
+const (
+       maxCachedBlocks           = 30
+       maxCachedBlockHeaders     = 1000
+       maxCachedBlockTransactons = 1000
+)
+
+type fillBlockFn func(hash *bc.Hash, height uint64) (*types.Block, error)
+type fillBlockHeaderFn func(hash *bc.Hash, height uint64) (*types.BlockHeader, error)
+type fillBlockTransactionsFn func(hash *bc.Hash) ([]*types.Tx, error)
 
-func newBlockCache(fillFn func(hash *bc.Hash, height uint64) (*types.Block, error)) blockCache {
+func newBlockCache(fillBlock fillBlockFn, fillBlockHeader fillBlockHeaderFn, fillBlockTxs fillBlockTransactionsFn) blockCache {
        return blockCache{
-               lru:    lru.New(maxCachedBlocks),
-               fillFn: fillFn,
+               lru:                    lru.New(maxCachedBlocks),
+               lruBlockHeaders:        lru.New(maxCachedBlockHeaders),
+               lruTxs:                 lru.New(maxCachedBlockTransactons),
+               fillFn:                 fillBlock,
+               fillBlockHeaderFn:      fillBlockHeader,
+               fillBlockTransactionFn: fillBlockTxs,
        }
 }
 
 type blockCache struct {
-       mu     sync.Mutex
-       lru    *lru.Cache
-       fillFn func(hash *bc.Hash, height uint64) (*types.Block, error)
-       single singleflight.Group
+       mu                     sync.Mutex
+       muHeaders              sync.Mutex
+       muTxs                  sync.Mutex
+       lru                    *lru.Cache
+       lruBlockHeaders        *lru.Cache
+       lruTxs                 *lru.Cache
+       fillFn                 func(hash *bc.Hash, height uint64) (*types.Block, error)
+       fillBlockHeaderFn      func(hash *bc.Hash, height uint64) (*types.BlockHeader, error)
+       fillBlockTransactionFn func(hash *bc.Hash) ([]*types.Tx, error)
+       single                 singleflight.Group
+       singleBlockHeader      singleflight.Group
+       singleBlockTxs         singleflight.Group
 }
 
 func (c *blockCache) lookup(hash *bc.Hash, height uint64) (*types.Block, error) {
@@ -51,6 +71,54 @@ func (c *blockCache) lookup(hash *bc.Hash, height uint64) (*types.Block, error)
        return block.(*types.Block), nil
 }
 
+func (c *blockCache) lookupBlockHeader(hash *bc.Hash, height uint64) (*types.BlockHeader, error) {
+       if bH, ok := c.getBlockHeader(hash); ok {
+               return bH, nil
+       }
+
+       blockHeader, err := c.singleBlockHeader.Do(hash.String(), func() (interface{}, error) {
+               bH, err := c.fillBlockHeaderFn(hash, height)
+               if err != nil {
+                       return nil, err
+               }
+
+               if bH == nil {
+                       return nil, fmt.Errorf("There are no blockHeader with given hash %s", hash.String())
+               }
+
+               c.addHeader(bH)
+               return bH, nil
+       })
+       if err != nil {
+               return nil, err
+       }
+       return blockHeader.(*types.BlockHeader), nil
+}
+
+func (c *blockCache) lookupBlockTxs(hash *bc.Hash) ([]*types.Tx, error) {
+       if bTxs, ok := c.getBlockTransactions(hash); ok {
+               return bTxs, nil
+       }
+
+       blockTransactions, err := c.singleBlockTxs.Do(hash.String(), func() (interface{}, error) {
+               bTxs, err := c.fillBlockTransactionFn(hash)
+               if err != nil {
+                       return nil, err
+               }
+
+               if bTxs == nil {
+                       return nil, fmt.Errorf("There are no block transactions with given hash %s", hash.String())
+               }
+
+               c.addTxs(*hash, bTxs)
+               return bTxs, nil
+       })
+       if err != nil {
+               return nil, err
+       }
+       return blockTransactions.([]*types.Tx), nil
+}
+
 func (c *blockCache) get(hash *bc.Hash) (*types.Block, bool) {
        c.mu.Lock()
        block, ok := c.lru.Get(*hash)
@@ -61,8 +129,40 @@ func (c *blockCache) get(hash *bc.Hash) (*types.Block, bool) {
        return block.(*types.Block), ok
 }
 
+func (c *blockCache) getBlockHeader(hash *bc.Hash) (*types.BlockHeader, bool) {
+       c.muHeaders.Lock()
+       blockHeader, ok := c.lruBlockHeaders.Get(*hash)
+       c.muHeaders.Unlock()
+       if blockHeader == nil {
+               return nil, ok
+       }
+       return blockHeader.(*types.BlockHeader), ok
+}
+
+func (c *blockCache) getBlockTransactions(hash *bc.Hash) ([]*types.Tx, bool) {
+       c.muTxs.Lock()
+       txs, ok := c.lruTxs.Get(*hash)
+       c.muTxs.Unlock()
+       if txs == nil {
+               return nil, ok
+       }
+       return txs.([]*types.Tx), ok
+}
+
 func (c *blockCache) add(block *types.Block) {
        c.mu.Lock()
        c.lru.Add(block.Hash(), block)
        c.mu.Unlock()
 }
+
+func (c *blockCache) addHeader(blockHeader *types.BlockHeader) {
+       c.muHeaders.Lock()
+       c.lruBlockHeaders.Add(blockHeader.Hash(), blockHeader)
+       c.muHeaders.Unlock()
+}
+
+func (c *blockCache) addTxs(hash bc.Hash, txs []*types.Tx) {
+       c.muTxs.Lock()
+       c.lruTxs.Add(hash, txs)
+       c.muTxs.Unlock()
+}
index e7929e9..43d7fe6 100644 (file)
@@ -22,7 +22,6 @@ const logModule = "leveldb"
 
 var (
        blockStoreKey          = []byte("blockStore")
-       blockPrefix            = []byte("B:")
        blockHeaderPrefix      = []byte("BH:")
        blockTransactonsPrefix = []byte("BTXS:")
        txStatusPrefix         = []byte("BTS:")
@@ -49,10 +48,6 @@ type Store struct {
        cache blockCache
 }
 
-func calcBlockKey(hash *bc.Hash) []byte {
-       return append(blockPrefix, hash.Bytes()...)
-}
-
 func calcBlockHeaderKey(height uint64, hash *bc.Hash) []byte {
        buf := [8]byte{}
        binary.BigEndian.PutUint64(buf[:], height)
@@ -76,16 +71,7 @@ func calcVoteResultKey(seq uint64) []byte {
 
 // GetBlock return the block by given hash and height
 func GetBlock(db dbm.DB, hash *bc.Hash, height uint64) (*types.Block, error) {
-       /*
-               bytez := db.Get(calcBlockKey(hash))
-               if bytez == nil {
-                       return nil, nil
-               }
-       */
-
        block := &types.Block{}
-       //err := block.UnmarshalText(bytez)
-
        binaryBlockHeader := db.Get(calcBlockHeaderKey(height, hash))
        if binaryBlockHeader == nil {
                return nil, nil
@@ -93,25 +79,63 @@ func GetBlock(db dbm.DB, hash *bc.Hash, height uint64) (*types.Block, error) {
 
        binaryBlockTxs := db.Get(calcBlockTransactionsKey(hash))
        if binaryBlockTxs == nil {
-               return nil, nil
+               return nil, errors.New("The transactions in the block is empty")
        }
 
-       if err := block.BlockHeader.UnmarshalText(binaryBlockHeader); err != nil {
+       if err := block.UnmarshalText(binaryBlockHeader); err != nil {
                return nil, err
        }
 
-       if err := block.UnmarshalTextForTransactions(binaryBlockTxs); err != nil {
+       if err := block.UnmarshalText(binaryBlockTxs); err != nil {
                return nil, err
        }
 
        return block, nil
 }
 
+// GetBlockHeader return the block header by given hash and height
+func GetBlockHeader(db dbm.DB, hash *bc.Hash, height uint64) (*types.BlockHeader, error) {
+       block := &types.Block{}
+       binaryBlockHeader := db.Get(calcBlockHeaderKey(height, hash))
+       if binaryBlockHeader == nil {
+               return nil, nil
+       }
+       if err := block.UnmarshalText(binaryBlockHeader); err != nil {
+               return nil, err
+       }
+
+       return &block.BlockHeader, nil
+}
+
+// GetBlockTransactions return the block transactions by given hash
+func GetBlockTransactions(db dbm.DB, hash *bc.Hash) ([]*types.Tx, error) {
+       block := &types.Block{}
+       binaryBlockTxs := db.Get(calcBlockTransactionsKey(hash))
+       if binaryBlockTxs == nil {
+               return nil, errors.New("The transactions in the block is empty")
+       }
+
+       if err := block.UnmarshalText(binaryBlockTxs); err != nil {
+               return nil, err
+       }
+       return block.Transactions, nil
+}
+
 // NewStore creates and returns a new Store object.
 func NewStore(db dbm.DB) *Store {
-       cache := newBlockCache(func(hash *bc.Hash, height uint64) (*types.Block, error) {
+       fillBlockFn := func(hash *bc.Hash, height uint64) (*types.Block, error) {
                return GetBlock(db, hash, height)
-       })
+       }
+
+       fillBlockHeaderFn := func(hash *bc.Hash, height uint64) (*types.BlockHeader, error) {
+               return GetBlockHeader(db, hash, height)
+       }
+
+       fillBlockTxsFn := func(hash *bc.Hash) ([]*types.Tx, error) {
+               return GetBlockTransactions(db, hash)
+       }
+
+       cache := newBlockCache(fillBlockFn, fillBlockHeaderFn, fillBlockTxsFn)
        return &Store{
                db:    db,
                cache: cache,
@@ -125,18 +149,43 @@ func (s *Store) GetUtxo(hash *bc.Hash) (*storage.UtxoEntry, error) {
 
 // BlockExist check if the block is stored in disk
 func (s *Store) BlockExist(hash *bc.Hash, height uint64) bool {
-       block, err := s.cache.lookup(hash, height)
-       return err == nil && block != nil
+       blockHeader, err := s.cache.lookupBlockHeader(hash, height)
+       return err == nil && blockHeader != nil
 }
 
 // GetBlock return the block by given hash
 func (s *Store) GetBlock(hash *bc.Hash, height uint64) (*types.Block, error) {
-       return s.cache.lookup(hash, height)
+       blockHeader, err := s.GetBlockHeader(hash, height)
+       if err != nil {
+               return nil, err
+       }
+       txs, err := s.GetBlockTransactions(hash)
+       if err != nil {
+               return nil, err
+       }
+
+       return &types.Block{
+               BlockHeader:  *blockHeader,
+               Transactions: txs,
+       }, nil
 }
 
 // GetBlockHeader return the BlockHeader by given hash
-func (s *Store) GetBlockHeader(*bc.Hash, uint64) (*types.BlockHeader, error) {
-       return nil, nil
+func (s *Store) GetBlockHeader(hash *bc.Hash, height uint64) (*types.BlockHeader, error) {
+       blockHeader, err := s.cache.lookupBlockHeader(hash, height)
+       if err != nil {
+               return nil, err
+       }
+       return blockHeader, nil
+}
+
+// GetBlockTransactions return the Block transactions by given hash
+func (s *Store) GetBlockTransactions(hash *bc.Hash) ([]*types.Tx, error) {
+       txs, err := s.cache.lookupBlockTxs(hash)
+       if err != nil {
+               return nil, err
+       }
+       return txs, nil
 }
 
 // GetTransactionsUtxo will return all the utxo that related to the input txs
@@ -224,7 +273,7 @@ func (s *Store) LoadBlockIndex(stateBestHeight uint64) (*state.BlockIndex, error
 func (s *Store) SaveBlock(block *types.Block, ts *bc.TransactionStatus) error {
        startTime := time.Now()
 
-       binaryBlockHeader, err := block.BlockHeader.MarshalText()
+       binaryBlockHeader, err := block.MarshalTextForBlockHeadr()
        if err != nil {
                return errors.Wrap(err, "Marshal block header")
        }
index 1eeb00c..4ad073a 100644 (file)
@@ -58,24 +58,15 @@ func (b *Block) UnmarshalText(text []byte) error {
        return nil
 }
 
-// MarshalTextForTransactions fulfills the json.Marshaler interface.
-func (b *Block) MarshalTextForTransactions() ([]byte, error) {
+// MarshalTextForBlockHeadr fulfills the json.Marshaler interface.
+func (b *Block) MarshalTextForBlockHeadr() ([]byte, error) {
        buf := bufpool.Get()
        defer bufpool.Put(buf)
 
        ew := errors.NewWriter(buf)
-       ew.Write([]byte{SerBlockTransactions})
-
-       if _, err := blockchain.WriteVarint31(ew, uint64(len(b.Transactions))); err != nil {
+       if err := b.writeTo(ew, SerBlockHeader); err != nil {
                return nil, err
        }
-
-       for _, tx := range b.Transactions {
-               if _, err := tx.WriteTo(ew); err != nil {
-                       return nil, err
-               }
-       }
-
        if err := ew.Err(); err != nil {
                return nil, err
        }
@@ -85,50 +76,43 @@ func (b *Block) MarshalTextForTransactions() ([]byte, error) {
        return enc, nil
 }
 
-// UnmarshalTextForTransactions fulfills the encoding.TextUnmarshaler interface.
-func (b *Block) UnmarshalTextForTransactions(text []byte) error {
-       decoded := make([]byte, hex.DecodedLen(len(text)))
-       if _, err := hex.Decode(decoded, text); err != nil {
-               return err
-       }
-
-       r := blockchain.NewReader(decoded)
-       var serflags [1]byte
-       io.ReadFull(r, serflags[:])
-       if serflags[0] != SerBlockTransactions {
-               return fmt.Errorf("unsupported serialization flags 0x%x", serflags)
-       }
+// MarshalTextForTransactions fulfills the json.Marshaler interface.
+func (b *Block) MarshalTextForTransactions() ([]byte, error) {
+       buf := bufpool.Get()
+       defer bufpool.Put(buf)
 
-       n, err := blockchain.ReadVarint31(r)
-       if err != nil {
-               return errors.Wrap(err, "reading number of transactions")
+       ew := errors.NewWriter(buf)
+       if err := b.writeTo(ew, SerBlockTransactions); err != nil {
+               return nil, err
        }
-
-       for ; n > 0; n-- {
-               data := TxData{}
-               if err = data.readFrom(r); err != nil {
-                       return errors.Wrapf(err, "reading transaction %d", len(b.Transactions))
-               }
-
-               b.Transactions = append(b.Transactions, NewTx(data))
+       if err := ew.Err(); err != nil {
+               return nil, err
        }
 
-       if trailing := r.Len(); trailing > 0 {
-               return fmt.Errorf("trailing garbage (%d bytes)", trailing)
-       }
-       return nil
+       enc := make([]byte, hex.EncodedLen(buf.Len()))
+       hex.Encode(enc, buf.Bytes())
+       return enc, nil
 }
 
 func (b *Block) readFrom(r *blockchain.Reader) error {
-       serflags, err := b.BlockHeader.readFrom(r)
+       serflag, err := b.BlockHeader.readFrom(r)
        if err != nil {
                return err
        }
 
-       if serflags == SerBlockHeader {
+       if serflag == SerBlockHeader {
                return nil
        }
 
+       if serflag != SerBlockTransactions {
+               var serflags [1]byte
+               io.ReadFull(r, serflags[:])
+               serflag = serflags[0]
+               if serflag != SerBlockTransactions {
+                       return fmt.Errorf("unsupported serialization flags 0x%x", serflags)
+               }
+       }
+
        n, err := blockchain.ReadVarint31(r)
        if err != nil {
                return errors.Wrap(err, "reading number of transactions")
@@ -155,14 +139,18 @@ func (b *Block) WriteTo(w io.Writer) (int64, error) {
 }
 
 func (b *Block) writeTo(w io.Writer, serflags uint8) error {
-       if err := b.BlockHeader.writeTo(w, serflags); err != nil {
-               return err
+       if serflags == SerBlockHeader || serflags == SerBlockFull {
+               if err := b.BlockHeader.writeTo(w, serflags); err != nil {
+                       return err
+               }
        }
 
        if serflags == SerBlockHeader {
                return nil
        }
 
+       w.Write([]byte{SerBlockTransactions})
+
        if _, err := blockchain.WriteVarint31(w, uint64(len(b.Transactions))); err != nil {
                return err
        }
index 88bdddb..5c84ad1 100644 (file)
@@ -66,6 +66,8 @@ func (bh *BlockHeader) readFrom(r *blockchain.Reader) (serflag uint8, err error)
        serflag = serflags[0]
        switch serflag {
        case SerBlockHeader, SerBlockFull:
+       case SerBlockTransactions:
+               return
        default:
                return 0, fmt.Errorf("unsupported serialization flags 0x%x", serflags)
        }