OSDN Git Service

Merge branch 'master' into key_alias key_alias
authorPaladz <yzhu101@uottawa.ca>
Thu, 2 Apr 2020 11:59:30 +0000 (19:59 +0800)
committerGitHub <noreply@github.com>
Thu, 2 Apr 2020 11:59:30 +0000 (19:59 +0800)
application/mov/database/mov_store.go
application/mov/mov_core.go
node/node.go
proposal/proposal.go
protocol/protocol.go
test/rollback_test.go
test/subprotocol_test.go [new file with mode: 0644]

index 20d3101..c45b46f 100644 (file)
@@ -12,8 +12,12 @@ import (
        "github.com/bytom/vapor/protocol/bc/types"
 )
 
+// ErrNotInitDBState represent the database state of mov store is not initialized
+var ErrNotInitDBState = errors.New("database state of mov store is not initialized")
+
 // MovStore is the interface for mov's persistent storage
 type MovStore interface {
+       Clear()
        GetMovDatabaseState() (*common.MovDatabaseState, error)
        InitDBState(height uint64, hash *bc.Hash) error
        ListOrders(orderAfter *common.Order) ([]*common.Order, error)
@@ -89,6 +93,19 @@ func NewLevelDBMovStore(db dbm.DB) *LevelDBMovStore {
        return &LevelDBMovStore{db: db}
 }
 
+// Clear will clear all the data of store
+func (m *LevelDBMovStore) Clear() {
+       batch := m.db.NewBatch()
+
+       iter := m.db.Iterator()
+       defer iter.Release()
+
+       for iter.Next() {
+               batch.Delete(iter.Key())
+       }
+       batch.Write()
+}
+
 // GetMovDatabaseState return the current DB's image status
 func (m *LevelDBMovStore) GetMovDatabaseState() (*common.MovDatabaseState, error) {
        if value := m.db.Get(bestMatchStore); value != nil {
@@ -96,7 +113,7 @@ func (m *LevelDBMovStore) GetMovDatabaseState() (*common.MovDatabaseState, error
                return state, json.Unmarshal(value, state)
        }
 
-       return nil, errors.New("don't find state of mov-database")
+       return nil, ErrNotInitDBState
 }
 
 // InitDBState set the DB's image status
index c4f7760..4a08cf5 100644 (file)
@@ -11,11 +11,13 @@ import (
        "github.com/bytom/vapor/consensus/segwit"
        dbm "github.com/bytom/vapor/database/leveldb"
        "github.com/bytom/vapor/errors"
+       "github.com/bytom/vapor/protocol"
        "github.com/bytom/vapor/protocol/bc"
        "github.com/bytom/vapor/protocol/bc/types"
 )
 
 var (
+       errChainStatusHasAlreadyInit    = errors.New("mov chain status has already initialized")
        errInvalidTradePairs            = errors.New("The trade pairs in the tx input is invalid")
        errStatusFailMustFalse          = errors.New("status fail of transaction does not allow to be true")
        errInputProgramMustP2WMCScript  = errors.New("input program of trade tx must p2wmc script")
@@ -57,7 +59,7 @@ func (m *Core) ApplyBlock(block *types.Block) error {
 
        if block.Height == m.startBlockHeight {
                blockHash := block.Hash()
-               return m.movStore.InitDBState(block.Height, &blockHash)
+               return m.InitChainStatus(&blockHash)
        }
 
        if err := m.validateMatchedTxSequence(block.Transactions); err != nil {
@@ -98,6 +100,10 @@ func (m *Core) BeforeProposalBlock(txs []*types.Tx, blockHeight uint64, gasLeft
 // ChainStatus return the current block height and block hash in dex core
 func (m *Core) ChainStatus() (uint64, *bc.Hash, error) {
        state, err := m.movStore.GetMovDatabaseState()
+       if err == database.ErrNotInitDBState {
+               return 0, nil, protocol.ErrNotInitSubProtocolChainStatus
+       }
+
        if err != nil {
                return 0, nil, err
        }
@@ -112,6 +118,11 @@ func (m *Core) DetachBlock(block *types.Block) error {
                return nil
        }
 
+       if block.Height == m.startBlockHeight {
+               m.movStore.Clear()
+               return nil
+       }
+
        deleteOrders, addOrders, err := decodeTxsOrders(block.Transactions)
        if err != nil {
                return err
@@ -120,6 +131,15 @@ func (m *Core) DetachBlock(block *types.Block) error {
        return m.movStore.ProcessOrders(addOrders, deleteOrders, &block.BlockHeader)
 }
 
+// InitChainStatus used to init the start block height and start block hash to store
+func (m *Core) InitChainStatus(startHash *bc.Hash) error {
+       if _, err := m.movStore.GetMovDatabaseState(); err == nil {
+               return errChainStatusHasAlreadyInit
+       }
+
+       return m.movStore.InitDBState(m.startBlockHeight, startHash)
+}
+
 // IsDust block the transaction that are not generated by the match engine
 func (m *Core) IsDust(tx *types.Tx) bool {
        for _, input := range tx.Inputs {
index 313f55b..61e2b8c 100644 (file)
@@ -88,7 +88,7 @@ func NewNode(config *cfg.Config) *Node {
        movCore := mov.NewCore(config.DBBackend, config.DBDir(), consensus.ActiveNetParams.MovStartHeight)
        assetFilter := protocol.NewAssetFilter(config.CrossChain.AssetWhitelist)
        txPool := protocol.NewTxPool(store, []protocol.DustFilterer{movCore, assetFilter}, dispatcher)
-       chain, err := protocol.NewChain(store, txPool, []protocol.Protocoler{movCore}, dispatcher)
+       chain, err := protocol.NewChain(store, txPool, []protocol.SubProtocol{movCore}, dispatcher)
        if err != nil {
                cmn.Exit(cmn.Fmt("Failed to create chain structure: %v", err))
        }
@@ -180,7 +180,7 @@ func Rollback(config *cfg.Config, targetHeight uint64) error {
        dispatcher := event.NewDispatcher()
        movCore := mov.NewCore(config.DBBackend, config.DBDir(), consensus.ActiveNetParams.MovStartHeight)
        txPool := protocol.NewTxPool(store, []protocol.DustFilterer{movCore}, dispatcher)
-       chain, err := protocol.NewChain(store, txPool, []protocol.Protocoler{movCore}, dispatcher)
+       chain, err := protocol.NewChain(store, txPool, []protocol.SubProtocol{movCore}, dispatcher)
        if err != nil {
                return err
        }
index 1a4d1c8..2186249 100644 (file)
@@ -334,7 +334,7 @@ func (b *blockBuilder) preValidateTxs(txs []*types.Tx, chain *protocol.Chain, vi
        return results, gasLeft
 }
 
-func (b *blockBuilder) validateBySubProtocols(tx *types.Tx, statusFail bool, subProtocols []protocol.Protocoler) error {
+func (b *blockBuilder) validateBySubProtocols(tx *types.Tx, statusFail bool, subProtocols []protocol.SubProtocol) error {
        for _, subProtocol := range subProtocols {
                verifyResult := &bc.TxVerifyResult{StatusFail: statusFail}
                if err := subProtocol.ValidateTx(tx, verifyResult, b.block.Height); err != nil {
index a1cbb70..ea1b2b0 100644 (file)
@@ -19,12 +19,19 @@ const (
        maxKnownTxs           = 32768 // Maximum transactions hashes to keep in the known list (prevent DOS)
 )
 
-// Protocoler is interface for layer 2 consensus protocol
-type Protocoler interface {
+// ErrNotInitSubProtocolChainStatus represent the node state of sub protocol has not been initialized
+var ErrNotInitSubProtocolChainStatus = errors.New("node state of sub protocol has not been initialized")
+
+// SubProtocol is interface for layer 2 consensus protocol
+type SubProtocol interface {
        Name() string
        StartHeight() uint64
        BeforeProposalBlock(txs []*types.Tx, blockHeight uint64, gasLeft int64, isTimeout func() bool) ([]*types.Tx, error)
+
+       // ChainStatus return the the current block height and block hash of sub protocol.
+       // it will return ErrNotInitSubProtocolChainStatus if not initialized.
        ChainStatus() (uint64, *bc.Hash, error)
+       InitChainStatus(*bc.Hash) error
        ValidateBlock(block *types.Block, verifyResults []*bc.TxVerifyResult) error
        ValidateTx(tx *types.Tx, verifyResult *bc.TxVerifyResult, blockHeight uint64) error
        ApplyBlock(block *types.Block) error
@@ -37,7 +44,7 @@ type Chain struct {
        txPool         *TxPool
        store          Store
        processBlockCh chan *processBlockMsg
-       subProtocols   []Protocoler
+       subProtocols   []SubProtocol
 
        signatureCache  *common.Cache
        eventDispatcher *event.Dispatcher
@@ -50,7 +57,7 @@ type Chain struct {
 }
 
 // NewChain returns a new Chain using store as the underlying storage.
-func NewChain(store Store, txPool *TxPool, subProtocols []Protocoler, eventDispatcher *event.Dispatcher) (*Chain, error) {
+func NewChain(store Store, txPool *TxPool, subProtocols []SubProtocol, eventDispatcher *event.Dispatcher) (*Chain, error) {
        knownTxs, _ := common.NewOrderedSet(maxKnownTxs)
        c := &Chain{
                orphanManage:    NewOrphanManage(),
@@ -175,7 +182,7 @@ func (c *Chain) InMainChain(hash bc.Hash) bool {
 }
 
 // SubProtocols return list of layer 2 consensus protocol
-func (c *Chain) SubProtocols() []Protocoler {
+func (c *Chain) SubProtocols() []SubProtocol {
        return c.subProtocols
 }
 
@@ -215,14 +222,25 @@ func (c *Chain) markTransactions(txs ...*types.Tx) {
        }
 }
 
-func (c *Chain) syncProtocolStatus(subProtocol Protocoler) error {
+func (c *Chain) syncProtocolStatus(subProtocol SubProtocol) error {
        if c.bestBlockHeader.Height < subProtocol.StartHeight() {
                return nil
        }
 
        protocolHeight, protocolHash, err := subProtocol.ChainStatus()
-       if err != nil {
-               return errors.Wrap(err, "failed on get sub protocol status")
+       if err == ErrNotInitSubProtocolChainStatus {
+               startHash, err := c.store.GetMainChainHash(subProtocol.StartHeight())
+               if err != nil {
+                       return errors.Wrap(err, subProtocol.Name(), "can't get block hash by height")
+               }
+
+               if err := subProtocol.InitChainStatus(startHash); err != nil {
+                       return errors.Wrap(err, subProtocol.Name(), "fail init chain status")
+               }
+
+               protocolHeight, protocolHash = subProtocol.StartHeight(), startHash
+       } else if err != nil {
+               return errors.Wrap(err, subProtocol.Name(), "can't get chain status")
        }
 
        if *protocolHash == c.bestBlockHeader.Hash() {
index d40f9ba..00bc599 100644 (file)
@@ -1460,7 +1460,7 @@ func TestRollback(t *testing.T) {
                        t.Fatal(err)
                }
 
-               chain, err := protocol.NewChain(store, nil, []protocol.Protocoler{movCore}, nil)
+               chain, err := protocol.NewChain(store, nil, []protocol.SubProtocol{movCore}, nil)
                if err != nil {
                        t.Fatal(err)
                }
diff --git a/test/subprotocol_test.go b/test/subprotocol_test.go
new file mode 100644 (file)
index 0000000..36ca3a5
--- /dev/null
@@ -0,0 +1,235 @@
+package test
+
+import (
+       "os"
+       "testing"
+
+       "github.com/bytom/vapor/application/mov"
+       movDatabase "github.com/bytom/vapor/application/mov/database"
+       "github.com/bytom/vapor/database"
+       dbm "github.com/bytom/vapor/database/leveldb"
+       "github.com/bytom/vapor/protocol"
+       "github.com/bytom/vapor/protocol/bc"
+       "github.com/bytom/vapor/protocol/bc/types"
+       "github.com/bytom/vapor/protocol/state"
+       "github.com/bytom/vapor/testutil"
+)
+
+type chainStatus struct {
+       blockHeight uint64
+       blockHash   bc.Hash
+}
+
+type chainBlock struct {
+       block       *types.Block
+       inMainChain bool
+}
+
+var blocks = map[uint64][]*types.Block{
+       0: {
+               {
+                       BlockHeader: types.BlockHeader{
+                               Height:            0,
+                               Timestamp:         1585814309,
+                               PreviousBlockHash: bc.Hash{},
+                       },
+               },
+       },
+       1: {
+               // prev block is [0][0]
+               {
+                       BlockHeader: types.BlockHeader{
+                               Height:            1,
+                               Timestamp:         1585814310,
+                               PreviousBlockHash: testutil.MustDecodeHash("2e5406c82fe34f6ee44fe694b05ffc8fb5918a026415b086df03fb03760b42a9"),
+                       },
+               },
+               // prev block is [0][0]
+               {
+                       BlockHeader: types.BlockHeader{
+                               Height:            1,
+                               Timestamp:         1585814311,
+                               PreviousBlockHash: testutil.MustDecodeHash("2e5406c82fe34f6ee44fe694b05ffc8fb5918a026415b086df03fb03760b42a9"),
+                       },
+               },
+       },
+       2: {
+               // prev block is [1][0]
+               {
+                       BlockHeader: types.BlockHeader{
+                               Height:            2,
+                               Timestamp:         1585814320,
+                               PreviousBlockHash: testutil.MustDecodeHash("5bc198f4c0198e7e8b52173a82836cfd3f124d88bf052f53390948d845bf6fe0"),
+                       },
+               },
+       },
+}
+
+func TestSyncProtocolStatus(t *testing.T) {
+       cases := []struct {
+               desc            string
+               savedBlocks     []*chainBlock
+               startHeight     uint64
+               startHash       *bc.Hash
+               wantChainStatus *chainStatus
+       }{
+               {
+                       desc: "start height from 0, mov is not init",
+                       savedBlocks: []*chainBlock{
+                               {
+                                       block:       blocks[0][0],
+                                       inMainChain: true,
+                               },
+                               {
+                                       block:       blocks[1][0],
+                                       inMainChain: true,
+                               },
+                               {
+                                       block:       blocks[2][0],
+                                       inMainChain: true,
+                               },
+                       },
+                       startHeight: 0,
+                       wantChainStatus: &chainStatus{
+                               blockHeight: 2,
+                               blockHash:   blocks[2][0].Hash(),
+                       },
+               },
+               {
+                       desc: "start height from 1, mov is not init",
+                       savedBlocks: []*chainBlock{
+                               {
+                                       block:       blocks[0][0],
+                                       inMainChain: true,
+                               },
+                               {
+                                       block:       blocks[1][0],
+                                       inMainChain: true,
+                               },
+                               {
+                                       block:       blocks[2][0],
+                                       inMainChain: true,
+                               },
+                       },
+                       startHeight: 1,
+                       wantChainStatus: &chainStatus{
+                               blockHeight: 2,
+                               blockHash:   blocks[2][0].Hash(),
+                       },
+               },
+               {
+                       desc: "start height from 1, state of mov is not sync completed",
+                       savedBlocks: []*chainBlock{
+                               {
+                                       block:       blocks[0][0],
+                                       inMainChain: true,
+                               },
+                               {
+                                       block:       blocks[1][0],
+                                       inMainChain: true,
+                               },
+                               {
+                                       block:       blocks[2][0],
+                                       inMainChain: true,
+                               },
+                       },
+                       startHeight: 1,
+                       startHash:   hashPtr(blocks[1][0].Hash()),
+                       wantChainStatus: &chainStatus{
+                               blockHeight: 2,
+                               blockHash:   blocks[2][0].Hash(),
+                       },
+               },
+               {
+                       desc: "chain status of mov is forked",
+                       savedBlocks: []*chainBlock{
+                               {
+                                       block:       blocks[0][0],
+                                       inMainChain: true,
+                               },
+                               {
+                                       block:       blocks[1][0],
+                                       inMainChain: true,
+                               },
+                               {
+                                       block:       blocks[1][1],
+                                       inMainChain: false,
+                               },
+                               {
+                                       block:       blocks[2][0],
+                                       inMainChain: true,
+                               },
+                       },
+                       startHeight: 1,
+                       startHash:   hashPtr(blocks[1][1].Hash()),
+                       wantChainStatus: &chainStatus{
+                               blockHeight: 2,
+                               blockHash:   blocks[2][0].Hash(),
+                       },
+               },
+       }
+
+       defer os.RemoveAll("temp")
+
+       for i, c := range cases {
+               chainDB := dbm.NewDB("core", "leveldb", "temp")
+               store := database.NewStore(chainDB)
+               if err := initStore(store, c.savedBlocks); err != nil {
+                       t.Fatal(err)
+               }
+
+               movDB := dbm.NewDB("mov", "leveldb", "temp")
+               movCore := mov.NewCoreWithDB(movDatabase.NewLevelDBMovStore(movDB), c.startHeight)
+               if c.startHash != nil {
+                       if err := movCore.InitChainStatus(c.startHash); err != nil {
+                               t.Fatal(err)
+                       }
+               }
+
+               _, err := protocol.NewChain(store, nil, []protocol.SubProtocol{movCore}, nil)
+               if err != nil {
+                       t.Fatal(err)
+               }
+
+               gotHeight, gotHash, err := movCore.ChainStatus()
+               if err != nil {
+                       t.Fatal(err)
+               }
+
+               if gotHeight != c.wantChainStatus.blockHeight || *gotHash != c.wantChainStatus.blockHash {
+                       t.Logf("#%d(%s): got chain status of sub protocol is not equals want chain status", i, c.desc)
+               }
+
+               movDB.Close()
+               chainDB.Close()
+               os.RemoveAll("temp")
+       }
+}
+
+func initStore(store *database.Store, savedBlocks []*chainBlock) error {
+       var mainBlockHeaders []*types.BlockHeader
+       for _, block := range savedBlocks {
+               if err := store.SaveBlock(block.block, bc.NewTransactionStatus()); err != nil {
+                       return err
+               }
+
+               if block.inMainChain {
+                       mainBlockHeaders = append(mainBlockHeaders, &block.block.BlockHeader)
+               }
+
+               last := len(mainBlockHeaders) - 1
+               if err := store.SaveChainStatus(mainBlockHeaders[last], mainBlockHeaders[last], mainBlockHeaders, state.NewUtxoViewpoint(), nil); err != nil {
+                       return err
+               }
+       }
+       return nil
+}
+
+func hashPtr(hash bc.Hash) *bc.Hash {
+       return &hash
+}
+
+func TestBlockHash(t *testing.T) {
+       blockHash := blocks[1][0].Hash()
+       t.Log(blockHash.String())
+}