OSDN Git Service

Opt db (#1926)
authorhauson <32921352+hauson@users.noreply.github.com>
Thu, 29 Apr 2021 03:33:25 +0000 (11:33 +0800)
committerGitHub <noreply@github.com>
Thu, 29 Apr 2021 03:33:25 +0000 (11:33 +0800)
* feat(add save_block_header fn): add SaveBlockHeader fn

* feat(opt save_block): opt SaveBlock fn

* feat(modify cache_test): modify cache test

* feat(rename block cache): rename blockCache as cache

* feat(add get block): add get block

* feat(modify): modify

* feat(modify block_header): modify writer_to and read_from

* feat(modify bloch_header): modify block_header

* feat(fix block_header_test): add BlockWitness

* feat(fix block_test): fix block_test

* feat(fix block integration test): fix block integration test

* feat(fix block_header_test.go): fix

* feat(fix block_test.go): fix block_test.go

* feat(opt code format): opt code format of store.go

* feat(modify cache): modify cache

* feat(modify cache): modify cache

* feat(modify store.go): modify store.go

* feat(modify): modify

* feat(modify block_integration_uitl.go): modify block_integration_util.go

* feat(delete max_cached_blocks): delete maCachedBlocks

* feat(delete mu): delete mu

* feat(delete blockprefix and txstatusprefix): delete const BlockPrefix and TxsStatusPrefix

* feat(add notes): add consts notes

13 files changed:
database/cache.go
database/cache_test.go
database/store.go
database/store_geter.go [new file with mode: 0644]
database/store_test.go
netsync/chainmgr/storage.go
protocol/bc/types/block.go
protocol/bc/types/block_header.go
protocol/bc/types/block_header_test.go
protocol/bc/types/block_test.go
protocol/bc/types/transaction.go
test/integration/block_integration_test.go
test/integration/block_integration_util.go

index 64571df..a8026e3 100644 (file)
 package database
 
 import (
-       "fmt"
-       "sync"
+       "strconv"
 
-       "github.com/golang/groupcache/lru"
        "github.com/golang/groupcache/singleflight"
 
+       "github.com/bytom/bytom/common"
        "github.com/bytom/bytom/protocol/bc"
        "github.com/bytom/bytom/protocol/bc/types"
 )
 
-const maxCachedBlocks = 30
+const (
+       maxCachedBlockHeaders      = 4096
+       maxCachedBlockTransactions = 1024
+       maxCachedBlockHashes       = 8192
+)
+
+type fillBlockHeaderFn func(hash *bc.Hash) (*types.BlockHeader, error)
+type fillBlockTransactionsFn func(hash *bc.Hash) ([]*types.Tx, error)
+type fillBlockHashesFn func(height uint64) ([]*bc.Hash, error)
 
-func newBlockCache(fillFn func(hash *bc.Hash) (*types.Block, error)) blockCache {
-       return blockCache{
-               lru:    lru.New(maxCachedBlocks),
-               fillFn: fillFn,
+func newCache(fillBlockHeader fillBlockHeaderFn, fillBlockTxs fillBlockTransactionsFn, fillBlockHashes fillBlockHashesFn) cache {
+       return cache{
+               lruBlockHeaders: common.NewCache(maxCachedBlockHeaders),
+               lruBlockTxs:     common.NewCache(maxCachedBlockTransactions),
+               lruBlockHashes:  common.NewCache(maxCachedBlockHashes),
+
+               fillBlockHeaderFn:      fillBlockHeader,
+               fillBlockTransactionFn: fillBlockTxs,
+               fillBlockHashesFn:      fillBlockHashes,
        }
 }
 
-type blockCache struct {
-       mu     sync.Mutex
-       lru    *lru.Cache
-       fillFn func(hash *bc.Hash) (*types.Block, error)
-       single singleflight.Group
+type cache struct {
+       lruBlockHeaders *common.Cache
+       lruBlockTxs     *common.Cache
+       lruBlockHashes  *common.Cache
+
+       fillBlockHashesFn      func(uint64) ([]*bc.Hash, error)
+       fillBlockTransactionFn func(hash *bc.Hash) ([]*types.Tx, error)
+       fillBlockHeaderFn      func(hash *bc.Hash) (*types.BlockHeader, error)
+
+       sf singleflight.Group
+}
+
+func (c *cache) removeBlockHeader(blockHeader *types.BlockHeader) {
+       c.lruBlockHeaders.Remove(blockHeader.Hash())
 }
 
-func (c *blockCache) lookup(hash *bc.Hash) (*types.Block, error) {
-       if b, ok := c.get(hash); ok {
-               return b, nil
+func (c *cache) lookupBlockHashesByHeight(height uint64) ([]*bc.Hash, error) {
+       if hashes, ok := c.lruBlockHashes.Get(height); ok {
+               return hashes.([]*bc.Hash), nil
        }
 
-       block, err := c.single.Do(hash.String(), func() (interface{}, error) {
-               b, err := c.fillFn(hash)
+       heightStr := strconv.FormatUint(height, 10)
+       hashes, err := c.sf.Do("BlockHashesByHeight:"+heightStr, func() (interface{}, error) {
+               hashes, err := c.fillBlockHashesFn(height)
                if err != nil {
                        return nil, err
                }
-               
-               if b == nil {
-                       return nil, fmt.Errorf("There are no block with given hash %s", hash.String())
-               }
 
-               c.add(b)
-               return b, nil
+               c.lruBlockHashes.Add(height, hashes)
+               return hashes, nil
        })
        if err != nil {
                return nil, err
        }
-       return block.(*types.Block), nil
+       return hashes.([]*bc.Hash), nil
 }
 
-func (c *blockCache) get(hash *bc.Hash) (*types.Block, bool) {
-       c.mu.Lock()
-       block, ok := c.lru.Get(*hash)
-       c.mu.Unlock()
-       if block == nil {
-               return nil, ok
+func (c *cache) removeBlockHashes(height uint64) {
+       c.lruBlockHashes.Remove(height)
+}
+
+func (c *cache) lookupBlockHeader(hash *bc.Hash) (*types.BlockHeader, error) {
+       if data, ok := c.lruBlockHeaders.Get(*hash); ok {
+               return data.(*types.BlockHeader), nil
        }
-       return block.(*types.Block), ok
+
+       blockHeader, err := c.sf.Do("BlockHeader:"+hash.String(), func() (interface{}, error) {
+               blockHeader, err := c.fillBlockHeaderFn(hash)
+               if err != nil {
+                       return nil, err
+               }
+
+               c.lruBlockHeaders.Add(blockHeader.Hash(), blockHeader)
+               return blockHeader, nil
+       })
+       if err != nil {
+               return nil, err
+       }
+       return blockHeader.(*types.BlockHeader), nil
 }
 
-func (c *blockCache) add(block *types.Block) {
-       c.mu.Lock()
-       c.lru.Add(block.Hash(), block)
-       c.mu.Unlock()
+func (c *cache) lookupBlockTxs(hash *bc.Hash) ([]*types.Tx, error) {
+       if data, ok := c.lruBlockTxs.Get(*hash); ok {
+               return data.([]*types.Tx), nil
+       }
+
+       blockTxs, err := c.sf.Do("BlockTxs:"+hash.String(), func() (interface{}, error) {
+               blockTxs, err := c.fillBlockTransactionFn(hash)
+               if err != nil {
+                       return nil, err
+               }
+
+               c.lruBlockTxs.Add(*hash, blockTxs)
+               return blockTxs, nil
+       })
+       if err != nil {
+               return nil, err
+       }
+       return blockTxs.([]*types.Tx), nil
 }
index 4c60fbb..0b8baa2 100644 (file)
@@ -16,34 +16,32 @@ func TestBlockCache(t *testing.T) {
                }
        }
        blocks := make(map[bc.Hash]*types.Block)
-       for i := 0; i < maxCachedBlocks + 10; i++ {
+       blockIndexHashes := make(map[uint64][]*bc.Hash)
+       for i := 0; i < maxCachedBlockHeaders+10; i++ {
                block := newBlock(uint64(i))
+               hash := block.Hash()
+
                blocks[block.Hash()] = block
+               blockIndexHashes[block.Height] = append(blockIndexHashes[block.Height], &hash)
        }
 
-       cache := newBlockCache(func(hash *bc.Hash) (*types.Block, error) {
-               return blocks[*hash], nil
-       })
+       fillBlockHeaderFn := func(hash *bc.Hash) (*types.BlockHeader, error) {
+               return &blocks[*hash].BlockHeader, nil
+       }
 
-       for i := 0; i < maxCachedBlocks + 10; i++ {
-               block := newBlock(uint64(i))
-               hash := block.Hash()
-               cache.lookup(&hash)
+       fillBlockTxsFn := func(hash *bc.Hash) ([]*types.Tx, error) {
+               return blocks[*hash].Transactions, nil
        }
 
-       for i := 0; i < 10; i++ {
-               block := newBlock(uint64(i))
-               hash := block.Hash()
-               if b, _ := cache.get(&hash); b != nil {
-                       t.Fatalf("find old block")
-               }
+       fillBlockHashesFn := func(height uint64) ([]*bc.Hash, error) {
+               return blockIndexHashes[height], nil
        }
 
-       for i := 10; i < maxCachedBlocks + 10; i++ {
+       cache := newCache(fillBlockHeaderFn, fillBlockTxsFn, fillBlockHashesFn)
+
+       for i := 0; i < maxCachedBlockHeaders+10; i++ {
                block := newBlock(uint64(i))
                hash := block.Hash()
-               if b, _ := cache.get(&hash); b == nil {
-                       t.Fatalf("can't find new block")
-               }
+               cache.lookupBlockHeader(&hash)
        }
 }
index cf9da66..6d0e42c 100644 (file)
@@ -1,7 +1,6 @@
 package database
 
 import (
-       "encoding/binary"
        "encoding/json"
        "time"
 
@@ -20,10 +19,10 @@ import (
 const logModule = "leveldb"
 
 var (
-       BlockStoreKey     = []byte("blockStore")
-       BlockPrefix       = []byte("B:")
-       BlockHeaderPrefix = []byte("BH:")
-       TxStatusPrefix    = []byte("BTS:")
+       // BlockStoreKey block store key
+       BlockStoreKey          = []byte("blockStore")
+       // BlockHeaderIndexPrefix  block header index with height
+       BlockHeaderIndexPrefix = []byte("BH:")
 )
 
 func loadBlockStoreStateJSON(db dbm.DB) *protocol.BlockStoreState {
@@ -43,48 +42,35 @@ func loadBlockStoreStateJSON(db dbm.DB) *protocol.BlockStoreState {
 // methods for querying current data.
 type Store struct {
        db    dbm.DB
-       cache blockCache
+       cache cache
 }
 
-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)
-       key := append(BlockHeaderPrefix, buf[:]...)
-       return append(key, hash.Bytes()...)
-}
-
-// GetBlockHeader return the BlockHeader by given hash
-func (s *Store) GetBlockHeader(hash *bc.Hash) (*types.BlockHeader, error) {
-       return nil, nil
-}
+// NewStore creates and returns a new Store object.
+func NewStore(db dbm.DB) *Store {
+       fillBlockHeaderFn := func(hash *bc.Hash) (*types.BlockHeader, error) {
+               return GetBlockHeader(db, hash)
+       }
 
-// GetBlock return the block by given hash
-func GetBlock(db dbm.DB, hash *bc.Hash) (*types.Block, error) {
-       bytez := db.Get(CalcBlockKey(hash))
-       if bytez == nil {
-               return nil, nil
+       fillBlockTxsFn := func(hash *bc.Hash) ([]*types.Tx, error) {
+               return GetBlockTransactions(db, hash)
        }
 
-       block := &types.Block{}
-       err := block.UnmarshalText(bytez)
-       return block, err
-}
+       fillBlockHashesFn := func(height uint64) ([]*bc.Hash, error) {
+               return GetBlockHashesByHeight(db, height)
+       }
 
-// NewStore creates and returns a new Store object.
-func NewStore(db dbm.DB) *Store {
-       cache := newBlockCache(func(hash *bc.Hash) (*types.Block, error) {
-               return GetBlock(db, hash)
-       })
+       cache := newCache(fillBlockHeaderFn, fillBlockTxsFn, fillBlockHashesFn)
        return &Store{
                db:    db,
                cache: cache,
        }
 }
 
+// GetBlockHeader return the BlockHeader by given hash
+func (s *Store) GetBlockHeader(hash *bc.Hash) (*types.BlockHeader, error) {
+       return s.cache.lookupBlockHeader(hash)
+}
+
 // GetUtxo will search the utxo in db
 func (s *Store) GetUtxo(hash *bc.Hash) (*storage.UtxoEntry, error) {
        return getUtxo(s.db, hash)
@@ -96,13 +82,93 @@ func (s *Store) GetContract(hash [32]byte) ([]byte, error) {
 
 // BlockExist check if the block is stored in disk
 func (s *Store) BlockExist(hash *bc.Hash) bool {
-       block, err := s.cache.lookup(hash)
-       return err == nil && block != nil
+       _, err := s.cache.lookupBlockHeader(hash)
+       return err == nil
+}
+
+// SaveBlockHeader persists a new block header in the protocol.
+func (s *Store) SaveBlockHeader(blockHeader *types.BlockHeader) error {
+       binaryBlockHeader, err := blockHeader.MarshalText()
+       if err != nil {
+               return errors.Wrap(err, "Marshal block header")
+       }
+
+       blockHash := blockHeader.Hash()
+       s.db.Set(CalcBlockHeaderKey(&blockHash), binaryBlockHeader)
+       s.cache.removeBlockHeader(blockHeader)
+       return nil
+}
+
+// GetBlockHashesByHeight return the block hash by the specified height
+func (s *Store) GetBlockHashesByHeight(height uint64) ([]*bc.Hash, error) {
+       return s.cache.lookupBlockHashesByHeight(height)
+}
+
+// SaveBlock persists a new block in the protocol.
+func (s *Store) SaveBlock(block *types.Block) error {
+       startTime := time.Now()
+       binaryBlockHeader, err := block.MarshalTextForBlockHeader()
+       if err != nil {
+               return errors.Wrap(err, "Marshal block header")
+       }
+
+       binaryBlockTxs, err := block.MarshalTextForTransactions()
+       if err != nil {
+               return errors.Wrap(err, "Marshal block transactions")
+       }
+
+       blockHashes := []*bc.Hash{}
+       hashes, err := s.GetBlockHashesByHeight(block.Height)
+       if err != nil {
+               return err
+       }
+
+       blockHashes = append(blockHashes, hashes...)
+       blockHash := block.Hash()
+       blockHashes = append(blockHashes, &blockHash)
+       binaryBlockHashes, err := json.Marshal(blockHashes)
+       if err != nil {
+               return errors.Wrap(err, "Marshal block hashes")
+       }
+
+       batch := s.db.NewBatch()
+       batch.Set(CalcBlockHashesKey(block.Height), binaryBlockHashes)
+       batch.Set(CalcBlockHeaderKey(&blockHash), binaryBlockHeader)
+       batch.Set(CalcBlockTransactionsKey(&blockHash), binaryBlockTxs)
+       batch.Set(CalcBlockHeaderIndexKey(block.Height, &blockHash), binaryBlockHeader)
+       batch.Write()
+
+       s.cache.removeBlockHashes(block.Height)
+       log.WithFields(log.Fields{
+               "module":   logModule,
+               "height":   block.Height,
+               "hash":     blockHash.String(),
+               "duration": time.Since(startTime),
+       }).Info("block saved on disk")
+       return nil
+}
+
+// GetBlockTransactions return the Block transactions by given hash
+func (s *Store) GetBlockTransactions(hash *bc.Hash) ([]*types.Tx, error) {
+       return s.cache.lookupBlockTxs(hash)
 }
 
 // GetBlock return the block by given hash
 func (s *Store) GetBlock(hash *bc.Hash) (*types.Block, error) {
-       return s.cache.lookup(hash)
+       blockHeader, err := s.GetBlockHeader(hash)
+       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
 }
 
 // GetTransactionsUtxo will return all the utxo that related to the input txs
@@ -115,10 +181,11 @@ func (s *Store) GetStoreStatus() *protocol.BlockStoreState {
        return loadBlockStoreStateJSON(s.db)
 }
 
+// LoadBlockIndex loadblockIndex by bestHeight
 func (s *Store) LoadBlockIndex(stateBestHeight uint64) (*state.BlockIndex, error) {
        startTime := time.Now()
        blockIndex := state.NewBlockIndex()
-       bhIter := s.db.IteratorPrefix(BlockHeaderPrefix)
+       bhIter := s.db.IteratorPrefix(BlockHeaderIndexPrefix)
        defer bhIter.Release()
 
        var lastNode *state.BlockNode
@@ -158,34 +225,6 @@ func (s *Store) LoadBlockIndex(stateBestHeight uint64) (*state.BlockIndex, error
        return blockIndex, nil
 }
 
-// SaveBlock persists a new block in the protocol.
-func (s *Store) SaveBlock(block *types.Block) error {
-       startTime := time.Now()
-       binaryBlock, err := block.MarshalText()
-       if err != nil {
-               return errors.Wrap(err, "Marshal block meta")
-       }
-
-       binaryBlockHeader, err := block.BlockHeader.MarshalText()
-       if err != nil {
-               return errors.Wrap(err, "Marshal block header")
-       }
-
-       blockHash := block.Hash()
-       batch := s.db.NewBatch()
-       batch.Set(CalcBlockKey(&blockHash), binaryBlock)
-       batch.Set(CalcBlockHeaderKey(block.Height, &blockHash), binaryBlockHeader)
-       batch.Write()
-
-       log.WithFields(log.Fields{
-               "module":   logModule,
-               "height":   block.Height,
-               "hash":     blockHash.String(),
-               "duration": time.Since(startTime),
-       }).Info("block saved on disk")
-       return nil
-}
-
 // SaveChainStatus save the core's newest status && delete old status
 func (s *Store) SaveChainStatus(node *state.BlockNode, view *state.UtxoViewpoint, contractView *state.ContractViewpoint) error {
        batch := s.db.NewBatch()
diff --git a/database/store_geter.go b/database/store_geter.go
new file mode 100644 (file)
index 0000000..d5bd254
--- /dev/null
@@ -0,0 +1,94 @@
+package database
+
+import (
+       "encoding/binary"
+       "encoding/json"
+       "fmt"
+
+       dbm "github.com/bytom/bytom/database/leveldb"
+       "github.com/bytom/bytom/protocol/bc"
+       "github.com/bytom/bytom/protocol/bc/types"
+)
+
+const (
+       colon = byte(0x3a)
+
+       blockStore byte = iota
+       blockHashes
+       blockHeader
+       blockTransactons
+)
+
+var (
+       // BlockHashesKeyPrefix key Prefix
+       BlockHashesKeyPrefix = []byte{blockHashes, colon}
+       blockHeaderKeyPrefix = []byte{blockHeader, colon}
+       blockTransactionsKey = []byte{blockTransactons, colon}
+)
+
+// CalcBlockHeaderKey make up header key with prefix + hash
+func CalcBlockHeaderKey(hash *bc.Hash) []byte {
+       return append(blockHeaderKeyPrefix, hash.Bytes()...)
+}
+
+// CalcBlockHashesKey make up hashes key with prefix + height
+func CalcBlockHashesKey(height uint64) []byte {
+       buf := [8]byte{}
+       binary.BigEndian.PutUint64(buf[:], height)
+       return append(BlockHashesKeyPrefix, buf[:]...)
+}
+
+// CalcBlockTransactionsKey make up txs key with prefix + hash
+func CalcBlockTransactionsKey(hash *bc.Hash) []byte {
+       return append(blockTransactionsKey, hash.Bytes()...)
+}
+
+// CalcBlockHeaderIndexKey make up BlockHeaderIndexKey with prefix + hash
+func CalcBlockHeaderIndexKey(height uint64, hash *bc.Hash) []byte {
+       buf := [8]byte{}
+       binary.BigEndian.PutUint64(buf[:], height)
+       key := append(BlockHeaderIndexPrefix, buf[:]...)
+       return append(key, hash.Bytes()...)
+}
+
+// GetBlockHeader return the block header by given hash
+func GetBlockHeader(db dbm.DB, hash *bc.Hash) (*types.BlockHeader, error) {
+       binaryBlockHeader := db.Get(CalcBlockHeaderKey(hash))
+       if binaryBlockHeader == nil {
+               return nil, fmt.Errorf("There are no blockHeader with given hash %s", hash.String())
+       }
+
+       blockHeader := &types.BlockHeader{}
+       if err := blockHeader.UnmarshalText(binaryBlockHeader); err != nil {
+               return nil, err
+       }
+       return blockHeader, nil
+}
+
+// GetBlockTransactions return the block transactions by given hash
+func GetBlockTransactions(db dbm.DB, hash *bc.Hash) ([]*types.Tx, error) {
+       binaryBlockTxs := db.Get(CalcBlockTransactionsKey(hash))
+       if binaryBlockTxs == nil {
+               return nil, fmt.Errorf("There are no block transactions with given hash %s", hash.String())
+       }
+
+       block := &types.Block{}
+       if err := block.UnmarshalText(binaryBlockTxs); err != nil {
+               return nil, err
+       }
+       return block.Transactions, nil
+}
+
+// GetBlockHashesByHeight return block hashes by given height
+func GetBlockHashesByHeight(db dbm.DB, height uint64) ([]*bc.Hash, error) {
+       binaryHashes := db.Get(CalcBlockHashesKey(height))
+       if binaryHashes == nil {
+               return []*bc.Hash{}, nil
+       }
+
+       hashes := []*bc.Hash{}
+       if err := json.Unmarshal(binaryHashes, &hashes); err != nil {
+               return nil, err
+       }
+       return hashes, nil
+}
index 2b9b493..5d912f8 100644 (file)
@@ -91,7 +91,7 @@ func TestLoadBlockIndexBestHeight(t *testing.T) {
 
                for _, block := range savedBlocks {
                        blockHash := block.Hash()
-                       if block.Height <= c.stateBestHeight != index.BlockExist(&blockHash) {
+                       if existIndex := index.BlockExist(&blockHash); (block.Height <= c.stateBestHeight) != existIndex {
                                t.Errorf("Error in load block index")
                        }
                }
@@ -135,6 +135,7 @@ func TestLoadBlockIndexEquals(t *testing.T) {
                t.Errorf("got block index:%v, expect block index:%v", index, expectBlockIndex)
        }
 }
+
 func TestSaveChainStatus(t *testing.T) {
        defer os.RemoveAll("temp")
        testDB := dbm.NewDB("testdb", "leveldb", "temp")
@@ -176,10 +177,13 @@ func TestSaveChainStatus(t *testing.T) {
 }
 
 func TestSaveBlock(t *testing.T) {
-       defer os.RemoveAll("temp")
        testDB := dbm.NewDB("testdb", "leveldb", "temp")
-       store := NewStore(testDB)
+       defer func() {
+               testDB.Close()
+               os.RemoveAll("temp")
+       }()
 
+       store := NewStore(testDB)
        block := config.GenesisBlock()
        if err := store.SaveBlock(block); err != nil {
                t.Fatal(err)
@@ -197,7 +201,7 @@ func TestSaveBlock(t *testing.T) {
                t.Errorf("got block:%v, expect block:%v", gotBlock, block)
        }
 
-       data := store.db.Get(CalcBlockHeaderKey(block.Height, &blockHash))
+       data := store.db.Get(CalcBlockHeaderIndexKey(block.Height, &blockHash))
        gotBlockHeader := types.BlockHeader{}
        if err := gotBlockHeader.UnmarshalText(data); err != nil {
                t.Fatal(err)
@@ -207,3 +211,4 @@ func TestSaveBlock(t *testing.T) {
                t.Errorf("got block header:%v, expect block header:%v", gotBlockHeader, block.BlockHeader)
        }
 }
+
index 22cf900..38b20a1 100644 (file)
@@ -158,5 +158,9 @@ func (ls *levelDBStorage) readBlock(height uint64) (*types.Block, error) {
        }
 
        block := &types.Block{}
-       return block, block.UnmarshalText(binaryBlock)
+       if err := block.UnmarshalText(binaryBlock); err != nil {
+               return nil, err
+       }
+
+       return block, nil
 }
index e80e90f..fb3c38b 100644 (file)
@@ -25,13 +25,15 @@ type Block struct {
        Transactions []*Tx
 }
 
-// MarshalText fulfills the json.Marshaler interface. This guarantees that
-// blocks will get deserialized correctly when being parsed from HTTP requests.
-func (b *Block) MarshalText() ([]byte, error) {
+func (b *Block) marshalText(serflags uint8) ([]byte, error) {
        buf := bufpool.Get()
        defer bufpool.Put(buf)
 
-       if _, err := b.WriteTo(buf); err != nil {
+       ew := errors.NewWriter(buf)
+       if err := b.writeTo(ew, serflags); err != nil {
+               return nil, err
+       }
+       if err := ew.Err(); err != nil {
                return nil, err
        }
 
@@ -40,6 +42,22 @@ func (b *Block) MarshalText() ([]byte, error) {
        return enc, nil
 }
 
+// MarshalText fulfills the json.Marshaler interface. This guarantees that
+// blocks will get deserialized correctly when being parsed from HTTP requests.
+func (b *Block) MarshalText() ([]byte, error) {
+       return b.marshalText(SerBlockFull)
+}
+
+// MarshalTextForBlockHeader fulfills the json.Marshaler interface.
+func (b *Block) MarshalTextForBlockHeader() ([]byte, error) {
+       return b.marshalText(SerBlockHeader)
+}
+
+// MarshalTextForTransactions fulfills the json.Marshaler interface.
+func (b *Block) MarshalTextForTransactions() ([]byte, error) {
+       return b.marshalText(SerBlockTransactions)
+}
+
 // UnmarshalText fulfills the encoding.TextUnmarshaler interface.
 func (b *Block) UnmarshalText(text []byte) error {
        decoded := make([]byte, hex.DecodedLen(len(text)))
@@ -59,12 +77,12 @@ func (b *Block) UnmarshalText(text []byte) error {
 }
 
 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
        }
 
@@ -84,7 +102,7 @@ func (b *Block) readFrom(r *blockchain.Reader) error {
        return nil
 }
 
-// WriteTo will write block to input io.Writer
+// WriteTo write block to io.Writer
 func (b *Block) WriteTo(w io.Writer) (int64, error) {
        ew := errors.NewWriter(w)
        if err := b.writeTo(ew, SerBlockFull); err != nil {
index 37d2990..ae03d44 100644 (file)
@@ -57,16 +57,29 @@ func (bh *BlockHeader) UnmarshalText(text []byte) error {
                return err
        }
 
-       _, err := bh.readFrom(blockchain.NewReader(decoded))
-       return err
+       serflag, err := bh.readFrom(blockchain.NewReader(decoded))
+       if err != nil {
+               return err
+       }
+
+       if serflag == SerBlockTransactions {
+               return fmt.Errorf("unsupported serialization flags 0x%02x", serflag)
+       }
+
+       return nil
 }
 
 func (bh *BlockHeader) readFrom(r *blockchain.Reader) (serflag uint8, err error) {
        var serflags [1]byte
-       io.ReadFull(r, serflags[:])
+       if _, err := io.ReadFull(r, serflags[:]); err != nil {
+               return 0, err
+       }
+
        serflag = serflags[0]
        switch serflag {
        case SerBlockHeader, SerBlockFull:
+       case SerBlockTransactions:
+               return
        default:
                return 0, fmt.Errorf("unsupported serialization flags 0x%x", serflags)
        }
@@ -113,6 +126,10 @@ func (bh *BlockHeader) WriteTo(w io.Writer) (int64, error) {
 
 func (bh *BlockHeader) writeTo(w io.Writer, serflags uint8) (err error) {
        w.Write([]byte{serflags})
+       if serflags == SerBlockTransactions {
+               return nil
+       }
+
        if _, err = blockchain.WriteVarint63(w, bh.Version); err != nil {
                return err
        }
index 75fddde..60e0aa0 100644 (file)
@@ -154,7 +154,6 @@ func TestMarshalBlockHeader(t *testing.T) {
                        }, ""),
                },
        }
-
        for i, test := range cases {
                got, err := test.blockHeader.MarshalText()
                if err != nil && err != test.wantError {
@@ -237,6 +236,7 @@ func TestUnmarshalBlockHeader(t *testing.T) {
                                "904e", // block height
                                "c34048bd60c4c13144fd34f408627d1be68f6cb4fdd34e879d6d791060ea73a0", // prev block hash
                                "e8b287d905", // timestamp
+                               "0100",       // BlockWitness
                                "20",         // commitment extensible field length
                                "ad9ac003d08ff305181a345d64fe0b02311cc1a6ec04ab73f3318d90139bfe03", // transactions merkle root
                        }, ""),
@@ -249,6 +249,7 @@ func TestUnmarshalBlockHeader(t *testing.T) {
                                "908", // block height (error with odd length)
                                "c34048bd60c4c13144fd34f408627d1be68f6cb4fdd34e879d6d791060ea73a0", // prev block hash
                                "e8b287d905", // timestamp
+                               "0100",       // BlockWitness
                                "20",         // commitment extensible field length
                                "ad9ac003d08ff305181a345d64fe0b02311cc1a6ec04ab73f3318d90139bfe03", // transactions merkle root
                        }, ""),
@@ -261,6 +262,7 @@ func TestUnmarshalBlockHeader(t *testing.T) {
                                "ffffffffffffffffff", // block height
                                "c34048bd60c4c13144fd34f408627d1be68f6cb4fdd34e879d6d791060ea73a0", // prev block hash
                                "e8b287d905", // timestamp
+                               "0100",       // BlockWitness
                                "20",         // commitment extensible field length
                                "ad9ac003d08ff305181a345d64fe0b02311cc1a6ec04ab73f3318d90139bfe03", // transactions merkle root
                                "0100", // block witness
index 6f80e4f..11a9ee4 100644 (file)
@@ -34,8 +34,8 @@ func TestBlock(t *testing.T) {
                                "01", // version
                                "01", // block height
                                "0000000000000000000000000000000000000000000000000000000000000000", // prev block hash
-                               "00", // timestamp
-                               "20", // commitment extensible field length
+                               "00",   // timestamp
+                               "20",   // commitment extensible field length
                                "0000000000000000000000000000000000000000000000000000000000000000", // transactions merkle root
                                "0100", // block witness
                                "0100", // sup links
@@ -200,7 +200,6 @@ func TestReadFrom(t *testing.T) {
        }
 
        for _, c := range cases {
-
                blockBytes, err := hex.DecodeString(c.rawBlock)
                if err != nil {
                        t.Fatal(err)
index 48f16b0..d36fb9a 100644 (file)
@@ -182,3 +182,4 @@ func (tx *TxData) writeTo(w io.Writer, serflags byte) error {
        }
        return nil
 }
+
index 41a3ccf..0495232 100644 (file)
@@ -485,7 +485,7 @@ func TestProcessBlock(t *testing.T) {
                                        PreviousBlockHash: blockMap[0][0].block.Hash(),
                                },
                        },
-                       wantStore: createStoreItems([]int{0}, []*attachBlock{blockMap[0][0]}),
+                       wantStore: createStoreEntries([]int{0}, []*attachBlock{blockMap[0][0]}),
                        wantBlockIndex: state.NewBlockIndexWithData(
                                map[bc.Hash]*state.BlockNode{
                                        blockMap[0][0].block.Hash(): mustCreateBlockNode(&blockMap[0][0].block.BlockHeader),
@@ -500,7 +500,7 @@ func TestProcessBlock(t *testing.T) {
                {
                        desc:      "process a orphan block normally",
                        newBlock:  blockMap[2][0].block,
-                       wantStore: createStoreItems([]int{0}, []*attachBlock{blockMap[0][0]}),
+                       wantStore: createStoreEntries([]int{0}, []*attachBlock{blockMap[0][0]}),
                        wantBlockIndex: state.NewBlockIndexWithData(
                                map[bc.Hash]*state.BlockNode{
                                        blockMap[0][0].block.Hash(): mustNewBlockNode(&blockMap[0][0].block.BlockHeader, nil),
@@ -519,7 +519,7 @@ func TestProcessBlock(t *testing.T) {
                {
                        desc:      "attach a block normally",
                        newBlock:  blockMap[1][0].block,
-                       wantStore: createStoreItems([]int{0, 1}, []*attachBlock{blockMap[0][0], blockMap[1][0]}),
+                       wantStore: createStoreEntries([]int{0, 1}, []*attachBlock{blockMap[0][0], blockMap[1][0]}),
                        wantBlockIndex: state.NewBlockIndexWithData(
                                map[bc.Hash]*state.BlockNode{
                                        blockMap[0][0].block.Hash(): mustCreateBlockNode(&blockMap[0][0].block.BlockHeader),
@@ -537,8 +537,8 @@ func TestProcessBlock(t *testing.T) {
                {
                        desc:      "init genesis block from db",
                        newBlock:  blockMap[1][0].block,
-                       initStore: createStoreItems([]int{0}, []*attachBlock{blockMap[0][0]}),
-                       wantStore: createStoreItems([]int{0, 1}, []*attachBlock{blockMap[0][0], blockMap[1][0]}),
+                       initStore: createStoreEntries([]int{0}, []*attachBlock{blockMap[0][0]}),
+                       wantStore: createStoreEntries([]int{0, 1}, []*attachBlock{blockMap[0][0], blockMap[1][0]}),
                        wantBlockIndex: state.NewBlockIndexWithData(
                                map[bc.Hash]*state.BlockNode{
                                        blockMap[0][0].block.Hash(): mustCreateBlockNode(&blockMap[0][0].block.BlockHeader),
@@ -556,8 +556,8 @@ func TestProcessBlock(t *testing.T) {
                {
                        desc:      "attach a block to fork chain normally, not rollback",
                        newBlock:  blockMap[2][0].block,
-                       initStore: createStoreItems([]int{0, 1}, []*attachBlock{blockMap[0][0], blockMap[1][0], blockMap[1][1]}),
-                       wantStore: createStoreItems([]int{0, 1, 3}, []*attachBlock{blockMap[0][0], blockMap[1][0], blockMap[1][1], blockMap[2][0]}),
+                       initStore: createStoreEntries([]int{0, 1}, []*attachBlock{blockMap[0][0], blockMap[1][0], blockMap[1][1]}),
+                       wantStore: createStoreEntries([]int{0, 1, 3}, []*attachBlock{blockMap[0][0], blockMap[1][0], blockMap[1][1], blockMap[2][0]}),
                        wantBlockIndex: state.NewBlockIndexWithData(
                                map[bc.Hash]*state.BlockNode{
                                        blockMap[0][0].block.Hash(): mustCreateBlockNode(&blockMap[0][0].block.BlockHeader),
@@ -578,11 +578,11 @@ func TestProcessBlock(t *testing.T) {
                {
                        desc:     "attach a block with btm transaction normally",
                        newBlock: blockMap[2][1].block,
-                       initStore: createStoreItems([]int{0, 1}, []*attachBlock{blockMap[0][0], blockMap[1][0]}, &storeItem{
+                       initStore: createStoreEntries([]int{0, 1}, []*attachBlock{blockMap[0][0], blockMap[1][0]}, &storeItem{
                                key: database.CalcUtxoKey(hashPtr(testutil.MustDecodeHash("c93b687f98d039046cd2afd514c62f5d1c2c3b0804e4845b00a33e736ef48a33"))),
                                val: &storage.UtxoEntry{IsCoinBase: false, BlockHeight: 1, Spent: false},
                        }),
-                       wantStore: createStoreItems([]int{0, 1, 2}, []*attachBlock{blockMap[0][0], blockMap[1][0], blockMap[2][1]}, &storeItem{
+                       wantStore: createStoreEntries([]int{0, 1, 2}, []*attachBlock{blockMap[0][0], blockMap[1][0], blockMap[2][1]}, &storeItem{
                                key: database.CalcUtxoKey(hashPtr(testutil.MustDecodeHash("c93b687f98d039046cd2afd514c62f5d1c2c3b0804e4845b00a33e736ef48a33"))),
                                val: &storage.UtxoEntry{IsCoinBase: false, BlockHeight: 1, Spent: false},
                        }),
@@ -605,11 +605,11 @@ func TestProcessBlock(t *testing.T) {
                {
                        desc:     "attach a block with retire transaction normally",
                        newBlock: blockMap[2][2].block,
-                       initStore: createStoreItems([]int{0, 1}, []*attachBlock{blockMap[0][0], blockMap[1][0]}, &storeItem{
+                       initStore: createStoreEntries([]int{0, 1}, []*attachBlock{blockMap[0][0], blockMap[1][0]}, &storeItem{
                                key: database.CalcUtxoKey(hashPtr(testutil.MustDecodeHash("c93b687f98d039046cd2afd514c62f5d1c2c3b0804e4845b00a33e736ef48a33"))),
                                val: &storage.UtxoEntry{IsCoinBase: false, BlockHeight: 1, Spent: false},
                        }),
-                       wantStore: createStoreItems([]int{0, 1, 2}, []*attachBlock{blockMap[0][0], blockMap[1][0], blockMap[2][2]}, &storeItem{
+                       wantStore: createStoreEntries([]int{0, 1, 2}, []*attachBlock{blockMap[0][0], blockMap[1][0], blockMap[2][2]}, &storeItem{
                                key: database.CalcUtxoKey(hashPtr(testutil.MustDecodeHash("c93b687f98d039046cd2afd514c62f5d1c2c3b0804e4845b00a33e736ef48a33"))),
                                val: &storage.UtxoEntry{IsCoinBase: false, BlockHeight: 1, Spent: false},
                        }),
@@ -632,11 +632,11 @@ func TestProcessBlock(t *testing.T) {
                {
                        desc:     "attach a block with issuance transaction normally",
                        newBlock: blockMap[2][3].block,
-                       initStore: createStoreItems([]int{0, 1}, []*attachBlock{blockMap[0][0], blockMap[1][0]}, &storeItem{
+                       initStore: createStoreEntries([]int{0, 1}, []*attachBlock{blockMap[0][0], blockMap[1][0]}, &storeItem{
                                key: database.CalcUtxoKey(hashPtr(testutil.MustDecodeHash("c93b687f98d039046cd2afd514c62f5d1c2c3b0804e4845b00a33e736ef48a33"))),
                                val: &storage.UtxoEntry{IsCoinBase: false, BlockHeight: 1, Spent: false},
                        }),
-                       wantStore: createStoreItems([]int{0, 1, 2}, []*attachBlock{blockMap[0][0], blockMap[1][0], blockMap[2][3]}, &storeItem{
+                       wantStore: createStoreEntries([]int{0, 1, 2}, []*attachBlock{blockMap[0][0], blockMap[1][0], blockMap[2][3]}, &storeItem{
                                key: database.CalcUtxoKey(hashPtr(testutil.MustDecodeHash("c93b687f98d039046cd2afd514c62f5d1c2c3b0804e4845b00a33e736ef48a33"))),
                                val: &storage.UtxoEntry{IsCoinBase: false, BlockHeight: 1, Spent: false},
                        }),
@@ -659,11 +659,11 @@ func TestProcessBlock(t *testing.T) {
                {
                        desc:     "attach a block with issuance transaction but status fail is true",
                        newBlock: blockMap[2][4].block,
-                       initStore: createStoreItems([]int{0, 1}, []*attachBlock{blockMap[0][0], blockMap[1][0]}, &storeItem{
+                       initStore: createStoreEntries([]int{0, 1}, []*attachBlock{blockMap[0][0], blockMap[1][0]}, &storeItem{
                                key: database.CalcUtxoKey(hashPtr(testutil.MustDecodeHash("c93b687f98d039046cd2afd514c62f5d1c2c3b0804e4845b00a33e736ef48a33"))),
                                val: &storage.UtxoEntry{IsCoinBase: false, BlockHeight: 1, Spent: false},
                        }),
-                       wantStore: createStoreItems([]int{0, 1}, []*attachBlock{blockMap[0][0], blockMap[1][0]}, &storeItem{
+                       wantStore: createStoreEntries([]int{0, 1}, []*attachBlock{blockMap[0][0], blockMap[1][0]}, &storeItem{
                                key: database.CalcUtxoKey(hashPtr(testutil.MustDecodeHash("c93b687f98d039046cd2afd514c62f5d1c2c3b0804e4845b00a33e736ef48a33"))),
                                val: &storage.UtxoEntry{IsCoinBase: false, BlockHeight: 1, Spent: false},
                        }),
@@ -684,14 +684,14 @@ func TestProcessBlock(t *testing.T) {
                {
                        desc:     "attach a block with non btm transaction",
                        newBlock: blockMap[2][5].block,
-                       initStore: createStoreItems([]int{0, 1}, []*attachBlock{blockMap[0][0], blockMap[1][0]}, &storeItem{
+                       initStore: createStoreEntries([]int{0, 1}, []*attachBlock{blockMap[0][0], blockMap[1][0]}, &storeItem{
                                key: database.CalcUtxoKey(hashPtr(testutil.MustDecodeHash("be164edbce8bcd1d890c1164541b8418fdcb257499757d3b88561bca06e97e29"))),
                                val: &storage.UtxoEntry{IsCoinBase: false, BlockHeight: 1, Spent: false},
                        }, &storeItem{
                                key: database.CalcUtxoKey(hashPtr(testutil.MustDecodeHash("c93b687f98d039046cd2afd514c62f5d1c2c3b0804e4845b00a33e736ef48a33"))),
                                val: &storage.UtxoEntry{IsCoinBase: false, BlockHeight: 1, Spent: false},
                        }),
-                       wantStore: createStoreItems([]int{0, 1, 2}, []*attachBlock{blockMap[0][0], blockMap[1][0], blockMap[2][5]}, &storeItem{
+                       wantStore: createStoreEntries([]int{0, 1, 2}, []*attachBlock{blockMap[0][0], blockMap[1][0], blockMap[2][5]}, &storeItem{
                                key: database.CalcUtxoKey(hashPtr(testutil.MustDecodeHash("be164edbce8bcd1d890c1164541b8418fdcb257499757d3b88561bca06e97e29"))),
                                val: &storage.UtxoEntry{IsCoinBase: false, BlockHeight: 1, Spent: false},
                        }, &storeItem{
@@ -717,14 +717,14 @@ func TestProcessBlock(t *testing.T) {
                {
                        desc:     "attach a block with non btm transaction but status fail is true",
                        newBlock: blockMap[2][6].block,
-                       initStore: createStoreItems([]int{0, 1}, []*attachBlock{blockMap[0][0], blockMap[1][0]}, &storeItem{
+                       initStore: createStoreEntries([]int{0, 1}, []*attachBlock{blockMap[0][0], blockMap[1][0]}, &storeItem{
                                key: database.CalcUtxoKey(hashPtr(testutil.MustDecodeHash("be164edbce8bcd1d890c1164541b8418fdcb257499757d3b88561bca06e97e29"))),
                                val: &storage.UtxoEntry{IsCoinBase: false, BlockHeight: 1, Spent: false},
                        }, &storeItem{
                                key: database.CalcUtxoKey(hashPtr(testutil.MustDecodeHash("c93b687f98d039046cd2afd514c62f5d1c2c3b0804e4845b00a33e736ef48a33"))),
                                val: &storage.UtxoEntry{IsCoinBase: false, BlockHeight: 1, Spent: false},
                        }),
-                       wantStore: createStoreItems([]int{0, 1}, []*attachBlock{blockMap[0][0], blockMap[1][0]}, &storeItem{
+                       wantStore: createStoreEntries([]int{0, 1}, []*attachBlock{blockMap[0][0], blockMap[1][0]}, &storeItem{
                                key: database.CalcUtxoKey(hashPtr(testutil.MustDecodeHash("be164edbce8bcd1d890c1164541b8418fdcb257499757d3b88561bca06e97e29"))),
                                val: &storage.UtxoEntry{IsCoinBase: false, BlockHeight: 1, Spent: false},
                        }, &storeItem{
@@ -748,8 +748,8 @@ func TestProcessBlock(t *testing.T) {
                {
                        desc:      "rollback a block only has coinbase transaction",
                        newBlock:  blockMap[2][0].block,
-                       initStore: createStoreItems([]int{0, 2}, []*attachBlock{blockMap[0][0], blockMap[1][0], blockMap[1][1]}),
-                       wantStore: createStoreItems([]int{0, 1, 3}, []*attachBlock{blockMap[0][0], blockMap[1][0], blockMap[1][1], blockMap[2][0]}),
+                       initStore: createStoreEntries([]int{0, 2}, []*attachBlock{blockMap[0][0], blockMap[1][0], blockMap[1][1]}),
+                       wantStore: createStoreEntries([]int{0, 1, 3}, []*attachBlock{blockMap[0][0], blockMap[1][0], blockMap[1][1], blockMap[2][0]}),
                        wantBlockIndex: state.NewBlockIndexWithData(
                                map[bc.Hash]*state.BlockNode{
                                        blockMap[0][0].block.Hash(): mustCreateBlockNode(&blockMap[0][0].block.BlockHeader),
@@ -770,11 +770,11 @@ func TestProcessBlock(t *testing.T) {
                {
                        desc:     "rollback a block has spend btm transaction",
                        newBlock: blockMap[3][0].block,
-                       initStore: createStoreItems([]int{0, 1, 3}, []*attachBlock{blockMap[0][0], blockMap[1][0], blockMap[2][0], blockMap[2][1]}, &storeItem{
+                       initStore: createStoreEntries([]int{0, 1, 3}, []*attachBlock{blockMap[0][0], blockMap[1][0], blockMap[2][0], blockMap[2][1]}, &storeItem{
                                key: database.CalcUtxoKey(hashPtr(testutil.MustDecodeHash("c93b687f98d039046cd2afd514c62f5d1c2c3b0804e4845b00a33e736ef48a33"))),
                                val: &storage.UtxoEntry{IsCoinBase: false, BlockHeight: 1, Spent: false},
                        }),
-                       wantStore: createStoreItems([]int{0, 1, 2, 4}, []*attachBlock{blockMap[0][0], blockMap[1][0], blockMap[2][0], blockMap[2][1], blockMap[3][0]}, &storeItem{
+                       wantStore: createStoreEntries([]int{0, 1, 2, 4}, []*attachBlock{blockMap[0][0], blockMap[1][0], blockMap[2][0], blockMap[2][1], blockMap[3][0]}, &storeItem{
                                key: database.CalcUtxoKey(hashPtr(testutil.MustDecodeHash("c93b687f98d039046cd2afd514c62f5d1c2c3b0804e4845b00a33e736ef48a33"))),
                                val: &storage.UtxoEntry{IsCoinBase: false, BlockHeight: 0, Spent: false},
                        }),
@@ -800,11 +800,11 @@ func TestProcessBlock(t *testing.T) {
                {
                        desc:     "rollback a block has issuance transaction",
                        newBlock: blockMap[3][0].block,
-                       initStore: createStoreItems([]int{0, 1, 3}, []*attachBlock{blockMap[0][0], blockMap[1][0], blockMap[2][0], blockMap[2][3]}, &storeItem{
+                       initStore: createStoreEntries([]int{0, 1, 3}, []*attachBlock{blockMap[0][0], blockMap[1][0], blockMap[2][0], blockMap[2][3]}, &storeItem{
                                key: database.CalcUtxoKey(hashPtr(testutil.MustDecodeHash("c93b687f98d039046cd2afd514c62f5d1c2c3b0804e4845b00a33e736ef48a33"))),
                                val: &storage.UtxoEntry{IsCoinBase: false, BlockHeight: 1, Spent: false},
                        }),
-                       wantStore: createStoreItems([]int{0, 1, 2, 4}, []*attachBlock{blockMap[0][0], blockMap[1][0], blockMap[2][0], blockMap[2][3], blockMap[3][0]}, &storeItem{
+                       wantStore: createStoreEntries([]int{0, 1, 2, 4}, []*attachBlock{blockMap[0][0], blockMap[1][0], blockMap[2][0], blockMap[2][3], blockMap[3][0]}, &storeItem{
                                key: database.CalcUtxoKey(hashPtr(testutil.MustDecodeHash("c93b687f98d039046cd2afd514c62f5d1c2c3b0804e4845b00a33e736ef48a33"))),
                                val: &storage.UtxoEntry{IsCoinBase: false, BlockHeight: 0, Spent: false},
                        }),
@@ -830,7 +830,7 @@ func TestProcessBlock(t *testing.T) {
                {
                        desc:     "rollback a block has issuance transaction but status fail is true",
                        newBlock: blockMap[3][0].block,
-                       initStore: createStoreItems([]int{0, 1, 3}, []*attachBlock{blockMap[0][0], blockMap[1][0], blockMap[2][0], blockMap[2][4]}, &storeItem{
+                       initStore: createStoreEntries([]int{0, 1, 3}, []*attachBlock{blockMap[0][0], blockMap[1][0], blockMap[2][0], blockMap[2][4]}, &storeItem{
                                key: database.CalcUtxoKey(hashPtr(testutil.MustDecodeHash("c93b687f98d039046cd2afd514c62f5d1c2c3b0804e4845b00a33e736ef48a33"))),
                                val: &storage.UtxoEntry{IsCoinBase: false, BlockHeight: 1, Spent: false},
                        }),
@@ -843,14 +843,14 @@ func TestProcessBlock(t *testing.T) {
                {
                        desc:     "rollback a block has spend non btm",
                        newBlock: blockMap[3][0].block,
-                       initStore: createStoreItems([]int{0, 1, 3}, []*attachBlock{blockMap[0][0], blockMap[1][0], blockMap[2][0], blockMap[2][5]}, &storeItem{
+                       initStore: createStoreEntries([]int{0, 1, 3}, []*attachBlock{blockMap[0][0], blockMap[1][0], blockMap[2][0], blockMap[2][5]}, &storeItem{
                                key: database.CalcUtxoKey(hashPtr(testutil.MustDecodeHash("c93b687f98d039046cd2afd514c62f5d1c2c3b0804e4845b00a33e736ef48a33"))),
                                val: &storage.UtxoEntry{IsCoinBase: false, BlockHeight: 1, Spent: false},
                        }, &storeItem{
                                key: database.CalcUtxoKey(hashPtr(testutil.MustDecodeHash("be164edbce8bcd1d890c1164541b8418fdcb257499757d3b88561bca06e97e29"))),
                                val: &storage.UtxoEntry{IsCoinBase: false, BlockHeight: 1, Spent: false},
                        }),
-                       wantStore: createStoreItems([]int{0, 1, 2, 4}, []*attachBlock{blockMap[0][0], blockMap[1][0], blockMap[2][0], blockMap[2][5], blockMap[3][0]}, &storeItem{
+                       wantStore: createStoreEntries([]int{0, 1, 2, 4}, []*attachBlock{blockMap[0][0], blockMap[1][0], blockMap[2][0], blockMap[2][5], blockMap[3][0]}, &storeItem{
                                key: database.CalcUtxoKey(hashPtr(testutil.MustDecodeHash("c93b687f98d039046cd2afd514c62f5d1c2c3b0804e4845b00a33e736ef48a33"))),
                                val: &storage.UtxoEntry{IsCoinBase: false, BlockHeight: 0, Spent: false},
                        }, &storeItem{
@@ -879,7 +879,7 @@ func TestProcessBlock(t *testing.T) {
                {
                        desc:     "rollback a block has spend non btm but status fail is true",
                        newBlock: blockMap[3][0].block,
-                       initStore: createStoreItems([]int{0, 1, 3}, []*attachBlock{blockMap[0][0], blockMap[1][0], blockMap[2][0], blockMap[2][6]}, &storeItem{
+                       initStore: createStoreEntries([]int{0, 1, 3}, []*attachBlock{blockMap[0][0], blockMap[1][0], blockMap[2][0], blockMap[2][6]}, &storeItem{
                                key: database.CalcUtxoKey(hashPtr(testutil.MustDecodeHash("c93b687f98d039046cd2afd514c62f5d1c2c3b0804e4845b00a33e736ef48a33"))),
                                val: &storage.UtxoEntry{IsCoinBase: false, BlockHeight: 1, Spent: false},
                        }, &storeItem{
@@ -895,14 +895,14 @@ func TestProcessBlock(t *testing.T) {
                {
                        desc:      "rollback a block only has coinbase tx, and from orphan manage",
                        newBlock:  blockMap[1][0].block,
-                       initStore: createStoreItems([]int{0, 1}, []*attachBlock{blockMap[0][0], blockMap[1][1]}),
+                       initStore: createStoreEntries([]int{0, 1}, []*attachBlock{blockMap[0][0], blockMap[1][1]}),
                        initOrphanManage: protocol.NewOrphanManageWithData(
                                map[bc.Hash]*protocol.OrphanBlock{
                                        blockMap[2][0].block.Hash(): protocol.NewOrphanBlock(blockMap[2][0].block, time.Now().Add(time.Minute*60)),
                                },
                                map[bc.Hash][]*bc.Hash{blockMap[2][0].block.PreviousBlockHash: {hashPtr(blockMap[2][0].block.Hash())}},
                        ),
-                       wantStore: createStoreItems([]int{0, 1, 3}, []*attachBlock{blockMap[0][0], blockMap[1][0], blockMap[1][1], blockMap[2][0]}),
+                       wantStore: createStoreEntries([]int{0, 1, 3}, []*attachBlock{blockMap[0][0], blockMap[1][0], blockMap[1][1], blockMap[2][0]}),
                        wantBlockIndex: state.NewBlockIndexWithData(
                                map[bc.Hash]*state.BlockNode{
                                        blockMap[0][0].block.Hash(): mustCreateBlockNode(&blockMap[0][0].block.BlockHeader),
@@ -923,7 +923,7 @@ func TestProcessBlock(t *testing.T) {
                {
                        desc:     "rollback a block has spend btm tx, and from orphan manage",
                        newBlock: blockMap[2][0].block,
-                       initStore: createStoreItems([]int{0, 1, 2}, []*attachBlock{blockMap[0][0], blockMap[1][0], blockMap[2][1]}, &storeItem{
+                       initStore: createStoreEntries([]int{0, 1, 2}, []*attachBlock{blockMap[0][0], blockMap[1][0], blockMap[2][1]}, &storeItem{
                                key: database.CalcUtxoKey(hashPtr(testutil.MustDecodeHash("c93b687f98d039046cd2afd514c62f5d1c2c3b0804e4845b00a33e736ef48a33"))),
                                val: &storage.UtxoEntry{IsCoinBase: false, BlockHeight: 1, Spent: false},
                        }),
@@ -933,7 +933,7 @@ func TestProcessBlock(t *testing.T) {
                                },
                                map[bc.Hash][]*bc.Hash{blockMap[3][0].block.PreviousBlockHash: {hashPtr(blockMap[3][0].block.Hash())}},
                        ),
-                       wantStore: createStoreItems([]int{0, 1, 2, 4}, []*attachBlock{blockMap[0][0], blockMap[1][0], blockMap[2][0], blockMap[2][1], blockMap[3][0]}, &storeItem{
+                       wantStore: createStoreEntries([]int{0, 1, 2, 4}, []*attachBlock{blockMap[0][0], blockMap[1][0], blockMap[2][0], blockMap[2][1], blockMap[3][0]}, &storeItem{
                                key: database.CalcUtxoKey(hashPtr(testutil.MustDecodeHash("c93b687f98d039046cd2afd514c62f5d1c2c3b0804e4845b00a33e736ef48a33"))),
                                val: &storage.UtxoEntry{IsCoinBase: false, BlockHeight: 0, Spent: false},
                        }),
@@ -959,7 +959,7 @@ func TestProcessBlock(t *testing.T) {
                {
                        desc:     "rollback a block has retire tx, and from orphan manage",
                        newBlock: blockMap[2][0].block,
-                       initStore: createStoreItems([]int{0, 1, 2}, []*attachBlock{blockMap[0][0], blockMap[1][0], blockMap[2][2]}, &storeItem{
+                       initStore: createStoreEntries([]int{0, 1, 2}, []*attachBlock{blockMap[0][0], blockMap[1][0], blockMap[2][2]}, &storeItem{
                                key: database.CalcUtxoKey(hashPtr(testutil.MustDecodeHash("c93b687f98d039046cd2afd514c62f5d1c2c3b0804e4845b00a33e736ef48a33"))),
                                val: &storage.UtxoEntry{IsCoinBase: false, BlockHeight: 1, Spent: false},
                        }),
@@ -969,7 +969,7 @@ func TestProcessBlock(t *testing.T) {
                                },
                                map[bc.Hash][]*bc.Hash{blockMap[3][0].block.PreviousBlockHash: {hashPtr(blockMap[3][0].block.Hash())}},
                        ),
-                       wantStore: createStoreItems([]int{0, 1, 2, 4}, []*attachBlock{blockMap[0][0], blockMap[1][0], blockMap[2][0], blockMap[2][2], blockMap[3][0]}, &storeItem{
+                       wantStore: createStoreEntries([]int{0, 1, 2, 4}, []*attachBlock{blockMap[0][0], blockMap[1][0], blockMap[2][0], blockMap[2][2], blockMap[3][0]}, &storeItem{
                                key: database.CalcUtxoKey(hashPtr(testutil.MustDecodeHash("c93b687f98d039046cd2afd514c62f5d1c2c3b0804e4845b00a33e736ef48a33"))),
                                val: &storage.UtxoEntry{IsCoinBase: false, BlockHeight: 0, Spent: false},
                        }),
@@ -995,7 +995,7 @@ func TestProcessBlock(t *testing.T) {
                {
                        desc:     "rollback a block has issuance tx, and from orphan manage",
                        newBlock: blockMap[2][0].block,
-                       initStore: createStoreItems([]int{0, 1, 2}, []*attachBlock{blockMap[0][0], blockMap[1][0], blockMap[2][3]}, &storeItem{
+                       initStore: createStoreEntries([]int{0, 1, 2}, []*attachBlock{blockMap[0][0], blockMap[1][0], blockMap[2][3]}, &storeItem{
                                key: database.CalcUtxoKey(hashPtr(testutil.MustDecodeHash("c93b687f98d039046cd2afd514c62f5d1c2c3b0804e4845b00a33e736ef48a33"))),
                                val: &storage.UtxoEntry{IsCoinBase: false, BlockHeight: 1, Spent: false},
                        }),
@@ -1005,7 +1005,7 @@ func TestProcessBlock(t *testing.T) {
                                },
                                map[bc.Hash][]*bc.Hash{blockMap[3][0].block.PreviousBlockHash: {hashPtr(blockMap[3][0].block.Hash())}},
                        ),
-                       wantStore: createStoreItems([]int{0, 1, 2, 4}, []*attachBlock{blockMap[0][0], blockMap[1][0], blockMap[2][0], blockMap[2][3], blockMap[3][0]}, &storeItem{
+                       wantStore: createStoreEntries([]int{0, 1, 2, 4}, []*attachBlock{blockMap[0][0], blockMap[1][0], blockMap[2][0], blockMap[2][3], blockMap[3][0]}, &storeItem{
                                key: database.CalcUtxoKey(hashPtr(testutil.MustDecodeHash("c93b687f98d039046cd2afd514c62f5d1c2c3b0804e4845b00a33e736ef48a33"))),
                                val: &storage.UtxoEntry{IsCoinBase: false, BlockHeight: 0, Spent: false},
                        }),
@@ -1031,7 +1031,7 @@ func TestProcessBlock(t *testing.T) {
                {
                        desc:     "rollback a block has non btm tx, and from orphan manage",
                        newBlock: blockMap[2][0].block,
-                       initStore: createStoreItems([]int{0, 1, 2}, []*attachBlock{blockMap[0][0], blockMap[1][0], blockMap[2][5]}, &storeItem{
+                       initStore: createStoreEntries([]int{0, 1, 2}, []*attachBlock{blockMap[0][0], blockMap[1][0], blockMap[2][5]}, &storeItem{
                                key: database.CalcUtxoKey(hashPtr(testutil.MustDecodeHash("c93b687f98d039046cd2afd514c62f5d1c2c3b0804e4845b00a33e736ef48a33"))),
                                val: &storage.UtxoEntry{IsCoinBase: false, BlockHeight: 1, Spent: false},
                        }, &storeItem{
@@ -1044,7 +1044,7 @@ func TestProcessBlock(t *testing.T) {
                                },
                                map[bc.Hash][]*bc.Hash{blockMap[3][0].block.PreviousBlockHash: {hashPtr(blockMap[3][0].block.Hash())}},
                        ),
-                       wantStore: createStoreItems([]int{0, 1, 2, 4}, []*attachBlock{blockMap[0][0], blockMap[1][0], blockMap[2][0], blockMap[2][5], blockMap[3][0]}, &storeItem{
+                       wantStore: createStoreEntries([]int{0, 1, 2, 4}, []*attachBlock{blockMap[0][0], blockMap[1][0], blockMap[2][0], blockMap[2][5], blockMap[3][0]}, &storeItem{
                                key: database.CalcUtxoKey(hashPtr(testutil.MustDecodeHash("c93b687f98d039046cd2afd514c62f5d1c2c3b0804e4845b00a33e736ef48a33"))),
                                val: &storage.UtxoEntry{IsCoinBase: false, BlockHeight: 0, Spent: false},
                        }, &storeItem{
@@ -1094,11 +1094,10 @@ func createStoreItems(mainChainIndexes []int, attachBlocks []*attachBlock, extra
                block := attachBlock.block
                blockHash := block.Hash()
                items = append(items, &storeItem{
-                       key: database.CalcBlockKey(&blockHash),
                        val: block,
                })
                items = append(items, &storeItem{
-                       key: database.CalcBlockHeaderKey(block.Height, &blockHash),
+                       key: database.CalcBlockHeaderIndexKey(block.Height, &blockHash),
                        val: block.BlockHeader,
                })
                if _, ok := mainChainIndexMap[i]; !ok {
@@ -1211,3 +1210,18 @@ func spendUTXO(spendOutputID bc.Hash, items storeItems, blockHeight uint64) int
        }
        panic("can not find available utxo")
 }
+
+func createStoreEntries(mainChainIndexes []int, attachBlocks []*attachBlock, extralItem ...*storeItem) []storeEntry {
+       storeItems := createStoreItems(mainChainIndexes, attachBlocks, extralItem...)
+       var storeEntries []storeEntry
+       for _, item := range storeItems {
+               entrys, err := serialItem(item)
+               if err != nil {
+                       panic(err)
+               }
+
+               storeEntries = append(storeEntries, entrys...)
+       }
+
+       return storeEntries
+}
index 49b3bd3..92cdf5d 100644 (file)
@@ -31,100 +31,12 @@ type storeItem struct {
        val interface{}
 }
 
-type serialFun func(obj interface{}) ([]byte, error)
-type deserialFun func(data []byte) (interface{}, error)
-
-func getSerialFun(item interface{}) (serialFun, error) {
-       switch item.(type) {
-       case *protocol.BlockStoreState:
-               return json.Marshal, nil
-       case *types.Block:
-               return func(obj interface{}) ([]byte, error) {
-                       block := obj.(*types.Block)
-                       return block.MarshalText()
-               }, nil
-       case types.BlockHeader:
-               return func(obj interface{}) ([]byte, error) {
-                       bh := obj.(types.BlockHeader)
-                       return bh.MarshalText()
-               }, nil
-       case *bc.TransactionStatus:
-               return func(obj interface{}) ([]byte, error) {
-                       status := obj.(*bc.TransactionStatus)
-                       return proto.Marshal(status)
-               }, nil
-       case *storage.UtxoEntry:
-               return func(obj interface{}) ([]byte, error) {
-                       utxo := obj.(*storage.UtxoEntry)
-                       return proto.Marshal(utxo)
-               }, nil
-       }
-       typ := reflect.TypeOf(item)
-       return nil, fmt.Errorf("can not found any serialization function for type:%s", typ.Name())
-}
-
-func getDeserialFun(key []byte) (deserialFun, error) {
-       funMap := map[string]deserialFun{
-               string(database.BlockStoreKey): func(data []byte) (interface{}, error) {
-                       storeState := &protocol.BlockStoreState{}
-                       err := json.Unmarshal(data, storeState)
-                       return storeState, err
-               },
-               string(database.TxStatusPrefix): func(data []byte) (interface{}, error) {
-                       status := &bc.TransactionStatus{}
-                       err := proto.Unmarshal(data, status)
-                       return status, err
-               },
-               string(database.BlockPrefix): func(data []byte) (interface{}, error) {
-                       block := &types.Block{}
-                       err := block.UnmarshalText(data)
-                       sortSpendOutputID(block)
-                       return block, err
-               },
-               string(database.BlockHeaderPrefix): func(data []byte) (interface{}, error) {
-                       bh := types.BlockHeader{}
-                       err := bh.UnmarshalText(data)
-                       return bh, err
-               },
-               database.UtxoPreFix: func(data []byte) (interface{}, error) {
-                       utxo := &storage.UtxoEntry{}
-                       err := proto.Unmarshal(data, utxo)
-                       return utxo, err
-               },
-       }
-
-       for prefix, converter := range funMap {
-               if strings.HasPrefix(string(key), prefix) {
-                       return converter, nil
-               }
-       }
-       return nil, fmt.Errorf("can not found any deserialization function for key:%s", string(key))
-}
-
 type storeItems []*storeItem
 
-func (s1 storeItems) equals(s2 storeItems) bool {
-       if s2 == nil {
-               return false
-       }
-
-       itemMap1 := make(map[string]interface{}, len(s1))
-       for _, item := range s1 {
-               itemMap1[string(item.key)] = item.val
-       }
-
-       itemMap2 := make(map[string]interface{}, len(s2))
-       for _, item := range s2 {
-               itemMap2[string(item.key)] = item.val
-       }
-
-       return testutil.DeepEqual(itemMap1, itemMap2)
-}
-
 type processBlockTestCase struct {
        desc             string
-       initStore        []*storeItem
-       wantStore        []*storeItem
+       initStore        []storeEntry
+       wantStore        []storeEntry
        wantBlockIndex   *state.BlockIndex
        initOrphanManage *protocol.OrphanManage
        wantOrphanManage *protocol.OrphanManage
@@ -136,7 +48,7 @@ type processBlockTestCase struct {
 func (p *processBlockTestCase) Run() error {
        defer os.RemoveAll(dbDir)
        if p.initStore == nil {
-               p.initStore = make([]*storeItem, 0)
+               p.initStore = make([]storeEntry, 0)
        }
        store, db, err := initStore(p)
        if err != nil {
@@ -164,39 +76,18 @@ func (p *processBlockTestCase) Run() error {
        }
 
        if p.wantStore != nil {
-               gotStoreItems, err := loadStoreItems(db)
-               if err != nil {
-                       return err
-               }
-
-               if !storeItems(gotStoreItems).equals(p.wantStore) {
-                       gots := make(map[string]bool)
-                       for _, gotItem := range gotStoreItems {
-                               gotItemKey := hex.EncodeToString(gotItem.key)
-                               gots[gotItemKey] = true
+               gotStoreEntries := loadStoreEntries(db)
+               if !equalsStoreEntries(p.wantStore, gotStoreEntries) {
+                       gotMap := make(map[string]string)
+                       for _, entry := range gotStoreEntries {
+                               gotMap[hex.EncodeToString(entry.key)] = hex.EncodeToString(entry.val)
                        }
 
-                       wants := make(map[string]bool)
-                       for _, wantItem := range p.wantStore {
-                               wantItemKey := hex.EncodeToString(wantItem.key)
-                               wants[wantItemKey] = true
+                       wantMap := make(map[string]string)
+                       for _, entry := range p.wantStore {
+                               wantMap[hex.EncodeToString(entry.key)] = hex.EncodeToString(entry.val)
                        }
-
-                       fmt.Println("only in want:")
-                       for wantItemKey, _ := range wants {
-                               if !gots[wantItemKey] {
-                                       fmt.Println(wantItemKey)
-                               }
-                       }
-
-                       fmt.Println("only in got:")
-                       for wantItemKey, _ := range gots {
-                               if !wants[wantItemKey] {
-                                       fmt.Println(wantItemKey)
-                               }
-                       }
-
-                       return fmt.Errorf("#case(%s) want store:%v, got store:%v", p.desc, p.wantStore, gotStoreItems)
+                       return fmt.Errorf("#case(%s) want store:%v, got store:%v", p.desc, p.wantStore, gotStoreEntries)
                }
        }
 
@@ -215,57 +106,107 @@ func (p *processBlockTestCase) Run() error {
        return nil
 }
 
-func loadStoreItems(db dbm.DB) ([]*storeItem, error) {
-       iter := db.Iterator()
-       defer iter.Release()
+func initStore(c *processBlockTestCase) (protocol.Store, dbm.DB, error) {
+       testDB := dbm.NewDB("testdb", "leveldb", dbDir)
+       batch := testDB.NewBatch()
+       for _, entry := range c.initStore {
+               batch.Set(entry.key, entry.val)
+       }
+       batch.Write()
+       return database.NewStore(testDB), testDB, nil
+}
 
-       var items []*storeItem
-       for iter.Next() {
-               item := &storeItem{key: iter.Key()}
-               fun, err := getDeserialFun(iter.Key())
+func sortSpendOutputID(block *types.Block) {
+       for _, tx := range block.Transactions {
+               sort.Sort(HashSlice(tx.SpentOutputIDs))
+       }
+}
+
+type HashSlice []bc.Hash
+
+func (p HashSlice) Len() int           { return len(p) }
+func (p HashSlice) Less(i, j int) bool { return p[i].String() < p[j].String() }
+func (p HashSlice) Swap(i, j int)      { p[i], p[j] = p[j], p[i] }
+
+type storeEntry struct {
+       key []byte
+       val []byte
+}
+
+func serialItem(item *storeItem) ([]storeEntry, error) {
+       var storeEntrys []storeEntry
+       switch item.val.(type) {
+       case *protocol.BlockStoreState:
+               bytes, err := json.Marshal(item.val)
                if err != nil {
                        return nil, err
                }
 
-               val, err := fun(iter.Value())
+               storeEntrys = append(storeEntrys, storeEntry{key: item.key, val: bytes})
+       case *types.Block:
+               block := item.val.(*types.Block)
+               hash := block.Hash()
+               binaryBlockHeader, err := block.MarshalTextForBlockHeader()
                if err != nil {
                        return nil, err
                }
 
-               item.val = val
-               items = append(items, item)
-       }
-       return items, nil
-}
+               storeEntrys = append(storeEntrys, storeEntry{key: database.CalcBlockHeaderKey(&hash), val: binaryBlockHeader})
+               binaryBlockTxs, err := block.MarshalTextForTransactions()
+               if err != nil {
+                       return nil, err
+               }
 
-func initStore(c *processBlockTestCase) (protocol.Store, dbm.DB, error) {
-       testDB := dbm.NewDB("testdb", "leveldb", dbDir)
-       batch := testDB.NewBatch()
-       for _, item := range c.initStore {
-               fun, err := getSerialFun(item.val)
+               storeEntrys = append(storeEntrys, storeEntry{key: database.CalcBlockTransactionsKey(&hash), val: binaryBlockTxs})
+       case types.BlockHeader:
+               bh := item.val.(types.BlockHeader)
+               bytes, err := bh.MarshalText()
                if err != nil {
-                       return nil, nil, err
+                       return nil, err
                }
 
-               bytes, err := fun(item.val)
+               storeEntrys = append(storeEntrys, storeEntry{key: item.key, val: bytes})
+       case *storage.UtxoEntry:
+               utxo := item.val.(*storage.UtxoEntry)
+               bytes, err := proto.Marshal(utxo)
                if err != nil {
-                       return nil, nil, err
+                       return nil, err
                }
 
-               batch.Set(item.key, bytes)
+               storeEntrys = append(storeEntrys, storeEntry{key: item.key, val: bytes})
+       default:
+               typ := reflect.TypeOf(item.val)
+               return nil, fmt.Errorf("can not found any serialization function for type:%s", typ.Name())
        }
-       batch.Write()
-       return database.NewStore(testDB), testDB, nil
+
+       return storeEntrys, nil
 }
 
-func sortSpendOutputID(block *types.Block) {
-       for _, tx := range block.Transactions {
-               sort.Sort(HashSlice(tx.SpentOutputIDs))
+func equalsStoreEntries(s1, s2 []storeEntry) bool {
+       itemMap1 := make(map[string]interface{}, len(s1))
+       for _, item := range s1 {
+               itemMap1[string(item.key)] = item.val
+       }
+
+       itemMap2 := make(map[string]interface{}, len(s2))
+       for _, item := range s2 {
+               itemMap2[string(item.key)] = item.val
        }
+
+       return testutil.DeepEqual(itemMap1, itemMap2)
 }
 
-type HashSlice []bc.Hash
+func loadStoreEntries(db dbm.DB) []storeEntry {
+       var entries []storeEntry
+       iter := db.Iterator()
+       defer iter.Release()
+       for iter.Next() {
+               if strings.HasPrefix(string(iter.Key()), string(database.BlockHashesKeyPrefix)) {
+                       continue
+               }
 
-func (p HashSlice) Len() int           { return len(p) }
-func (p HashSlice) Less(i, j int) bool { return p[i].String() < p[j].String() }
-func (p HashSlice) Swap(i, j int)      { p[i], p[j] = p[j], p[i] }
+               item := storeEntry{key: iter.Key(), val: iter.Value()}
+               entries = append(entries, item)
+       }
+       return entries
+}