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
}
}
}
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)
}
}
package database
import (
- "encoding/binary"
"encoding/json"
"time"
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 {
// 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)
// 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
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
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()
--- /dev/null
+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
+}
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")
}
}
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")
}
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)
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)
t.Errorf("got block header:%v, expect block header:%v", gotBlockHeader, block.BlockHeader)
}
}
+
}
block := &types.Block{}
- return block, block.UnmarshalText(binaryBlock)
+ if err := block.UnmarshalText(binaryBlock); err != nil {
+ return nil, err
+ }
+
+ return block, nil
}
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
}
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)))
}
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
}
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 {
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)
}
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
}
}, ""),
},
}
-
for i, test := range cases {
got, err := test.blockHeader.MarshalText()
if err != nil && err != test.wantError {
"904e", // block height
"c34048bd60c4c13144fd34f408627d1be68f6cb4fdd34e879d6d791060ea73a0", // prev block hash
"e8b287d905", // timestamp
+ "0100", // BlockWitness
"20", // commitment extensible field length
"ad9ac003d08ff305181a345d64fe0b02311cc1a6ec04ab73f3318d90139bfe03", // transactions merkle root
}, ""),
"908", // block height (error with odd length)
"c34048bd60c4c13144fd34f408627d1be68f6cb4fdd34e879d6d791060ea73a0", // prev block hash
"e8b287d905", // timestamp
+ "0100", // BlockWitness
"20", // commitment extensible field length
"ad9ac003d08ff305181a345d64fe0b02311cc1a6ec04ab73f3318d90139bfe03", // transactions merkle root
}, ""),
"ffffffffffffffffff", // block height
"c34048bd60c4c13144fd34f408627d1be68f6cb4fdd34e879d6d791060ea73a0", // prev block hash
"e8b287d905", // timestamp
+ "0100", // BlockWitness
"20", // commitment extensible field length
"ad9ac003d08ff305181a345d64fe0b02311cc1a6ec04ab73f3318d90139bfe03", // transactions merkle root
"0100", // block witness
"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
}
for _, c := range cases {
-
blockBytes, err := hex.DecodeString(c.rawBlock)
if err != nil {
t.Fatal(err)
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),
{
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),
{
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),
{
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),
{
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),
{
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},
}),
{
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},
}),
{
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},
}),
{
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},
}),
{
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{
{
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{
{
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),
{
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},
}),
{
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},
}),
{
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},
}),
{
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{
{
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{
{
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),
{
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},
}),
},
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},
}),
{
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},
}),
},
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},
}),
{
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},
}),
},
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},
}),
{
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{
},
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{
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 {
}
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
+}
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
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 {
}
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)
}
}
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
+}