"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) {
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)
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()
+}
var (
blockStoreKey = []byte("blockStore")
- blockPrefix = []byte("B:")
blockHeaderPrefix = []byte("BH:")
blockTransactonsPrefix = []byte("BTXS:")
txStatusPrefix = []byte("BTS:")
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)
// 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
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,
// 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
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")
}
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
}
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")
}
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
}
serflag = serflags[0]
switch serflag {
case SerBlockHeader, SerBlockFull:
+ case SerBlockTransactions:
+ return
default:
return 0, fmt.Errorf("unsupported serialization flags 0x%x", serflags)
}